飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

8912

主题

9000

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
29066
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
* q* {: r3 i0 C
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>8 E" O# e- L+ g
<p>&nbsp;ZooKeeper 是什么?</p>
: B9 Z, N& [" C! Q) f7 R; `% C: V<ul>
1 f' f! X' T% j3 z) q7 \<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li># {' S* k9 J% r2 Q) N
<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>8 d& X9 P/ k% z. {5 S, ?! `
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>& y4 {) ~7 v3 x0 L- }- s
</ul>/ M$ B; ^5 b5 l+ C; Y( e: r
<p>Zookeeper的作用(应用场景)?</p>
' y; o: c* H0 \- }& ]6 y<ul>
/ a4 \( N6 D2 Z5 N6 {<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>* ~1 j! h; y% ?& r& U
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
3 T! B+ q/ x! N+ [( B! f<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
: H, |  Q- p3 p' M* 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调用。
3 e1 G  p2 W. K/ z' D& o" R6 n<ul>
1 _+ z, r6 ]3 }2 n! B2 i<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>. R: A0 I& K5 m6 |% A. z
<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
( g7 V& b( m6 \7 a! U1 r/ h<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>
9 e) _  F: d* U4 i4 {( j</ul>3 R3 f) z$ x. O
</li>2 a1 k; q1 U) D3 G4 O& X
<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
9 x. f! n# h" V  r- h</ul>7 q. l; l9 T7 H/ G' L
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>0 t" v* J' E7 k7 g3 y+ B# w' P) i
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>& S  E5 b, Z5 h# N$ g, {
<hr>+ \2 h& v$ S# O" i" l) d0 f
<p>&nbsp;文件系统:</p>
+ n( b1 t8 K5 e<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>
2 t! w& s6 s* ]<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>/ ~/ ~$ m3 E  Q: |+ Y8 [
<p>&nbsp;Znode节点主要有4中类型:</p>
* z) h$ w6 g1 ?$ S/ m4 k, o$ H+ ?' s<ul>1 J6 u7 a$ ?, C6 J, g
<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
! I2 W* a: [/ @# m( P, x. a<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>8 h8 b" ?' X  U$ X: U- t# o
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>' a' }' k; s1 ?& A) E% _
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
+ _! s$ q$ V5 @. \; |+ J1 k</ul>
4 j$ ^! T9 t! m6 f. n1 m<hr>9 G8 j9 Z* ]/ r' y
<p>&nbsp;通知机制 (监听机制)</p>8 h' f2 B- O/ ^& v9 r' Q
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
( [1 G# A9 y- a+ {/ N/ B7 h<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>
9 t# ?2 G  q) V5 |<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
0 p. T0 g- L- U; H6 Y" W  L. y<ol>
( s9 {6 Q+ M. f4 _2 h7 h3 h2 m<li>客户端向服务端注册Wather监听</li>, x5 L/ L' b% T8 F- b2 X
<li>保存Wather对象到客户端本地的WatherManager中</li>) |" [; d9 {% J3 I2 X- D
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>- }- [: `3 `2 a. Q* r
</ol>
( w7 m+ p/ `% p# n/ _7 K, E/ w$ n<p>&nbsp;主要监听2方面内容:</p>
1 A3 {* E5 n8 W2 n4 P<ul class="list-paddingleft-2">
4 U6 B4 Q. A: d# q7 y& H  d<li>
* S# J: n; t; Z) Q8 A9 M. l<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>! i$ d2 D1 _) {+ g
</li>
/ B5 E/ |+ e2 `  z  ?0 o<li>; f# D5 \% @8 g0 n* S" A6 T; N
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
5 T  ~5 F6 g# B/ H, t</li>; d, E3 K' R/ f' I4 y
</ul>6 @5 z3 A" G$ R( ~, c; {. e7 z
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>( P1 R& c5 J4 t
<ul>2 z1 _2 M. q4 x4 T
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>, R, Z7 U/ r* S. E/ ~
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
6 I4 Z/ q: X5 h: a2 \<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
% J0 A. d6 W8 d  z</ul>
+ d) T& u. l( W7 X+ q( O/ @<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>, _6 m) H* D3 V
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>- n% d7 l2 w7 g- N
<ul>- }2 Y' D. L' h7 F* }2 S
<li>Leader:负责写数据。(写数据都有事务)</li>2 Y$ t+ c: g( T; A( G
<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>( t/ ~& _. w! L; [( M
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>
: d1 m, L$ _6 f% D) V- U' f* K5 B& A' O8 T7 b7 z" z
</ul>9 @3 v0 |5 u4 x$ C% u
<hr>
/ p, q% ^/ P- e, f* V7 d# f<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>: Q  E  ^2 @6 A0 o
<ul>* P' _; ?' z. k. c# N
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li># k8 K6 s. l' L5 I! F8 j: C" l
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>
8 r: C- W) u' a6 U5 u<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>; C) d: N9 [# }, k- t: e% o3 `+ H
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
# ]+ @+ }, S- w" l$ s$ k7 ?+ G" P' i/ [# K; |$ N: J( p8 j/ b
</ul>
$ S4 V- z$ @5 G% \<hr>
% {  r1 B" B* X+ U5 |5 x<p>其他概念:</p>
2 w9 q1 c6 R8 S4 U' Y& J* @* ?% ]<ul>
) t4 z6 ?& f: z1 {0 a<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:) h8 V; Y8 Z' r
<ul>
8 e3 m* Z6 A; j<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>  @. W3 U. t- y
<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>2 D* [+ m3 q6 F. `; i$ q

" W5 H& C* M  V: I: y
5 B' D5 k) |* k: F9 a  C# m! k0 n7 b7 s</ul>) ^& R7 E$ O& D7 D

, {6 F9 v) Q5 S/ C7 K+ e
7 \0 h( Y0 _' H& I! Y( H</li>. a8 l! C/ s8 T3 j; h. V" X( r: l

" b. S0 @) I# j1 c( H& N) I7 C. N5 f" S' I
</ul>2 N+ J7 R! `0 ?) ^; m! l$ n/ N
<hr>  p0 h# M9 {4 L! z2 a
<p>写数据原理:</p>
! @* t# l, N9 a<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
2 }4 g1 n7 a  d6 P4 Q5 D<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
8 P" ^# V/ e! _" P; C<ul>
+ q0 w8 d; I$ A/ F: \<li>写给leader,leader再通知其他节点 </li>
" ?% l6 E5 ~6 Q( ~& S8 O<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>) `7 T0 K3 X3 ]) q6 e
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>3 W0 ]. g0 Y$ t  u
( J4 W9 ^) U! i1 @/ V& y. K8 Z

& R( I) m( A# y/ ]</ul># I( I; Z- ^& p2 Z3 L( {: v
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>8 i8 x) k, A7 x2 I$ {' f6 C
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>, i7 a3 y  T6 a: C' W0 s: `
<hr>6 w! S& e$ e6 f* a  M; S! Z# u
<p>&nbsp;ZK怎么保证数据一致性?</p>
1 l0 \& s1 ^/ F+ I7 ?* P+ 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>
: Z2 J( j0 ~  J4 P<p>&nbsp;一般我们正常是消息广播:</p>, y0 y* ?0 {( G' _5 i
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
) a' Z/ v. b  C, d<ul>; a- H# i" {' d" V
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
" X. D! y( |+ T$ \+ h<ul>
* I2 A; _; ~6 E8 E1 z( V( p% @  m<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>, `7 N7 Y7 h. a, s  t
<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>5 n% W2 F4 n' b+ ~- u; f
. _" E8 m/ o" k1 r% ^* \! I" f" n# }
5 o8 s$ x: c1 H7 K0 t: t! e; Q
# i! ?0 b% V* b8 ]1 f
/ f" F0 a8 H( y! ?; W

3 Y/ m+ d2 v1 k
  [. M4 ]8 |1 {( c; w/ G' ?: @</ul>1 U4 Z' G2 v8 f2 V' |& G

( Q; {' m7 H: K) e5 t: t' l( K3 E! R3 p
2 _$ y+ E" q+ h( o- x

; e, w( H% e  X: Q/ h# z9 }* H2 j2 J0 `, j) d0 v0 @  \5 |* Y- j
( L. n! M; N/ `1 {
</li>+ y2 Q: Y4 T/ ~. ^
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的37 M1 s' {: T2 w3 m
<ul>
' ^2 M. a  ^9 i<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>
5 [) A! c2 C2 d; i
! O' }% P7 ]: b. g6 L
4 g0 e; X  h. Q7 r/ ?  ?' o: H; \. i( E6 G

) E4 ]! Y: m8 O8 H; ]: R
1 [# D( [" l2 o. s  b) V; D  o: q  B3 N' ?1 u" v& v
</ul>. _2 ^" [+ B$ R; n

9 W2 X2 f* g2 e. G7 V
. [8 e9 Y* \5 Z* A  t" @  C! Z$ {& d& d1 K' s  T5 ~( |% L

3 U+ I6 [! P7 }# D6 Q; L, W: B/ Z, S7 J; h# j7 H& ^

  j# O; K3 H0 L</li>4 i* O9 T9 B  P+ x$ ]% f. I" V& H
* ^) N6 l+ f# i# [# n

6 d3 L/ w6 t0 c0 }
) F7 M% I7 F6 K8 L: S% L6 t- ^& o8 Q, I

  @+ h$ c# r& z3 q! |4 c! }
4 U+ s3 _3 r8 v: a5 @</ul>" Y2 c+ ?- p3 @! ]' N  N( m+ z5 \
<hr>
7 x6 @; h1 r, ^<p>Leader宕机了如何进行选举?</p>) X3 R$ z1 H& n  |
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
% W' V( h* g, Y$ n<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>. N: E: E( l6 g+ S  q" q
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
( B/ p% |6 L$ X5 E  U& N" v<hr>! ]* {; r+ ?0 U- A9 ^
<p>选举后数据如何同步?</p>
5 N, k- f+ t: j: d6 \<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>2 _3 c. W) y( N- i+ o
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>$ o" [, m! Q! y* l7 i( Q! H
<ul>  p, U, z& L) o) O/ D/ L
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>0 l: a7 W& D7 M% z( W" |8 I# q' W7 G
<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>9 K. c" m0 G; v) d6 [$ n" }
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>2 w  N  X1 r& s' C
# {( P5 u; u8 q( g% ]9 C

0 h: ~. m) K6 L$ O; p) X3 C
; s  p6 C' y* c, ~* N
) H% Z0 f' V5 o; n) Y' `1 c/ G" s* |4 ^! @' t0 B6 E3 z* \7 m
# V2 m- y; i, @) Z. ?
</ul>! S5 s7 l8 k' Y2 c, C7 c, v3 E
<p>同步策略:</p>
9 @/ s- |2 x3 x2 _3 k, U<ul>. {9 E! r( n9 _! Q7 M
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
1 Z  u$ l: G4 D( @<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 \0 g: E" s2 l
<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>- I) I, o5 {! v) m3 f1 Z/ e/ |: w$ ?
<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>" i1 s5 H- _4 s8 A* ]7 ?7 R
8 L* u- H3 b9 u7 L- ^8 ~+ `

9 [4 G/ K4 [3 o% t" w3 M7 l, |% Z) H9 b& ?  f$ s/ C
/ b4 \- W5 G1 z& D2 `
</ol></li>3 F5 ]3 t2 b' E9 S; `
<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>
1 r9 w5 Y/ c0 z" V4 v- b* s<ul>6 z$ u1 ^* i4 H% E$ ?
<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>. |, k* p. A7 u# N8 _  |0 ]) o
<li style="text-align: justify">! n! X5 {1 V. W; [2 o( m# X- C4 D: B
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>5 v3 D, r7 K: T$ p; s6 J% q- ~
/ H- k5 l- J4 O

# i/ r2 x3 J$ g# w) Y" g$ ~4 Z1 r
! R0 O2 }# _" a
</li>
2 @# a; O) }4 G5 r7 g3 J7 s2 ]( p: u! V3 n% c( {, @  g" f+ w. @
  f( s3 ]( b- q9 {! @5 s- |
( z; N  V) T/ P% \" `2 J: E

4 z, L4 n0 E" I3 w9 e</ul>
1 o* D! w7 w9 w. _: h4 u8 a8 p5 x1 r4 ^' U
6 v; ]) S  `  S- {$ n. A1 j

# L& L6 T: H* L# q8 F& A, D/ h; M
& V1 v% l3 e+ ?4 W, I  @1 _2 x</li>
$ J4 V( t/ s; T. B# d$ B0 h<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):, @" D; Z* v- L( \  ~
<ul>
( o$ f7 [- H" g<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
2 l( q' ]" l! |& F<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>
9 ]' W4 G! S! {$ K* Q: Z<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
! j% r0 \( u& _6 I! C$ b% f6 b
" t) {& D5 [+ b  ^# n3 Y5 ]+ Z0 U' x2 ?$ Q( z1 O( f

4 M- A2 z& o, _  G. ^8 I8 i$ Z
2 _6 q& o7 L* x8 u</ul>
1 X1 ?/ B, d! \6 o$ i5 T8 A/ P* \2 Q; f  g/ D8 i
6 m! k& q! ~0 B5 n4 W. a; j8 E

0 s: C5 ^* V7 u6 U9 Z) H, f: E! L, ]4 x& V
</li>
$ I& Z7 K9 q1 n& B; `<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):- H! n: C) [$ o+ ~- `
<ul>% T1 a/ V" R/ G5 [; X
<li>
# |  r1 d* n$ g<p data-tool="mdnice编辑器">适用于两个场景:</p>/ @5 S8 g* S# v
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">" A" H# A* h7 x7 e4 {0 T
<li>PeerLastZxid小于minCommittedLog</li>6 \" u( U8 e5 O+ k/ G
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>+ e4 ]1 c% x$ W. B; D
0 {( q$ R0 c0 R7 s9 \4 B) n; E
" k3 S8 x! g( u; S- o2 Y

4 @4 [5 I9 s8 ~% V$ g
: p* M5 {  ]. ?8 Y</ol></li>
& v; p* y. u: o' T0 v<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
  L* W3 G( l# \) `6 A" S* N- `- ?$ _! v  }

6 }: M: ]: t3 v5 l
( A# @: p9 ?# N) s! g  k8 T+ ?) x! B7 e  A& B
</ul>8 N2 W! ~, ?5 O/ ~! {

1 H) h# |" Z. w- w; F" F- ]) T1 `1 b, ~. Y# {
1 P1 I% g# b- ], f8 V! V" \: R

7 i  j  B! m7 o8 l4 y</li>
9 N- x# q# e8 f3 E6 b+ W: c- }3 s

- Y0 N( m7 A* A  Z
* k& ?! x. b" L/ n  v
2 Y% y9 U; o5 I1 ^! y</ul>
. B' q  Q  K* U, J: T' u5 I<hr>
( C, m4 @) _( I<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
) d) W( r# i7 _) @<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
" G% s( w2 J* h; M1 b  Q, \<ul>
( J. C0 e& ~0 X! I2 I<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>! V1 Q( i: v; T3 \2 ?% p
<ul>* g. r6 M. w2 r5 W3 Z
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
7 Z- p. N" ~8 u, q0 |, M<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
/ o  U$ }* o8 G$ @+ B
8 m% u, `; v! A1 Z. _; s! f$ Q: G( B% G/ `& ]3 }+ F  E
</ul>
- _3 N' l/ Y: K
" s) W8 ^5 G( |9 V$ I. K
, i) P  o% B, g- F</li>
& [8 P' m* l: D# C8 s" D% N<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
1 y% y: A& |+ S) T. H<ul>
& `2 M$ Y- Y* e1 L( y* z8 ^4 o) s7 p<li data-tool="mdnice编辑器">
1 Q. s$ q0 O! i4 x. h3 T8 _<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>$ n0 L6 C- y7 j+ U! X

  G" \# b$ ^0 K5 s) s- w4 D! ~/ z
' U" ~* y- b7 h/ c! [</li>( ^% u# v3 w: x: q% m
<li data-tool="mdnice编辑器">
* |0 s7 t2 l! @2 \<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>/ P! c: z, R/ z, a

: [# ]4 y( O1 z  P/ Z9 k; {, S- M2 Z
</li>6 g4 Q" V. H. T1 c, R; X) v0 R
! Q8 k2 N4 h% Q" k8 \& y

% G6 P9 `" B; E6 J1 t7 ~</ul>
* T! r  ^) E8 ?9 R' n4 D
- Q' X) X* `0 Y, V6 F  S& h
( E1 C, n* x; n+ |: K$ t' U</li>6 o+ Q1 ], a0 K/ g" s
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>$ R: A& B7 Y; Q7 u& _" E; O
<ul>6 o' I1 V9 t/ m
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>- c- Y" z' B9 M, b* k' o6 H
# Z# M* ~7 u3 p, g
, H/ y: R" f8 |  ]. ?) n4 {2 B
</ul>, |+ g/ V' F/ o* ~- u2 m, j2 u  F
7 Y: f/ Y8 p" P' q/ {, h
" X5 G, A6 M8 S, Y+ k) s
</li>+ k; m7 {, ]) P! E% P
, _$ R% j+ T/ r: w  N9 L& E+ [

3 X5 G( k5 f6 l) v</ul>$ V/ \# B( t  f& J0 m6 ]7 S4 B: w
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>" I3 o4 B/ z4 D( K3 u' k
<p>zookeeper 是如何保证事务的顺序一致性的?</p>9 s3 w. ~+ t& Z: |% j+ Y$ u
<ul>) U2 M& H: a+ ]/ s' _9 f" j% k( I
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
4 M6 A' v+ N6 S; {
/ y7 D- T1 [% B& [
& V# D- h( [* r</ul>! _" w( [! F& S6 G% y7 C, u
<hr>
# e: m8 H- L# o2 M: x- Z3 |<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
# |8 v: L0 ^5 s+ \: Y0 H& H) y7 R5 C<ul>" k7 c9 |9 ?0 i5 ^; N4 F2 l) J
<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
0 w# w2 e/ t) c" W
4 {8 w  p) a! ]& Q: P( }; S( j
$ C  B9 L- }0 o' n# P3 @$ d</ul>
* `5 j- O# u0 U/ O& E% \<hr>/ o( R" O2 ^! P
<p>说几个 zookeeper 常用的命令:</p>
2 m9 s* z, m" t, {, K2 n<ul>
& r# s' z& N6 k% r0 q( k<li>ls path:查看当前 znode 的子节点</li>
+ J  \" ]6 c* C2 Y# V% s: {* M<li>get path:获取节点的值</li>
9 l  |& w8 D+ l- c9 k1 \<li>set:设置节点的值</li>1 ~3 Q1 o- I4 i! y
<li> create,delete:创建/删除节点</li>; c5 b. j1 J( q4 ]' Y
- ?- I, [0 b3 u1 N/ Z9 X

" ]  ]0 {3 R! E! k. v: |5 E</ul>- `7 [/ g! f/ _  M( Z) ^; G
<hr># G$ I) h3 P5 S. i7 T6 o  `
<p>会话Session:</p>
- {+ C5 d" P" E<ul>
% X, R. c* |( l1 u% ^<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
# R5 Q" i$ p" O7 n9 z0 K0 E, y4 @) E, }
& B; G2 a' R$ Z2 L3 {* w3 r0 v" I# O/ {
</ul>
3 f) t6 f! [. N7 z3 Y1 H<p>&nbsp;</p>
: a! k+ A4 R# d) _- N' E<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
3 ]  c( P/ _+ n7 G' t
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-6-29 04:56 , Processed in 0.063177 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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