飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

7100

主题

7188

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
23630
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
# d! H9 K1 S- d/ Z
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>
6 q; N0 B! Q: D# D5 J0 M<p>&nbsp;ZooKeeper 是什么?</p>9 C' i' Z! W" O, l, e3 |8 A
<ul>3 n& ^5 |0 n7 v8 K0 I( e; K
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>: o) t1 i8 s& ~: V2 ]% ]* \
<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>
7 p8 h4 G( Y7 D  W! O<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>
3 \+ g8 O" d% n, x0 I</ul>
7 V' q$ a" t5 U; ~" A& R<p>Zookeeper的作用(应用场景)?</p>
2 Q  x, J, j& o$ ?<ul>
8 k; E) I$ g+ O1 R" o<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>& `1 C8 S3 O% C% n
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>8 A: J: F1 R  ~
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>" L1 o' Z8 V* m3 ^
<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调用。- S3 W/ B0 _3 a  N) V
<ul>
/ ]4 g5 g: s- y" r4 z) [<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>6 B, q! y- F0 P3 Z+ ]' P
<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>( `9 B1 S/ \1 F- s$ d4 O
<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>' c4 j0 l0 R- N; l
</ul>9 Q, b) L; M  W0 l: F8 J3 }7 e: ~
</li>
# o% y& \  m4 z% G1 V<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
' F$ E" Z( b" W( d2 ]$ v/ B</ul>
6 h: y1 H5 g1 e( z2 `2 Q4 \<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>, ^- _  @4 D* B+ V0 D0 P; e
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>2 f+ M0 d* }/ M: X7 _. {
<hr>1 g* X' U0 i3 @8 ?9 U  e
<p>&nbsp;文件系统:</p>6 Z% a# X  w9 a0 y( l# S+ t
<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>
5 |2 Q9 v9 Q* k. z<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>! d7 m4 `5 h, }; l) i- M
<p>&nbsp;Znode节点主要有4中类型:</p>+ O2 z6 v0 f) Z$ ~& Z  ~" q
<ul>
) u( r! B! s' i& o8 O1 C! U<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>0 e  k$ _3 Q6 i7 U5 e; m
<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>; t6 h7 i% R% `: _( A: i
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>- T1 Y! ^. g9 z
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
. a: ]' `7 J* C8 g2 }; ?</ul>6 G; ?% i  m: A3 m9 l0 j
<hr>
7 @9 X& O# _, e' {<p>&nbsp;通知机制 (监听机制)</p>
7 {- @' T6 m) O' H- i$ x<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>  f! l$ I, D& I2 \% }+ o; S
<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>
$ N# T# w/ b( k+ D<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>+ p) ], }: E3 M  x# a
<ol>
' ]* `; V( Z$ T. ]% {<li>客户端向服务端注册Wather监听</li>: h: Y" k& Y7 p' u% T4 R! Z
<li>保存Wather对象到客户端本地的WatherManager中</li>1 f: R9 [9 U$ E, k  ?9 Y
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>
7 p9 Q3 L) @! M/ Z8 L' b, `" R</ol># N4 c- ~& d5 F
<p>&nbsp;主要监听2方面内容:</p>
6 D+ Q$ Z( O% d<ul class="list-paddingleft-2">. ~" f% A8 Z  P$ q
<li>% E$ c! [  M$ l& S5 [
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>4 a. s9 P$ }& u& ^( i2 R' v
</li>' X1 g2 m# J, N8 `, v, R5 `# J6 F  N
<li>% t* G/ S; y- F+ r+ j
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
+ w4 ^3 f( t! Y/ j. a  m  q/ a, _</li>( _$ I" g7 u2 e: ~  r
</ul>
% c) a9 I+ o/ @<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p># W$ Y, l7 Y* k6 n5 {: x7 k9 [# T
<ul>
2 u! j1 K- o( ?6 M# L" b* D+ S' Z<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
! P  J+ e* v1 @/ x  w( j<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
2 y0 c/ }  I% ^<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
! \2 ]1 Q* M) |) X</ul>
1 ]0 f5 Z! V1 A, y* h! ^5 K<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
! T2 A; v4 {7 s( ^<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>$ G! y7 f: W+ i0 b0 A* X+ c
<ul>( a. I& L% `( L: K+ M- g
<li>Leader:负责写数据。(写数据都有事务)</li>. p( N$ Y% W2 j3 I
<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>
0 w, h0 H$ x: V8 Z9 b7 b) F<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>4 e* D3 A$ t1 l
* x& z( O9 p7 I
</ul>. m- A; N$ I0 X" j& \  x
<hr>2 S9 G1 ]5 f, o7 ~
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
/ k- K0 y4 W& ~8 _4 J+ X<ul>) q" t3 o  s8 L$ n! O& L
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>: }: ]) H; a2 ?! k. m% S$ s! k
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>! J3 E  x! n- Y* x2 b% m+ t1 H
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>3 o8 g  B7 ]8 {3 K
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
# f( ~7 n& B) T. W" \( L9 L% t" r: \' u4 c  p) w
</ul>: }3 w# }1 m8 ~: k) Z
<hr>' a1 X* s7 D& L) E/ n4 M* B
<p>其他概念:</p>! K0 K6 t7 y$ I$ g0 P$ P, Y
<ul>
' |- |. R1 u1 q% u. O; k<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:& _+ b4 y+ G/ a- P" S+ @
<ul>
8 ^9 G  q2 j/ n  }) @6 |% k9 v) g2 b! o<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
1 E: A, \) [6 X4 z/ q. J7 Q% @/ N1 R! h<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
. b) S4 L( q5 l) b: i7 X+ |* [& V0 m2 W
$ E5 H2 }1 S7 f
8 Q7 R% }& h2 f* t</ul>1 n& R) {2 [* r# y5 q% \

. p5 d8 v9 u( E$ t
6 D: ?+ o# C0 R+ {' v</li>
1 {" Z/ D1 A( r# Q7 T
4 X$ [# L7 B6 \& B0 G
3 t6 n% v; [9 R" Z</ul>$ h2 }' A5 c5 s
<hr>
. J3 w& y& X6 ]( I/ g0 {<p>写数据原理:</p>
) ^3 Z9 d0 U: ]0 Q' x6 `9 H1 i6 P<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
6 T& ^+ E: g9 z4 g3 B9 b/ I<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
: D# S9 j1 ?" P4 S# J$ y5 V% D9 Q/ z<ul>
1 X2 Z1 d6 x5 H" k! W<li>写给leader,leader再通知其他节点 </li>* T% ?0 |$ ^& a4 z/ b- \
<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>! Y9 P6 V5 k7 L7 E
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>9 ~$ [0 u6 z7 G5 L

- E9 P8 g6 Z  z9 H1 O
+ d. Q" g  A* j  Y</ul>2 }) K' Z7 l6 C2 x0 _
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
) q  W6 ^3 e8 X9 V& |; m1 p# P  L<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>; S( d8 `8 E" X  c* n) e8 c
<hr>1 c" V2 Y' L! C- c/ Z8 I! p4 S6 _
<p>&nbsp;ZK怎么保证数据一致性?</p>- @/ G3 i, b: }  z- c6 o/ o1 R; |
<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>
+ M9 B0 e% T  L1 G<p>&nbsp;一般我们正常是消息广播:</p>2 j: T/ a- G! [! d  [- i  v2 b; ]. o
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>' c7 Y/ S: I1 R
<ul>
5 g8 D  z* _  @2 n+ u& l<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2' K2 r8 [& P  C" W* a9 N$ f6 C5 t
<ul>
  ^* O' e& k6 {$ f<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>; ~2 L/ S" f6 ~8 z/ a) H" @
<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>
, q* ^0 r" N. o: ~# \7 W
4 w+ U. o7 I3 c
- c$ P- e6 C) p" F" [6 W( I) V1 e
# \6 r1 A/ X: L- }$ x9 v1 z% p5 W4 P5 I

) {: M# X6 x: e$ V: d" l3 w2 l6 E. `$ H# O$ J
</ul>% \4 ^* n/ ]) Q

6 G/ j2 @' u5 F( z6 W6 L2 o6 w2 z
# l4 z1 F9 b" q9 [5 W3 ?
  B! r6 R% @. g# m, i* L6 _) p

* i0 v& ^$ O3 U# e. \+ n( K9 r& H+ t" d: o
</li>
8 F9 i0 W/ z4 S2 m! U<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
; C0 d, D& v: A5 @4 _$ b3 T$ Q3 i<ul>
. U2 ]4 v& ]) y<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>
! B" w3 V; U7 K# J0 T( R. k6 W
4 L9 H. Q8 u) D7 m
4 M6 j2 E. E8 i& b/ A- j% p* V' \9 v9 S0 \

! @0 n2 O7 T" m6 D! x' z8 H/ f9 {! y5 i. [! V# |6 Y* V

0 j/ g$ I; m0 O/ q0 Q' S</ul>2 j0 t) T! _+ \8 _8 ^; L( E* e1 N

6 Q) }' W# J6 s: X6 D: d0 P+ I- U7 J5 b5 c; A, w  y
! q% ]/ Q8 P2 P! }, k
7 O! g" |0 z8 K9 @2 X

5 K# M( J0 x+ t" ~
' P) w  m  t8 h: K) ~8 K0 Z</li>9 c- V* N; T% a/ X

9 P& I" ]) O1 f: {8 Z% L7 I* f( y" Z

- M. a( y5 I7 ?9 ]7 g, g7 n; |# m3 V

5 ~' R- h# [4 q) d) O- C( U4 w& E7 E. a3 L6 n- A* _
</ul>; {5 \/ I. \: @8 ~! k" {
<hr>8 p7 y! c9 Q: Y3 I
<p>Leader宕机了如何进行选举?</p>" M7 m( @  N9 w7 T% \  {9 n
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>- c' a# S" w4 L0 h9 E* Q, V
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>
. j$ `9 G6 `: C4 W<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>! a, Y# m$ ?# k0 V# f0 D( K1 k3 e; G- D
<hr>: ]; ?) W; a; h2 `6 x' E
<p>选举后数据如何同步?</p>
1 b+ ^$ U, e0 Q! R. ^<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>! D: O6 r9 m. n, B( L2 ^2 U
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
. D; X" k+ V0 G, R<ul># ]. F2 Z) W/ v& b/ l' ^
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
/ a' \* B5 f) w9 g<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
$ `- m1 e* K! r  D4 ^$ h<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>+ O; `  E6 B+ d4 L0 V
  j, R: }, K4 t  V. T

5 ]0 k( E+ l' `4 b/ d) ^
" f( ~+ P" j( I2 ^% ~
" a( }2 c- t5 Y% j$ l' s, p5 z4 v" h5 E; x! P
1 P1 W9 g" i# _2 q6 W& V* o
</ul># [/ |! p" s, o6 }# {# t
<p>同步策略:</p>
& n1 S2 z7 I- x! c  J, l! {<ul>: F. e' g& t6 t* ^$ d0 t, h
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
& B1 f1 e- _) @: M% `6 K& Y<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>7 {6 E# E- p+ f' t: r
<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>7 m( c3 G+ _/ i' X  `4 E" x) h" s0 n
<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>: c9 F) P/ j0 K! N+ r
0 L  ~5 v* e  ^, }- ?1 T5 p

8 p: d' C1 c* j% m, E" n, H! g, u7 Z; G. G( J

$ P0 ~, p% {3 z3 I+ l/ M</ol></li>
9 l$ H% A% |( L* l5 Z$ t, ^, \<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>0 Q/ c! P8 M* ^9 G
<ul>
5 W0 W5 s# e7 ]) J2 |<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>: ]! M" z' H; T! }; P8 B0 n* L
<li style="text-align: justify">
; X  z* a: y! F/ r, x<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>. r. s0 a* G7 |- [8 q& I

+ V* p, X7 f; b. z" S- _+ X; x2 Y3 B6 E' ~

% S/ O) b9 k! A8 u& l& r6 p) e# Z  {+ K
</li>1 W5 @3 a0 c! S. S1 p- G
1 X# \2 q1 F/ z9 v& v0 H8 g" `

( H5 B5 m8 O( ]( ]
# X' o3 z( [5 o4 q; J
9 f, P9 O0 q+ a0 b6 x  Y# t8 G</ul>
2 G- H' I, A% U4 a- A. R% y! T; A; ?0 l

$ ?" k; ^; L+ S$ o
: a, a3 w' S# W4 ~
: B  y! E$ j: M! R</li>
. \' C/ L, i* \5 P4 }( I. g<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
( h( A8 Q6 {2 I3 f- s% D9 l: P<ul>
; r( C9 [2 U' z0 S1 N<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>! b0 X2 z2 p! O- O
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>) N8 M/ V- Q7 S+ ^) O# }! {( [
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
8 c+ B1 j6 ]% c: Z; X$ C! m+ N+ _
6 g1 o! ~' W( D; L8 o) C  L7 e% z* i$ u% X& p' g- u' I" f, Z
, C  m; M+ ?* H
+ g9 M, ?7 M7 ]/ X, v  N/ q
</ul>
# P9 V: r- [% R6 E3 B& f7 `% N% u6 d) x
4 ^& H9 P; J$ `+ [$ G) H
% h5 E( {& j7 P* [
" V4 h+ H; c, C& e
</li>
  m( F- E* X, V: P: y<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):# c( ?/ N, h4 ^, O* V
<ul>
! i5 x8 F' e( Z1 z# F; h& m<li>% }2 z5 x3 `  d6 f# g
<p data-tool="mdnice编辑器">适用于两个场景:</p>
: u: j  ~. ?7 L# C<ol class="list-paddingleft-2" data-tool="mdnice编辑器">0 G# s" P- s- L( G5 D
<li>PeerLastZxid小于minCommittedLog</li>
1 I  |( n$ ~7 K3 y% U/ D+ W$ z<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>
* X! D; G! x# R9 O1 p1 J, Q8 K$ n
0 r% v$ j( T1 w) h: @

" g/ j! S* K# g4 H/ O/ G( q( p+ e! l$ a
</ol></li>
; Z' b' c& X2 B. u" z2 r) g+ n. k<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
% @# z% u5 L) A; N# W  t* p+ \/ L4 E7 h

5 f6 G9 z$ |" h& f
) k, A% t3 u, V
5 h1 ?  ^4 l$ L6 ~* Y; N</ul>
# n, E: F  `7 u
" i2 Q. M' h  }- ?6 A$ N7 v- f" q: C* j4 p5 v0 }) N

9 f+ t, Y& J$ h. h, R8 b4 e9 g; [& u4 ^2 a
</li>- n2 k" R% d( }& p. H
5 R- G6 e2 w* ?2 Y/ I
. o4 D/ m* F' |6 i' y

, l* }9 Z% t" L1 V9 q' G9 x0 J. v5 Z2 A. N* v9 n
</ul>2 N( x3 J! b+ I9 i0 T7 R
<hr># U4 h- j% e( x. ~9 s1 c3 f) r( F' V
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
/ t7 v0 t# D& I( o, d/ J) o<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
8 u) L; \! y5 h& w( C. f<ul>
. r% |) W. D9 m4 T+ Y1 S  E6 g! U<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
3 {6 V6 k2 A  o/ C" H<ul>4 E9 x. m9 H9 l
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>$ v- H$ b) u9 r% J. |
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
1 a4 I$ N: w+ H  i% p, R7 z
( E' _/ x9 v. Q/ Q) y. {$ C3 b5 o$ k5 B- B3 Z+ _( a" W9 p
</ul>
. p  M4 U! R3 p" f8 B
4 H9 [7 q: Y: u& k. o
! Q9 ~& V6 M  o( B# O/ y</li>
8 x6 R$ c' U! V% b  m4 Q<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>) }1 b* }% w* t( L) p; d0 Q( O
<ul>7 g" I$ m: S' m0 T7 ]+ N" k
<li data-tool="mdnice编辑器">
$ ^# f6 w9 R2 B. @! n$ ?<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>9 W. l3 {( ]4 ]: p

" U& j/ P3 [! S0 r  P
% X+ w" ?" e# B  d. `  `</li>  f4 L. Y8 D, @2 |2 [' k; y
<li data-tool="mdnice编辑器">
4 E/ W+ j" m" j9 h, Z) N7 \! |<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
* F$ F" S( ^, z0 N* A7 a3 ~7 d* p2 N4 N, ]

; }+ @) h0 Y8 l! P- R$ o; M</li>
' f5 ?/ w6 d$ w* M5 H7 \8 h. f5 i5 X, p1 u3 @, t' D) z
3 W0 v, t; A! m7 @; j6 k- s- c
</ul>5 F, m, Q: k0 n8 g) x5 r3 f" }9 q

* G$ h# O, O4 m. d' o) G8 C8 r2 w( d1 c7 @& a0 M* H; G0 J
</li>
8 \! u) X, K1 A0 a6 n<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>2 q& `. |# X. q9 T
<ul>
/ u9 \5 p5 V( Z( r/ b( w. m- w<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
1 _2 B  a) ?$ ~* m# ^- }( }6 T" S; F8 Q+ Y6 N4 G. ^$ Y# C

: z0 }; p4 }1 |9 ?$ _0 q( {</ul>
& V8 c% s# J+ J( y- m. T3 i7 U+ z' o9 I3 A- i9 i: T6 ~
8 j4 [! r* c4 ]" M3 F- s0 Q; j& B
</li>" n$ ^, D% t; f8 B, P
5 U0 g( W& _& F4 P1 j
8 M2 u) ~( }+ p  g( B. V
</ul>$ H0 R! N1 P6 d+ O
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>  a# r  k8 _% K
<p>zookeeper 是如何保证事务的顺序一致性的?</p>+ s, c1 |# d% V" N  i4 M
<ul>
2 L9 S1 u( Z: {3 }<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
/ L+ f: U7 x0 T8 V; [, T) q# L2 }

& ^$ z' M8 X7 \/ l" {3 C; O</ul>0 F. ~7 O9 v. A0 P" \0 X; c
<hr>
, Y- Q: }, l. j2 i# }/ M: W<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>* z6 N  K" J7 _; A' U
<ul>
$ P( C) n4 L: R# e9 t3 B<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>6 K1 B! Q/ j4 e5 q1 M3 z" W3 Q
/ |4 C" v: N2 `4 k4 x

. l+ k4 E+ Z1 P( K* u% q2 e% A</ul>$ F; F2 R# V1 y' @- S
<hr>
& Q6 F7 }2 [. w. g6 w<p>说几个 zookeeper 常用的命令:</p>& L: O: `: s' @9 M( N/ Q: m2 `
<ul>/ {. l# \3 n' \- W; C# B
<li>ls path:查看当前 znode 的子节点</li>
1 q7 U+ v9 ]+ P/ i* B" q1 [6 U3 A<li>get path:获取节点的值</li># b7 M) s5 T( J* q0 P# m+ ?
<li>set:设置节点的值</li>! V  q+ R( }5 D  N4 s3 v
<li> create,delete:创建/删除节点</li>* Y: C, e5 E( k1 {8 Z: {- p' k5 B
5 o2 O- o% }1 g' a8 B

3 L# M6 N; V: R2 z$ o</ul>3 t3 ]7 [$ V) r3 w7 O: J' c
<hr>+ `& o8 p1 c2 i! ~1 z
<p>会话Session:</p>* S; K+ e5 [, J6 [& `( a  e
<ul>% e& s8 r1 G: [  X
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
1 a% _2 y/ g3 ~# O  p# {* ?8 e! e) z
% ^) o1 K1 e0 H! Q# g. V6 s
  t( e% J* Q2 k* [0 V; {1 E6 b</ul>  m3 n3 {, ~: o
<p>&nbsp;</p>, x% ~. r, V! i6 B( v) q
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>* r# y4 R' F* n- ~4 F
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2025-7-30 18:29 , Processed in 0.065763 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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