飞雪团队

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 16333|回复: 0

一文搞懂Zookeeper原理

[复制链接]

9160

主题

9248

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
29810
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
# [$ g. X' W/ _1 n& l
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>
2 h; M# f3 a7 i, i/ l7 _: P# V& j<p>&nbsp;ZooKeeper 是什么?</p>
" ?3 b2 w- i) g0 k3 _$ U; }<ul>, B& k2 L8 U9 K; F+ F5 `
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>, l! H* V5 ~9 y4 H# a& C
<li>从设计模式角度来理解:是一个基于<span style="color: rgba(51, 204, 204, 1)">观察者模式</span>设计的分布式服务管理框架,它负责<span style="color: rgba(51, 204, 204, 1)">存储</span>和<span style="color: rgba(51, 204, 204, 1)">管理</span>大家都关心的数据,一旦这些数据的状态发生变化,Zookeeper 就 将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。</li>
% X0 E) v) K: F8 F/ F5 R" d' ^<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>. P. x1 h5 \/ v2 R
</ul>3 F( h+ x4 d- @: J; _+ L
<p>Zookeeper的作用(应用场景)?</p>
- V/ b' W% f4 W. S% K<ul>+ ~0 l: e4 M3 P" F5 ~( O
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
- e' l$ W7 K4 h6 z7 g0 w9 c7 H; v<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>* g; e, u" Z: S* ~( P+ ^9 q
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>& C/ ^+ L  C$ T$ t! p4 G. g
<li><span style="color: rgba(51, 204, 204, 1)">分布式协调</span>:这个是我们最常用的,比如把多个<span style="color: rgba(51, 204, 204, 1)">服务提供者</span>的信息放在某个节点上,<span style="color: rgba(51, 204, 204, 1)">服务的消费者</span>就可以通过ZK调用。0 s0 b; G4 h+ q: P6 O) t0 w. r0 ]
<ul>
3 R5 j6 N0 K4 w! o& @, j& f! {# \<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>* G" R4 x6 ~5 b( c5 k, Q) ]
<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>2 K# P" ^8 M1 m" S" U
<li><span style="color: rgba(51, 204, 204, 1)">动态选举Maste</span>r:Zookeeper会每次选举最小编号的作为Master,如果Master挂了,自然对应的Znode节点就会删除。然后让<span style="color: rgba(51, 204, 204, 1)">新的最小编号作为Master</span>,这样就可以实现动态选举的功能了。</li>: U$ c$ T$ e9 X
</ul>
# O1 A4 N- M# ~: f</li>
- b' e" ^5 S! F: |" n<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>1 }( b0 U' `  q! e5 S. ?- v
</ul>
) t; ]! N/ Q- G* I. [, M<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>* k. }  t5 }3 r8 B" w2 x1 O
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>4 M/ t. Q4 O9 G0 y( r
<hr>
. d; q3 u5 K) k- s<p>&nbsp;文件系统:</p>4 g- S' S5 Y5 r5 Y6 G
<p>ZooKeeper的数据结构,跟Unix文件系统非常类似,可以看做是一颗<span style="color: rgba(51, 204, 204, 1)">树</span>,每个节点叫做<span style="color: rgba(51, 204, 204, 1)">Znode</span>。每一个Znode只能存1MB数据。数据只是<span style="color: rgba(51, 204, 204, 1)">配置信息</span>。每一个节点可以通过<span style="color: rgba(51, 204, 204, 1)">路径</span>来标识,结构图如下:</p>
  p; V  N, \8 Y6 k) J9 O<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>6 o1 i' U9 V0 O0 D; ?* y4 ?$ C
<p>&nbsp;Znode节点主要有4中类型:</p>
5 F. j8 D6 G8 K7 {, h' z6 ^3 C  c<ul>
3 y' K- ?% m4 g<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
* G' R  V0 S& W+ a<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>6 T) @* O) W2 v* L
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>" R: j, I* {7 _! T
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>2 A8 O! @$ L, p0 e& C7 N
</ul>
" u0 W  H( D$ V5 A* ^$ u7 h<hr>3 P/ e( w, F" U8 x" t7 \
<p>&nbsp;通知机制 (监听机制)</p>
3 ~/ i% S0 M) Y3 T! m, Z7 J5 @+ r<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
, g) O" ?2 [/ S# }" L5 t' M, Z0 N! Y<p>客户端可以向服务端<span style="color: rgba(51, 204, 204, 1)">注册</span>Wather监听,服务端的指定事件<span style="color: rgba(51, 204, 204, 1)">触发</span>之后,就会向客户端发送一个事件<span style="color: rgba(51, 204, 204, 1)">通知</span>。具体步如下:</p>
  V' S: e' B% [  N8 K( C; H9 n<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
9 x% ^7 x2 M4 P% {2 g; J  N<ol># {5 V$ E9 _6 V" [4 j& X4 P. }1 K
<li>客户端向服务端注册Wather监听</li>
: i' N" d6 k( I5 v<li>保存Wather对象到客户端本地的WatherManager中</li>
7 \1 l$ z( `9 c+ E9 c5 D2 z<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>9 S+ n6 D9 @# J( f( x. Y4 `
</ol>
& l8 m! f5 n; G- b" N<p>&nbsp;主要监听2方面内容:</p>/ O3 `* u9 f4 Y) E0 z; s5 N- f
<ul class="list-paddingleft-2">" y- F' |7 U+ O0 M6 I, h
<li># t" A- u5 o: ?$ |; {: t4 a8 D
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>. }) ?+ u" C6 H9 w: O& A
</li>
. Z" k/ `. T# ~. ?. y<li>7 h( s8 P2 E/ C! h1 P
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
: z3 p3 A+ k4 ]: H  A" H4 {, D6 K2 b</li>
; x2 h& G0 _1 B" a1 n' n</ul>
/ D+ i: @5 l$ Y& o; E( D! G6 j& D4 W<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>% O/ O& y% q  b8 W" Z% L6 Y# T
<ul>
" c1 U9 O: x% \& N- m2 A9 U, c$ |<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>  ]! R* r5 O( W2 S  o" Y! Z
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>( W( n) Y+ k5 w
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>- ?4 g. v' m  w* c: O' D7 T
</ul>
6 Z. r  \. L0 D9 f4 L$ u<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>% z( r; \! H5 [
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>  c, |  L; w. G" Q% C& X; d
<ul>
% ^3 V' [. B: S, n<li>Leader:负责写数据。(写数据都有事务)</li>) j* i1 A8 }+ F6 X
<li>Follower:负责读数据,节点的<span style="color: rgba(51, 204, 204, 1)">选举</span>和<span style="color: rgba(51, 204, 204, 1)">过半写成功<span style="color: rgba(0, 0, 0, 1)">。(读数据没有事务)</span><strong><br></strong></span></li>3 z  R5 R7 w0 t( [6 B8 u
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>& m1 g# I& k, v. E: Y: b

! C! U, S1 m7 N# s+ T& d* m9 }</ul>
1 J, V9 U+ O9 @<hr>
- f5 \) g' A% F- Z3 M<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>& c2 t+ i* |7 o2 M
<ul>/ K: |2 ^. L+ [( Y% @
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
# k9 N. L* p6 {9 W- J% \0 i<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li># L$ n; l8 v7 j4 W7 p9 Y8 t
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>. _6 N6 U& I# \; I- o% P0 |1 Z# A
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
& e+ u. p3 |; {. C2 i0 o  |! \% l9 R5 p6 c2 y" L
</ul>. X8 X) v/ f; R6 |1 R2 z
<hr>
5 T3 y3 Q6 v0 ?- _2 A/ U. K<p>其他概念:</p>
( c8 |2 F" z$ t# B) M6 T& @8 U<ul>
6 v/ a. q+ L9 D) g/ x$ l" Y* E<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:( L. W' Z8 N2 E; I3 A% O- g# B: O
<ul>
! X4 f. \, x1 N/ H. p/ b<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
5 z+ f8 ~* ]4 R<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
. k; i! C. Z" O$ k" T) w. d
/ S1 e( X8 {2 W
! y; b5 ]3 X( j+ |. F* [$ J</ul>% P/ x9 q4 ^: `, T  s
+ Z+ ~* `1 r$ t+ `( v
' ~9 Q; v0 c. ?- m
</li>; f1 j% S) `4 E

; V) O, m  D! }8 p/ k
, O* J3 ^! y, O4 _  r. f9 v</ul>
% I, F* }/ H) q* q<hr>
8 [) a2 n) T) [0 {( C- k<p>写数据原理:</p>9 X1 |' `1 W+ _
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>. s7 [1 [0 ^) }9 p  w2 x
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
  ^+ A' P- k6 ~  c0 `- a* g0 b<ul>
9 w) O2 E& y9 g- ]<li>写给leader,leader再通知其他节点 </li>
5 M. U2 Z# l* ~- j5 F<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>
4 [8 g% d+ q* F5 j6 }: k. {# N<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>
  t- O9 f  f9 U2 ^5 f
+ {3 {! Y) i; o1 Z% `7 {1 x2 r, s0 E4 {- Y# {
</ul>4 R7 W( G9 [: ~; \
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
  T8 W* S: ^3 J+ \2 p<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>" [, z1 ?1 E) W1 V5 O
<hr>
6 {5 Q7 ^0 o9 \' @0 x) F<p>&nbsp;ZK怎么保证数据一致性?</p>
+ C$ l0 S. a( h; L( m3 F<p>由于ZK只有Leader节点可以写入数据,如果是其他节点收到写入数据的请求,则会将之转发给Leader节点。ZK通过<span style="color: rgba(51, 204, 204, 1)">ZAB协议</span>来实现数据的最终顺序一致性,他是一个类似2PC两阶段提交的过程。ZAB有2种模式:<span style="color: rgba(51, 204, 204, 1)">消息广播</span>,<span style="color: rgba(51, 204, 204, 1)">崩溃恢复</span>(选举)。</p>
' _2 L% l& l2 I0 n<p>&nbsp;一般我们正常是消息广播:</p>( f6 ]  v) ?$ M& W
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
% g" p: |4 G- O" {<ul>
4 q2 ^0 x1 j7 H$ q<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,22 v- e( e! n5 v  F
<ul>0 O5 o! |: V' \4 z( J* w- H: H
<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
6 P' d8 J0 W% {% I, L& K8 }<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>8 Q* Z( E& m: k
" I+ l2 y5 {  w" V' m: |
0 T8 x# [( g" Y, ?0 X2 h8 _

1 x  D6 ?' A  U  V3 V/ |0 b1 h" M& d3 r# `* \3 P+ ~

) @; _! q% D5 @0 V" z& T( F/ j+ U; W3 t  z7 H
</ul>
; n+ N" l/ X! S4 }8 b6 H# f- v# q. J- @! }" X
  J: K; M6 `0 T" K3 P

- S+ X+ Q0 c- r/ n
  A1 S, ~  h1 D  K+ M
9 z1 f1 E( b" U0 \, {1 V$ F
: q9 I% F- p" G, U. V( V9 Y</li>+ p9 f" ^0 _3 o+ d
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
$ C, p8 r  ]5 O4 K<ul>' R0 h& @* e$ X+ I
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>2 }% {( u. m5 I4 E: s- d

- C+ W7 z; ^! N+ v" g
5 M1 f+ V% C6 b/ \9 A; s# h; o

" K  y/ W  w) N9 @3 @8 a1 o( s  F9 d7 o2 i0 R: i; ?% a5 o

! p- f8 L) J, D& M# B</ul>
# {, Y$ ^! U! j: e# e0 c. }* W* y8 }  M0 r9 `: g. G9 i/ l

5 U+ }# O! T' P9 }! h9 {7 f- Q' L; j. N# A, [$ x

9 O/ G9 x) R6 _) A, m1 v, j; y0 G) R  P* t$ z* M' e9 V) [3 Q+ e

5 n, F. \) M' \9 L% s7 w; O</li>" }. r) e$ V& h

! [. v, ^- _3 n& l/ \5 A5 F
- S( A# b3 r( L* K; T7 ~
$ y3 A9 x! c" r/ ?( q+ p  x1 A
7 y' N6 A0 w7 D0 ?: U  ]0 j' G% w

9 o) ?+ P) @5 l3 X</ul>' B2 R, l5 V5 ~* W7 J
<hr>
6 ]  G9 ?+ l" z- c<p>Leader宕机了如何进行选举?</p>
+ u+ t% ^7 m- O1 H* X; ^& e5 \1 ^<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
# ~# \0 A5 o+ z2 h<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>. |( O, _( E( H
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
& j1 F& T1 G0 a0 y. t<hr>9 t8 {! a8 y& c1 z
<p>选举后数据如何同步?</p>
* a  v8 A  p- `  `/ h5 ]. Z% O; {<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>
9 Y0 f1 v6 Z. ~- K$ }<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>2 q7 U9 m7 s' j9 Z+ `
<ul>
5 o) J0 e& n9 I* _<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>2 Z- u% l/ u8 g( b1 b3 n
<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>( o" W" H1 E9 H2 y, b
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>) d! h8 x  a7 ]8 z1 p- V  N
* y/ J# E1 m+ N1 S

0 K8 T7 v6 Q, F( |7 l3 e: D$ K* W: q  p
  ]3 K, F. }( p- k

$ t8 v! @2 c" k/ o% m
/ |2 L" p/ Y' W7 n$ f</ul>
7 Y$ S7 K1 Z9 _" i! w<p>同步策略:</p>
' g. Z7 f! e8 `4 @) w  ^<ul>
! [/ z& s4 P( q! U<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
' l' D2 M$ u" E% Q! o1 O<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: 0; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important">首先Leader向Learner发送DIFF指令,代表开始差异化同步,然后把差异数据(从PeerLastZxid到maxCommittedLog之间的数据)提议proposal发送给Learner</li>5 p$ O0 M, x, p
<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: 0; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important">发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步</li>
2 A  k: C( o, U4 y2 e<li style="margin-top: 0; margin-right: 0; margin-bottom: 0; padding-top: 0; padding-right: 0; padding-bottom: 0; outline: 0; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important">接着等待集群中过半的Learner响应了ACK之后,就发送一个UPTODATE命令,Learner返回ACK,同步流程结束</li>5 }' V  @" B2 K/ L5 R: k, \
0 q5 M- _/ P: D

* v' o) b" E7 C) ^3 F
: F* S/ H& y) V. g) S
  x3 P' o% a: e$ [! T9 h</ol></li>9 p& q2 X* F8 G* v2 p  E8 u
<li style="text-align: justify"><span style="color: rgba(51, 204, 204, 1)">先回滚再差异化同步</span>(Trunc+DIFF同步):特殊场景:<span style="font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif"><span style="letter-spacing: 2px">如果Leader刚生成一个proposal,还没有来得及发送出去,此时Leader宕机,重新选举之后作为Follower,但是新的Leader没有这个proposal数据</span><span style="font-size: 16px; letter-spacing: 2px">。</span></span>
& i# ]  t$ Z# F3 T: F. w7 E7 l9 f<ul>$ `: A; l  B0 F; a) ^
<li style="text-align: justify"><span style="font-family: -apple-system-font, BlinkMacSystemFont, &quot;Helvetica Neue&quot;, &quot;PingFang SC&quot;, &quot;Hiragino Sans GB&quot;, &quot;Microsoft YaHei UI&quot;, &quot;Microsoft YaHei&quot;, Arial, sans-serif"><span style="letter-spacing: 2px">举个栗子:</span></span>假设现在的Leader是A,minCommittedLog=1,maxCommittedLog=3,刚好生成的一个proposal的ZXID=4,然后挂了。重新选举出来的Leader是B,B之后又处理了2个提议,然后minCommittedLog=1,maxCommittedLog=5。这时候A的PeerLastZxid=4,在(1,5)之间。那么这一条只存在于A的提议怎么处理?</li>
& z( c8 P0 c( `; k4 k3 ^5 A<li style="text-align: justify">
  @+ K# ]: O& W4 Q, j9 f4 D0 l* |; }5 i<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
0 U1 f* C: K* H  Q( K$ \) v' w5 J1 R; g6 g! q

  I; h! l! \4 [: E
/ }+ i$ |7 G+ C( _- _: u$ e5 _  F0 @* d0 D
</li>5 C" j. S' J* l+ @+ @$ e
) F" N6 \! S0 l" }) \1 Q0 ^+ O

, W; |3 J$ q) q; e
/ U5 ?' Q$ p' {3 |- E2 L, p! C' b2 [& X8 v1 Z/ Y5 _5 X5 N
</ul>
: z0 I. V2 R5 e" {# M$ W4 U% T3 g# {
$ G2 k+ Z. p1 o, i. J3 V2 n
# {- M  k3 [7 [. l9 _
; L3 o" t: ?4 ]6 `
</li>
; U4 l  B* m, D4 ^) E& W  |" C: f<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
; S2 w5 T. R3 W<ul>
/ g8 t+ P; V( v8 e. b<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>+ Z" r# `! Q2 @+ g
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>
6 |  {! @+ K0 K<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
& w/ y3 l) }/ n6 F6 Q1 R7 U
2 N. @  \  z! ?# h8 z; Z. u* _. i2 w- Q3 \( t( A

" \- d3 D; r! D' Z! ]; p8 E. |* }' F, w" [
</ul>+ u" }1 ]; o, A9 p
; i+ n; U, M) f5 H
& R+ p# |5 C9 G9 e! R
9 z; K6 Q, Y, L7 x' D2 K' C
$ a% L% U( o* `: F4 u
</li>8 q; V, t) x9 _1 y
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):
+ O# q: G% C- P0 |7 [9 j0 R<ul>) t5 ~- \: W: L- Z7 r9 u* v
<li>
6 Y/ `& r% u3 s; b' p' P0 j' V( l3 e0 R<p data-tool="mdnice编辑器">适用于两个场景:</p>
# p2 r5 z, C  g! [<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
7 ?! s% s, N' u: \$ ]<li>PeerLastZxid小于minCommittedLog</li>
, P5 h( h6 a: T/ z( ?) m<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>
! Q2 [, e2 o( n! m5 P% X( p9 v1 U: k3 @3 R( Q; i- ]

% w: I7 B5 O, M* K7 t, W1 h, P4 l' ?# U2 T% g
( x/ H% U; v; D# z- L! ]  v
</ol></li>
, C, g9 b" `2 l) h, _; S<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>4 w; e# @5 A) h- V( z4 h! S' Q
; }3 h! {: R) I, R2 S/ K7 B( @1 Y
; [" D/ [+ w7 T) \+ s
' Q9 J) C& P% F* w, M$ E' Z

: e1 F7 H) P& p7 q</ul>
3 j6 m' ~( a) f, O$ r; l( K3 R
9 U4 A/ T) B. c; {3 R: G5 y
6 ?/ \% Z3 Z4 P  A* [
( J$ n- \4 y' _" `
2 ]% ~3 z# j& n+ f0 P</li>9 V, a  ^! P; A1 A2 ~& P% K! i
9 _8 q7 d  d, k

7 H8 w' ~; ^% t  T) ~% n* F" e' |% {
$ q3 E' k9 O# m
</ul>
1 P* g5 Z1 h& m5 W# p<hr>. T* {- d$ `  W( F# B% v2 s# a0 p7 r
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
5 |! a/ t5 |4 J3 P- ~<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p># f: E8 k& w# O
<ul>- `3 ]7 o8 h% G: K0 `
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>! s6 ?( ~; ~9 `" I
<ul>% o# M+ G! i1 ~" U. t$ ^# Y
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
1 O4 q9 \6 ~) m! }<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
$ Q! K% U8 }4 C. G, [/ A. L
0 |4 X5 S. J7 B4 G% h3 J/ ~- i. Z4 D( z$ m+ g: P3 A' _+ ~8 h
</ul>% @0 t/ D$ b1 T9 V
# ]' t' l8 |1 T2 r$ Y

$ p) H4 o+ ?) H; L1 z0 j  L: j- E</li>" {  a, V* s4 H( z+ b  v3 J
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>! b. p  R. ], b. g
<ul>9 z* Y, x" V1 Q- x2 f
<li data-tool="mdnice编辑器">* Y; T3 E# q# y: i0 D( K$ F
<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>+ p: e% h* t& s$ V

* [8 g4 i( G: ?7 K2 ]: R9 f: ]# s+ \! x- Z  C0 x, E
</li>
$ S6 @: n4 J! r' ^; P<li data-tool="mdnice编辑器">
% V- H; p6 z  y& H8 F, ]<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
1 B( q( W& P& Q
- M& z8 {# f9 w8 }- n& u* i7 l: c: v$ S% K. \2 E3 a
</li>- N! _/ O6 L. O0 L+ t

' v5 c* N* j( a; O) g8 J
1 ^% L2 T6 Z" x$ @9 U. m. U</ul>
1 g7 t4 Y, L/ C' [  {5 z: [1 V) A1 F  A# `

* B! ?$ e7 T& ^) z; o6 R</li>
1 f* j! j8 J' ^5 Y<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
+ c; q" `$ ]# Z4 r<ul>
5 f1 w5 [3 a: W& n% {! F<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
; H6 w+ N& O* i& l& \( X8 l( ]0 i5 D! O

7 ]0 P; V. F2 W! H/ M/ A2 ~</ul>' }) _9 J* i# @* p

  M% m/ w; M; l* {% m8 o7 i
( K+ \: z- d+ }- \( i: S9 f$ W% M</li>6 z& v/ o0 g  ]: n- D1 h4 r3 ?5 w

& s! f; f. }. @; S* l- j2 P, o9 t/ c: z7 N! Q) e; s
</ul>
0 y  J2 u6 J/ ]<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>, J7 n  r' n4 r3 R+ }, |
<p>zookeeper 是如何保证事务的顺序一致性的?</p>
& T& Z5 L  Y. j$ i<ul>% ~  ^/ h  j" O" R0 H1 z
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
9 O" j! p: Z2 [$ e/ Z: e: F7 n9 X( N4 d& Z' J+ \! H/ u
' c2 I% `0 l  B
</ul>
/ E% w5 N( s8 p- N: x<hr>
# ^8 Z+ z' S4 q# A<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
1 J8 I  S9 `" M8 I' }& d5 `<ul>
/ W" B: e* T4 w7 f/ g* a  e<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
5 l( }; p8 f+ e/ X7 o) Z* v) W! G$ Z' {4 y6 ?8 A7 K  x7 m
7 R, D" P8 _" b
</ul>
8 a5 b/ y! h: y  E0 I3 i' q<hr>
; ^6 D5 t& h' Q! Q2 Q  m7 @3 H' M<p>说几个 zookeeper 常用的命令:</p>0 n. v' B. k9 y2 }) M* ?" u
<ul>; c5 s% J* z1 p: ]' c
<li>ls path:查看当前 znode 的子节点</li>
0 T! Q9 k6 g1 c: J$ [$ s<li>get path:获取节点的值</li>7 \. m" G6 j+ B3 Y" I1 _
<li>set:设置节点的值</li>' t$ \! L# q; E. R* J
<li> create,delete:创建/删除节点</li>- b+ K% m9 o4 F: N4 a0 o2 A  M
0 L" I. Q3 Y' ]# X* N% }/ O

7 c$ ^! C- K# X. C0 |+ b</ul>
5 P8 V. m2 c7 n: Z, ~6 I! k3 H<hr>
- o+ s1 B4 O9 ]% y* \<p>会话Session:</p>
" O7 f0 Z/ f& m6 s/ R6 q<ul>
4 X6 E& q# N, t' f3 W. F<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
& [# C3 @) s. X! z& W! d/ E3 }- a

8 p* q0 }4 k( n5 x% C</ul>$ A4 l5 g& A1 F, h
<p>&nbsp;</p>7 ]  U4 p# e" Q* ]: h( Z  V8 L: C
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
' L* Z) p: V1 M$ u8 S' b' k- ?
回复

使用道具 举报

懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|飞雪团队

GMT+8, 2026-8-28 06:56 , Processed in 0.070525 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表