飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

9321

主题

9409

帖子

3万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
30293
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式

4 D9 g6 J4 R1 B7 {' ?<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>, j7 V' P: r8 f: g, z6 ]
<p>&nbsp;ZooKeeper 是什么?</p>, ^8 w1 W4 H: v: \  o
<ul>
7 M) @! Z5 e2 ~% p4 Z. f<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>
8 N% [& c$ ^+ B1 P8 y<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>9 ?; K' _5 d  ~' e+ G
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>2 R4 _" L+ L6 b! [' U
</ul>; u. [: s1 S- T: K  k% y9 a
<p>Zookeeper的作用(应用场景)?</p>
$ T0 L( d# S3 ?0 c$ l9 \<ul>
* }& g9 f& t) ]<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
# |9 ~- x, W# ]2 e& r. m<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>/ ?! r+ }% @4 `' T2 }
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
3 K" T8 L% B. z<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调用。
/ W. e) d8 c2 a. @/ ~* s+ K! b<ul>. `4 x5 V0 t4 ^* S" l- a
<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>. N4 i7 ~0 F' i1 W" H
<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>; t. i, o+ K: d* S, _! m
<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>/ |1 R3 u2 W) l# `4 P0 m
</ul>
2 G; ?# O, t9 P</li>3 }, C- v. x8 _8 j% f
<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
) s2 R$ h6 g; j2 m, j8 Z- a. ~8 Y</ul># @* m0 k) \5 w( a$ t
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>- r9 I' U2 F9 }6 {: M. p5 D9 G' x
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>) E  Y5 A9 p6 I) o
<hr># D: M+ c5 G8 ]$ ]: W
<p>&nbsp;文件系统:</p>0 P+ w% s$ [* e& m& s* A
<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>! R  v* x+ Q! a% c
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
7 ?' F$ h! C- @: v% S5 i<p>&nbsp;Znode节点主要有4中类型:</p>
% {/ K5 U+ H  g8 b+ Z3 ]1 ~  @8 L<ul>
: D5 C) x7 Q" v$ D3 h<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
# O* U& f( M* v8 e  i2 d) [<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>( [0 y2 Q3 ~9 z4 V
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
) E7 N) G- g( @$ P1 p* Z. Z/ X; L; L<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>4 k/ e+ v* N4 Z# u
</ul>
1 U/ e" k& z9 ~5 P! r4 G<hr>+ p% g$ d' M2 I# U# p. @/ P  j3 i
<p>&nbsp;通知机制 (监听机制)</p>
) d5 N/ Z3 t  {- l# S<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p># q( p5 Y/ e  |
<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>: F  ?# b+ Q/ b6 O8 x
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>. O! r: z" N  ]. z! _" m
<ol>4 {6 N' w* T, B! g" J
<li>客户端向服务端注册Wather监听</li>
. X0 U; @+ O7 Q" Z7 H7 ]<li>保存Wather对象到客户端本地的WatherManager中</li>
( K) \: b( w& L9 S# D' s<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>) w4 K8 b# W* Q  i/ ~. h
</ol>
7 c9 I4 [6 ?" n<p>&nbsp;主要监听2方面内容:</p>
) h) j! j& ^9 [<ul class="list-paddingleft-2">: J! r9 w: y6 C* S5 ?
<li>
9 B: j. i! N. e, b<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>( n0 O& Z7 g7 [; e4 N
</li>- r' b- j  F5 q8 S1 s% g1 l5 l4 Y( S
<li>' m/ l& R% o$ [
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
6 V7 a/ w1 n) Q6 R</li>; z3 @1 @- v4 H" Y( v
</ul>
& l3 \- Z# Z# m6 J) ?<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>
( [* C! z& s4 |4 p9 Y$ i<ul>
- E0 L* ^7 [- R* @& Q# E& f<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>; y' I5 f8 j$ a: x
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>, n1 o6 n3 i3 e6 y, F
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>* N( h- B5 n- i/ M, P7 n2 K
</ul>) _0 n7 r0 W+ l+ c8 g/ a& m$ U
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
& d2 R/ h0 D- d<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>9 Q1 N* s" M: E! A  y- b/ a
<ul>
7 M! D  h8 q: w' {* ^, X<li>Leader:负责写数据。(写数据都有事务)</li>( J$ @& P' `! k$ a  U
<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>
  Z# i4 z  w! x9 g: y  h+ y<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>0 m$ Q( C& z8 E2 Z5 b9 V

" z$ W! S* J( p9 X</ul>8 E  N3 [. J; p: R
<hr>5 G* A( i. Q$ Z& n  u$ q
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
6 x, X" R% J% a  R3 y8 p; ]<ul># n2 d, N  L3 A$ t
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
8 {+ j$ ]; h' _& R/ H<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>' H7 ?5 m: ~7 B+ W& M
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li># R" L8 O8 c0 c& `7 U
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
1 y% J9 p# k( D2 N6 A0 n' k
, j; K# O7 k+ K1 V! c. ?</ul>
/ Q, {8 p2 _. l0 `" J  R- h<hr>; Y, i; m( I4 s6 j' Z
<p>其他概念:</p>
+ a& G  x. y7 |: w, t<ul>8 B8 k& {% N1 z7 G* R
<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:9 o) E5 M- `' H9 q0 v
<ul>
( c9 {( L7 y  l* b% ^" R8 Z- a) ]<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
5 ]6 d; C! [0 D6 I1 I( ^+ S) K<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
: v- \5 E' g/ ^8 s" }7 \4 }/ K7 B! _" q. i5 p6 m6 n

2 M( {* A9 A0 A- k0 M6 h) w7 y2 X5 m$ {</ul>. _# U- m* u+ p

2 a' d- |8 F) q6 O; O* \) Y: x5 I
' J" X* w1 w& p8 c7 _</li>
0 v( n4 W6 r. J* z  T
  u8 d2 B5 f4 u, C8 J/ @4 ~% ^6 f, N, {0 M" _
</ul>% z9 {3 }- @" V( ]( e6 x
<hr>1 U) V9 R' S; k$ p# z
<p>写数据原理:</p>
0 `- |( O' A, M$ m9 @  o. F<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
8 U! U4 X# l- H: i/ R& g) m<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
, q- j* u1 c& e8 ^+ ^<ul>. j3 u9 F% G7 F+ i: E9 E2 R
<li>写给leader,leader再通知其他节点 </li>
* Q& m' @. Z; ~<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>. l, U$ u/ \+ A3 C7 L
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>
6 r5 u8 s5 z) s3 w
+ [8 o1 g0 i- U) E* }1 P8 u9 p0 y
</ul>  K% |* K# J5 O  v. }
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote># m. f' b6 ?9 J, O
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
9 W8 o9 D6 K5 Q( [3 i2 ~  y- q<hr>
7 |, q* I, e" t9 n3 u7 y; b<p>&nbsp;ZK怎么保证数据一致性?</p>) u- S% C. @& O% I" J. r+ g
<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>6 z! V2 r" O2 r
<p>&nbsp;一般我们正常是消息广播:</p>
) \' J5 O6 [+ i1 D( R8 |- C<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>( B; p$ s, ]0 Y& D3 w
<ul>8 A+ b, s' D  `" w8 x, I
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
2 I! q' s! s& B: l8 L" h<ul>
* ^0 Q+ N. f4 e5 u  ?6 V$ U* w<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
, o5 p! h6 w$ W9 V<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>
% z  a  I! v+ z# x0 e5 h6 B8 m) G6 I# ?6 Q' j: K( l
! J" t  V: L9 T# x0 r
$ B& h$ D2 i3 P9 L& z( \

; P% c. ~! S. l4 T; j4 x
4 G' X6 B6 J2 m6 x" o) l; R8 g& k4 m% V* ?( v8 M4 x7 J. p
</ul>
0 Q4 N, h8 l: Z( P  d3 b5 g. A. u7 c2 P  T" f' f$ l
4 i! s+ ^# [* }9 \2 v

0 Q/ {1 _5 l, v0 q$ n* J5 Y# V- N+ f# b) b1 Z  \
: R( v6 E- R( m9 w4 I) Q5 R, u

* h5 F( M6 k! K9 K, c7 Z</li>* h, c& [  u1 C( I! w
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3, F5 j+ b3 H4 o' }4 R3 S' ]" z' ]6 i
<ul>
2 Q4 x, l3 ]' \; w% z# g<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>+ d! q. \; A0 r. Y  V
5 L4 ?8 q9 G7 h7 Z5 l: _
: R! B9 n! L2 w% Z: x% o9 Q" E

' b2 O1 a, p2 w5 C5 M1 _, O- `; L1 Z! Z6 o" I1 N) V% ^
. c9 U# j2 u6 S6 L  `6 L
' S$ Q8 Y) c0 p
</ul>
* l( ?2 ]  N% N* T# b2 W/ n9 G7 `! {9 l. ~: Z# r
( _' }& X" `- u  ^

) T8 \7 i2 q7 J) Y$ u
* M  c% m0 e$ b& D2 F6 `- y, u, V0 q- Y  m: Q: z; s
; i2 M; f( m1 q3 r2 s4 s
</li>
& _$ o( Q; r% o. V2 `% E2 H$ ~
8 k% ^$ A6 d3 `3 t( l% f* M; e5 y3 ?& Q3 U/ x4 r( P- F$ {
# [3 V9 [  k# z
5 E% O8 b( o: C$ \" ^. ?' d
6 |3 |( m) `7 C5 G) _/ l% D

! F4 n& Z) s" k3 q2 u% [</ul>" O8 x; M. V( T/ {" y# s. ]1 E
<hr>3 G2 i* Z; o8 r* F8 m1 x2 _
<p>Leader宕机了如何进行选举?</p>
" [$ `/ V- M( A  B1 [" c* T* a<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>' ^& v, V* s: k, r
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>
+ ?, w& k+ q$ @<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>4 s$ S/ m! S; f: [2 z, x/ p" S( ]
<hr>
5 p8 E& a1 _- x: F( r<p>选举后数据如何同步?</p>
$ a0 ?6 g0 [* a# v3 [<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>( z3 l  C' t5 x& R" I! h$ j
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>9 M) \& n* T5 V# u
<ul>; j8 E( C8 D( i- ~0 f  b% i
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
3 [' ~# Q; }% v( K<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>: s* e; Z! _# e! s0 ~! ~# B- @% c
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>) x  |1 K: c: O. G( ]
! N8 n8 m4 V1 c/ ^. Y: }% l

! n' [6 H, a% _- R% P8 @4 P6 u) n, S# K& T* P
' K8 D5 |  I+ p$ I5 d

7 d) {, b" s7 S$ R3 s" m! j  W( p% J, w& Q  K! t: [2 N
</ul>
+ |8 x' f! N; P$ u2 l- j0 `+ J1 A<p>同步策略:</p>
/ S: }; L4 l0 j<ul>% s# E- u5 Z) E- F8 `' i
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
+ p4 G: C$ E/ r" x<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>
4 a6 R* U; p6 g6 w; m<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>  h1 m0 d5 Q0 U, ~0 `2 n/ P1 I9 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>
- M! J6 S7 ]0 m; M& s0 {: Z
4 i: {: F1 `8 ~) Q: O: q3 ]8 u( u+ x6 C/ s
# d0 f8 u) e: R/ D

8 ~* f# h+ E0 ^' H2 T9 f8 h</ol></li>- V' `' L% Q5 ]6 M! m! c) @
<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>+ v5 d$ z9 C7 S7 @4 g
<ul>  l& D  d  ~" x  y$ u: q
<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>% E3 P9 h/ R1 k& u
<li style="text-align: justify">8 N! L3 J) N# }2 g. f
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>+ o( l" Y% m1 o$ X, _" c
8 x& ^4 i7 V: _2 Z
" h5 u9 M% m0 |/ h, i, V" l
$ n: {8 q+ Z& g6 Z
( `4 w0 D$ ?# D! k
</li>
5 _4 F9 h, M1 \$ m! x  [7 E) {% Y
/ k4 ]8 }( @# x% Y7 m9 P4 T2 D& q3 C
" k4 J6 X( c0 G) w6 j1 P
) g  D: A# L' a6 Y# I- [8 W7 h8 d2 Z9 W$ R
</ul>
0 y% u' N9 {" `1 t4 S* |
1 L. ?, P" ]4 }( f$ q2 j
5 i; B' l, v5 |" I6 C( s4 `$ B  L) c8 K; w/ t7 [$ B' Y8 \

& O+ g: @8 x0 O" Q( }, j3 _! L</li>! _. Q1 Y! f5 ?& X+ V5 u
<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):/ h7 w* Y$ C* i/ ?
<ul>; T: O5 u/ i4 p
<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
4 s% v. P) t( I! B9 f<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>
' S9 z7 I1 F  l8 p) v<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>& ?* i6 p; ?7 u8 J

' {  R& V5 k& a# k
9 Y! }, U( n4 p5 N; i3 S3 T5 j, D6 f* ^' c

9 o( ~1 Z/ N) J  f* P4 `</ul>4 F( V+ ~+ {4 f$ d1 J4 Y$ V

  h9 E0 U) ~3 X9 `7 v- O# t$ P
1 d5 A8 f" E0 S- a$ t1 o6 @) r. G$ Q0 }4 @

3 f! C4 V1 `7 q! |1 A</li>
* X! T7 b0 m, A9 n) i- L/ N<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):
4 E: n5 k: W, `- m& S) Y. O<ul>
' y9 F# C0 G& b: }- R" w<li>
9 C- D4 f) a6 a4 e- L& J5 ?9 }<p data-tool="mdnice编辑器">适用于两个场景:</p>6 Y1 O$ S& n+ V+ l9 s1 {
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
& o( k% n' ^3 j6 h6 g4 b5 e<li>PeerLastZxid小于minCommittedLog</li>0 q- k7 U: F$ X) _: n
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>
* Y. k- a4 r' {% O: _; L6 M- W1 o* C, ]( h2 l& W1 b; H8 H0 [
0 `- r) w1 q/ f
) V7 A4 j5 `8 m) }* l6 P% T

3 ]1 a! w& q  n$ E</ol></li>
/ `6 g: p) B! G. b! \  a<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
) O9 M+ f& R2 [4 p0 A) p# z7 G3 ^1 u; d0 g' {
: x5 e' P# X; Y+ Z- J

  D- c' S% D& Z: r
5 ?: Y; k+ M" ~1 t, b3 n</ul>
' v* {: k  J+ q, U/ {$ A
2 S  z7 A& G9 e9 v7 G7 U3 d- q" e; u# B. }7 d0 W

/ w* i0 G- _0 z9 K& e, x! h  i
" v6 _2 J- ~8 @- Z( ]- \</li>
; K' _8 R& j3 q$ M# U5 d4 ]$ d& b
9 O& v" J& p% D, ]  Y! ^2 d" z. [/ r7 H* l7 `# O
1 x9 j7 ]4 e+ ]
( F5 ]( @6 m+ k$ u3 U3 D9 Z
</ul>
) p, p* D. _( c3 f1 ~: I<hr>
4 w: d  B6 X- T. [<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>, v, @6 }" x. ]: d
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>3 c* U! i. T% G% j9 k- U
<ul>
: r& F7 j8 q- c<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
  ?5 s) a* n: e$ m5 W<ul>
9 W) t# d. L# [<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
- Q  G/ i: ~* {+ I* G<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
' ~! B/ K$ [# {8 @# {% @$ C1 R
5 Z6 E) ~% h7 t& j& {  }; M' a" C1 w1 e! b7 g, G1 M& @
</ul>( L# g3 m7 J% J5 O7 f6 c# p
' t6 V  [$ E+ t# B- u) n( E; C
/ n6 g# r: _0 T* `0 b% R
</li>& y3 D2 d* T: d0 u" m
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
2 u, T- T8 D% k" a<ul>
2 l; ?$ V: T2 k8 @4 l: `' ]$ _: @; U; S<li data-tool="mdnice编辑器">( _- t) D: Z% ?8 Y+ ~* G. R: @5 w
<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>: n1 H5 v7 p+ @

7 l' Y$ e! ?- P0 l$ a, i' T1 T( i- q, W- i
</li>
! q- I- k& a" q. \2 @<li data-tool="mdnice编辑器">
% d# N' k9 p7 K9 i# ~* B7 K<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
3 K8 x4 P: [" ^2 X$ Z8 K% ~* I; x3 i" Y
* ?! B2 w4 S7 I# a
</li>' Y% c  o. B; y  N- r
8 \0 d; U/ t, \

$ W, l% z! x4 Y* G</ul>
: i. r; }, V- C/ b7 [' w+ K2 s( g+ F+ ^8 o1 n
, I, v, R( Q5 d+ i
</li>
$ }1 _/ X9 Y; `) M; Y( ?) g0 v<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>! u, d' O: v- f9 d
<ul>
8 |& f6 F  r  s; \<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
* f6 ?6 t5 z5 \$ e" U3 {6 t. Y  W/ T  d% R: t+ ]" w/ |
. M2 N9 p) z1 O) n- G  t. n2 [
</ul>
  t1 l( p. C" Q
: ^0 }* }$ w- A2 N9 S2 b; L
# C1 [% \( Y9 Q- u</li>8 Y; d/ l: t* [3 `& C

- @2 l1 X; Y% y# {' t  ?' Z- T+ W9 e4 J0 {7 O  y
</ul>
/ ^  C" n& y9 I3 C! \: I<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>$ U& m& i" r" e& Z2 K
<p>zookeeper 是如何保证事务的顺序一致性的?</p>
7 r3 T( b: h3 ^<ul>0 B( A6 e  q  T8 k* I
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
0 G, S) G0 z7 [/ s! t( Y5 A8 @2 }7 a! X5 t/ f' u: n

" o5 R, c& q& S! H& g7 D. y; a0 Q</ul>
7 \4 k9 ?7 r0 h) t7 x' b& B<hr>
8 n8 {8 v( ?# z! c/ A% ~<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>! p, U- {( l  ?$ k0 m: C
<ul>
0 N6 q! a. N% e  ]4 J* {<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
+ Y1 i* Q( R' }
$ ]( o* i. w( X; c
2 l. f* N5 q7 K, m8 s</ul>
( E2 P& I5 R% b<hr>$ `9 `$ W& F# t3 o  L  m
<p>说几个 zookeeper 常用的命令:</p>5 L2 U3 _4 S1 y! Z: z
<ul>
2 H' G. X9 \# [0 ]1 n* @* ^<li>ls path:查看当前 znode 的子节点</li>) u$ u$ F! x7 {8 r0 A
<li>get path:获取节点的值</li>
) C9 [* E4 s. N) d! S<li>set:设置节点的值</li>
* Z5 @& y% c" H, q4 ]: M+ M7 N<li> create,delete:创建/删除节点</li>
0 r8 M0 |+ V, j9 Y3 e% ^: u9 O( e; X% U; f" B
- j/ h, y% Y3 K  a
</ul>$ p1 j( ]$ ^: I/ ^, L0 J1 A
<hr>
+ A# t/ G: P" _4 T- y+ t/ h<p>会话Session:</p>
+ R* K" o  H, H  w# ]<ul>: m$ a2 I8 i$ H0 u' |4 u+ o
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
7 X  J' S# i4 ?6 A) R0 z7 i- ]: a8 U2 d7 {: z# }6 J3 S/ y) w
* R% A" \4 F- k# \( q9 @
</ul>
; }( g/ T; ]( O* e$ j<p>&nbsp;</p>, K- R. [; e, w
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
& P- G* e* d. ]
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-10-27 22:52 , Processed in 0.068102 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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