飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

8242

主题

8330

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

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

) m; u0 B9 l/ u) Q1 X) x$ @<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>
- ]: e, e4 n" H9 K& p) N<p>&nbsp;ZooKeeper 是什么?</p># G3 g/ o+ T. f9 b# G
<ul>/ r( ~: y" q: U* U/ ?& D
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>
2 f7 n5 d: _4 {$ J) o<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>* j4 `1 S/ |1 @* F! U  W& B1 ~
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>
& o/ c7 d- f( |</ul>: N( A+ ~/ a! ^$ L
<p>Zookeeper的作用(应用场景)?</p>
- J; v- M) j- s# \. K& a<ul>
. M0 `7 n. u8 U9 \<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
, z/ U; m1 N- p' F1 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>
0 o# x9 a1 S; M* t8 a<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>  S) K% w0 }4 \# `3 _/ F5 D. T
<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调用。
) n) C) `3 p0 d5 \  p: D% n/ X<ul>* {: Y/ w) |# B1 w+ d) m5 D6 R7 ~
<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>' s$ n; D: |8 S) o1 f. r
<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>8 W8 n& D3 K9 G3 g' n
<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 C2 Y* c" F4 X+ ~8 z2 K  y  K; o2 Z
</ul>- x8 W0 a# Y; g; U6 _
</li>
) k1 x: m* g1 O+ B; R  C* s1 z<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>/ b  B0 I& Z8 r- w
</ul>3 @& b% |$ W, T/ z% w
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>
$ B' U2 @- Q7 y+ I% |6 q  k9 H<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>  U, U8 V' b& Y. r/ c
<hr>( k' L$ m8 T$ T6 C, R0 y" Z
<p>&nbsp;文件系统:</p>* r5 ?/ L- h9 J% v+ ]* O; ~0 x
<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>, |9 J4 V, G9 \! i5 O- N
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
, u* ^/ g/ F+ M' m* a5 R" x<p>&nbsp;Znode节点主要有4中类型:</p>/ R& H- g2 V3 ?: _1 L" z5 E% K! j
<ul>/ _" t. W8 S3 Y# c6 E9 v' R
<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
( L( N% d! \; C" r  z+ @: ?<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>' c+ F, A- ~3 D: E& {  |
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
/ z/ X5 p( ?" _3 U' L$ U<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>+ z* F, i1 a! e2 O0 d3 ~
</ul>
0 @2 S$ d0 Y. r( T  T5 e0 F8 G<hr>
: J: ?6 Y& a/ }  W. j- w9 M+ O<p>&nbsp;通知机制 (监听机制)</p>- q" ^. y" B& G; }! l
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
; n* n# n! ]! c- }<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>
. x5 u" y( u, X) V. E1 ~<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
. `! @; v/ w; U. }3 a1 u0 z6 f<ol>
1 }& n; [: t0 s1 f<li>客户端向服务端注册Wather监听</li>
' X, e2 t2 ?0 E# l( i: W<li>保存Wather对象到客户端本地的WatherManager中</li>
( x' H( }+ I  l6 y<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>
' ~: Z# r1 v- K# D5 v+ b7 I</ol>
! x% C; ]" K. {% q+ k5 C- R% b<p>&nbsp;主要监听2方面内容:</p>
! v$ t/ f3 M. Q  b7 W2 e, X+ w/ b<ul class="list-paddingleft-2">
! f1 v. a$ Q# E- J1 B0 j: B<li>, L& R0 Y! w# z% \+ ]- Z+ z
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
; U6 Z; S: e6 C; E0 z8 x" ], W</li>
" ?4 t9 u" `( i' d; c9 L<li>* G7 f" q' H: {6 z, `
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>; t( F! W" O& m1 W. E* D7 s
</li>, u, k$ j6 e: @; {
</ul>5 A; {$ r/ u7 B" z7 o' l* L$ a
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p># f5 L: y. n4 D" q- A: v
<ul>5 Z' H4 p/ J9 D/ U. f9 E
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>( p4 ]1 t  l8 B6 Z: w. o  M0 D
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>& B2 P7 t, R& i* k: ~- C
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
5 K8 r, |$ f3 }5 U. ?: B( Y</ul>
4 G1 m! b5 o9 z<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
3 P7 [1 l- A/ J<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>
# |: b" p3 E7 {' h<ul>
0 I9 r# u3 m/ L# a# O1 k<li>Leader:负责写数据。(写数据都有事务)</li>( d0 R1 L+ C4 R; J# Y- V3 Y' A9 y
<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>
7 _3 M2 i' w  G8 k0 n0 j% m3 i2 w<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>
4 q" {; F  E$ T' o3 H0 n1 y1 u5 ?$ {7 N9 C2 I
</ul>8 G+ ]" G$ p& T( X, m  w$ \# ]
<hr>
) U! [& B2 }8 B<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
8 h; d  H+ ?4 _7 |$ D# x( Z<ul>
& s/ r9 Q- x1 l# m# f. }9 W) ^<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>- P- e& S1 y6 r+ e* m- [
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>2 }3 }6 p" H# |+ X4 |& A
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
: X6 c9 f/ q- g+ D0 ?. O<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
7 r4 ~8 Y% o2 q& ^3 A& n0 x& b+ M0 ]- r1 i9 E4 ~
</ul>
) M: l: D  d3 Z/ L( H- g0 ^1 j5 G1 j<hr>/ ?2 J" y" x% `) @
<p>其他概念:</p>2 j' m* q, u! L; D: }6 f
<ul>( k: L* L4 x- P( `* d% E
<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
8 m" H7 b  F2 h1 P<ul>
" {1 M& c, P2 P1 j6 {<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
+ o& H/ L$ ?6 B( K9 T7 T<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>8 c: S" |- g7 \% a# r+ W1 V

8 B$ [8 j- N0 F& t2 q$ C- @( x# \* b9 Y$ W( Q* I0 f! Z: `
</ul>
! b3 u) l& y/ G+ a0 Q3 K0 L7 W8 }: E9 g8 ~! _

. z$ r( H+ [6 ~6 |: E/ L! L2 x</li>" S) t' P% v$ N, o0 B5 v9 K% V

: E( R5 Q' y6 R6 `# L5 Z. B# X! q6 @" s6 f- x) u
</ul>" g# Q. A/ Z1 c
<hr>- Q% b1 G2 Q1 B7 @  W& Y
<p>写数据原理:</p>% _  b) {+ h. G7 V" Y& R5 z5 v+ T
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>, U7 @$ j$ [7 W! e2 F- F) t
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
4 y( S% Y3 B4 m& ?" N7 i6 _" U<ul>: \) f  j& V" w$ D
<li>写给leader,leader再通知其他节点 </li>
8 |" }' @* \+ I/ J4 l$ Z<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>$ r4 r8 p2 ]4 B, P% G6 Z( D
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>
( J  G3 E5 Y1 ?6 N7 J% R* N+ ]; [3 |# i2 p' g$ _
' w3 Z9 b. S  R# I) M8 z
</ul>
5 D; D" r; {9 t; v3 j; ~% ~+ t<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
, F1 f! r. |& [2 J3 {2 c7 }7 |<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
+ y# x/ G" y8 y5 ~<hr>0 J% a" O1 |( e! N2 _  `
<p>&nbsp;ZK怎么保证数据一致性?</p>
: {1 O1 F$ R; b<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>
$ t5 s& y. l. f4 K1 I+ X<p>&nbsp;一般我们正常是消息广播:</p>& z2 ]+ f2 x, J( S5 D6 p3 o
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>  b6 }3 i% m8 p' c2 i  C: U; d% A3 W
<ul>
# x+ m- b  h; j4 f, @* G<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2. i1 o# X& K4 ?: K8 `( |3 s
<ul>
$ `$ C0 M8 H1 e; B- W  E( Z2 `* L<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
: L/ i, D( f/ ~  S0 f& c) z<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>
+ b# q) h. B" D: X  i( l7 C4 j6 c
: x+ W' K) A8 {

; Y5 [# b, ~# `' t1 V9 L; ~+ B# ~, l+ h! v: m2 L  }+ \* O

/ l& x+ Z/ W) x# C2 K
/ r8 B9 Q  y  `6 }$ A</ul>
: b$ c7 A; P; O+ l: K
; f7 m" o: C5 p( `6 ?3 O, g2 Z/ ~  z; X( Q0 x+ T9 W; n

4 |. M: M( A9 s8 @* l7 V) U' r* R, q  B' v
6 x1 E/ R0 Y( `) ]. G
" ~. b1 A5 z$ s0 y7 e0 C: d$ x
</li>8 N$ @0 d9 B( y! U; W; Z, I( j4 `4 V
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的32 R0 S' V2 @( p1 s- w
<ul>
7 E/ R3 y- ~/ Z6 ]<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>+ I0 ?. j; _' q' q. P

. F! x/ Z& L$ V9 _. v% I( I5 ^, Z3 e1 v3 M+ A; {
+ R! E  w. k+ z( \! I; D# ~! P
+ D3 a: ^, n: t1 I
3 Q* T9 }9 O& [  e! U
5 f/ O$ n$ n# {- T% Y, {4 g
</ul>
( I5 \6 I9 i2 w  ]# P& J
! S0 |; M' r) H- ], {
$ z8 v) k) ^5 O" r& \, G# O6 Y$ ?$ f- M$ A+ S2 C: M: d3 V

. y7 Z# E8 W: E. c
/ Q* ]" U5 L) @3 W3 w3 d! `' f; P9 y
/ [/ w5 c# b7 g( a- r  {/ K' R</li>
8 `& a6 Z7 A& b4 g/ k4 u# i, t4 F$ u  d' F1 f3 {% O7 m

' s0 r4 H& V  l" s# l+ T* Z9 a4 ^* w4 Q$ p+ F
2 f' i3 j% q# F( B
# R7 e; _# j0 G0 y9 B

- w! R4 T$ h! n5 [, g3 p1 s" a</ul>
; ?0 z/ l) q. |4 ^' H<hr>, E) @- \& |" y$ M# a/ ?% [! d" Y9 k
<p>Leader宕机了如何进行选举?</p>
) \7 ]1 ]0 D$ l<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>" m. V6 y0 [: }2 q4 f
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>
) _: H+ y- V3 G+ w  M<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>3 p/ V7 x! a' h6 n! l
<hr>
5 Q. d1 k; Q7 Z& s8 f5 D<p>选举后数据如何同步?</p>8 R6 f" @! y' H, v$ Z
<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>
! X& o. Z7 z. I<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>: n4 q/ [% P/ i, U# B+ Y! W9 m
<ul>
, ?; i$ H# ]3 ~: p) A0 {& ^<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>0 h* f+ x* `7 l: q  `+ D
<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>* L( A# G1 D1 @$ C: T- i7 F
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>
3 d6 Q$ J& ]4 u( Y. T) |1 h" f* q. I

8 z  ~4 o/ I8 W# _. ]
, b; W- S6 h! E: o8 M
% x1 g" c! f# b$ p- h0 P8 z0 [, e) D9 [4 {8 ~

. ^' P. `7 L# l9 k</ul>
6 {/ S& D* f! L. s<p>同步策略:</p>
" |- `0 L  ~, o. n<ul>; K- ~, p9 F1 u) H3 y! ?8 L
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
0 U0 R( G1 _" s3 W2 _# o3 @! 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">首先Leader向Learner发送DIFF指令,代表开始差异化同步,然后把差异数据(从PeerLastZxid到maxCommittedLog之间的数据)提议proposal发送给Learner</li>  h5 Q/ I2 M' z  d/ D  a  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">发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步</li>
& g( f# |9 ?7 \/ Z% K5 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">接着等待集群中过半的Learner响应了ACK之后,就发送一个UPTODATE命令,Learner返回ACK,同步流程结束</li>
# Z/ t  {" P9 t+ V9 ^4 p$ a% x( X
0 y/ X( Y/ `4 b% X
# g; L! r3 A' a  n9 h; J: u- b
$ l- F  G6 y8 R2 Y. p! R, a6 \* w/ v1 M* K* f8 _0 k- r
</ol></li>
* J8 \: b; B; [<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 F. [9 z/ z9 {4 d) ^, p
<ul>
" x+ R% W& _1 b7 M5 U<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>- V% m! s2 S% t+ |9 W1 K# g
<li style="text-align: justify">9 [  b) Y  K1 e, h) u4 Y
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
% \9 Q" ^; Y, u8 F% X( m
; r- I5 Z6 n2 q3 r" ^, _- @
0 Y+ e) S  O- @8 h
5 ^% B4 z4 V$ K
/ z5 J; r; [  v( B</li>
3 |7 S0 Q, L" o0 [" d: Q9 |& ?2 h: {
- \7 ^. j" h& Q* q
5 f. P6 i, S7 ?. C

5 H. {( s& k8 }: a+ S</ul>
* P) U# t9 e5 @" U
0 w& E* z& h+ f/ c3 Y' t9 a
5 X" k1 T+ S8 F2 E( f* v, Z6 G$ w

0 k$ c9 `1 b5 S  M+ c$ G</li>
& G" M. }% C% N+ K! s* v  K<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
( J, a" W$ S" o8 x" W  {: \' p<ul>
! ^, A0 F3 n: l" F. x% h0 Y<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>; G; e$ m0 k! C! N" ?
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>5 Q1 j) \. L$ k0 W$ B0 ]
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>: W8 a+ D' Q( |' Y% \( n* @; x
: {* o$ l+ f$ [( p0 `: P; I' |
" p% B1 B: W7 i& G9 B( R/ p

! b8 p: k4 k- n5 k1 y+ ?! u6 R" P" M) u% ]5 u, F% p
</ul>  n/ D6 u1 E0 T

) N" {' A. R; g& p. v& X, `
- C& \# q3 m" H2 y: U/ `% c; N  L3 A; f' U( |6 U" s

5 r5 r- z1 I' W" V( p: n% w4 l</li>
, r2 i. B0 T$ ^" N/ \<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):
+ t  f' C: V( R4 x. O4 h<ul>
0 O$ j5 \1 l% d. G. D<li>
6 O4 ~9 g4 i, l& @# _<p data-tool="mdnice编辑器">适用于两个场景:</p>
- V+ ^. X/ a1 V) R5 ?! Y* R" M<ol class="list-paddingleft-2" data-tool="mdnice编辑器">% L/ I1 _; U2 X1 y% v7 H  s
<li>PeerLastZxid小于minCommittedLog</li>1 @  E- W, f5 o2 x  Z! G0 n
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>5 Q9 Y5 `5 `  ~5 |$ l$ C8 |
) v- F! G/ c7 j% J+ ~8 q

% {. C& H! x7 x4 N" Y- N  \5 N% J* g% O
0 E* |* f" h, @6 f. [
</ol></li>
; i) O5 _! g+ c7 C% D<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>4 m: g8 @0 d2 O, D' g
* Y& I5 A4 x) `  K
- m# G& k+ p6 K% L: q- J$ }% e
" l3 E8 ]7 t$ l) H- I( Q4 o! u% u
2 l5 P6 U+ |) s( N- k! N
</ul>" j1 y* r1 D5 K2 a/ F# u5 r/ h
0 C; \3 j7 u6 J4 o

2 {3 {" Z% s& f% x% x
5 Q7 ~6 C4 W# Z8 N/ X  }4 ?; L4 B/ W" z9 c7 l
</li>
# Y9 l1 S% i6 Y5 M$ G3 X$ B. E3 [2 `# }, M! b* Q

" ]; c: I' t( a# d& N0 E. z/ D0 }0 m8 @, J& |' C& G* m

. V: V4 t9 o3 ~) u  w</ul>
) {1 P2 b1 P0 M5 z" A" O<hr>
! ^1 \, a' S7 J2 a8 A8 I<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
- |7 F6 A4 A  p" J<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
9 ]  O* u. K1 B5 H6 M' g<ul>
% _: f. _; z5 C. j<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
( _2 b1 t, Z5 H8 [<ul>+ r: v# _7 r1 \1 O2 ]$ M  a
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>5 K6 w3 I/ B  b/ ~: n7 \2 J4 `
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>% f+ \6 U" ?& {, W- `
1 _# R) D. C8 ]% f7 M: h

7 {4 m; `( M- B</ul>
) Y. {/ r4 L! j# I& B9 N8 {# k1 w
, K& p+ l7 D. Q) S( U; C; R& e+ o7 x$ u: L: |; ^+ P2 Z/ ?- r
</li>
3 G' O" e1 g, x  V<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
  |/ h. R% M* \1 W/ t4 D- u<ul>- ]! h! y9 c2 ^3 J; m" W' @# M# a# u
<li data-tool="mdnice编辑器">
- _+ W! P0 w# [/ _<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>  P( y2 E# f: b1 d
4 B" ]4 \# a- Y1 N, c" I" @

0 W$ d$ F) b4 b% j</li>1 C' t0 ~" u' O8 M% g8 |0 |
<li data-tool="mdnice编辑器">! E# r# |) i: a
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
+ n0 X) i! s: ]4 j( O
4 p" \2 D7 Z3 o; @! M% K. r& i) g8 W5 B/ R( E
</li>
# \6 N' w* \# [8 h. y* o& x3 o( i7 b7 b
0 T) R/ c+ T9 J9 e
</ul>, z. P$ O  \2 Z' x: D. G: `! s
9 p9 m: P) i# E- @
2 i. e; s/ k3 B+ i! A5 l" v
</li>
  b, g, V5 u/ i8 g0 h5 w% J0 O<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
1 E1 ^# _* t% G0 S. d" V<ul># D( S/ k( c  k8 [5 ]
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
' E& V# [# Q( ^7 j! t
6 E* S9 M1 j& f
6 `, [$ M8 a/ ^: n</ul>
- t! Q, h* j( z! d5 x1 {. N" p% S+ p) |4 M3 G/ Q+ Q0 A: Y; H
9 ]0 N; g$ Q' d8 c
</li>
+ n5 f! n1 K- W/ d3 B1 G9 l/ U$ @4 Z# U0 D9 K
1 H% I& A: B7 ~! E- \
</ul>9 u/ X7 K, K5 m6 u0 Q4 O
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>0 d* i( o# t6 a$ H  u, k2 P% a
<p>zookeeper 是如何保证事务的顺序一致性的?</p>. x9 d. ^; O$ B2 d5 n4 t. U* d& V
<ul>9 t' S% C. B0 c' z  N# s+ f* @
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
0 u* c% J, j7 q* A% R, t
) G: h7 U; M0 R( V% G0 N& V
- F7 P& A& r/ |; P: s8 K; [' ]  S</ul>
5 D9 `+ Y7 v* {9 s<hr>; U5 ]8 [: J# N4 C3 ~# U
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
# |0 N+ q- H6 C' ^( t6 d) G2 r/ n<ul>% b5 S$ |4 s8 b( w, @' `
<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>0 g. c- Y& `8 g- B, Q! L+ }4 t

/ @; S2 l  l' m& w- x# t( |: @* K* m, T
</ul>/ e& d0 ^2 a0 ^% o9 K3 P7 h
<hr>" M) |9 \/ K& J: p0 R: C8 }6 u5 A4 o
<p>说几个 zookeeper 常用的命令:</p>' u# D" ^3 t" Q0 V: y5 D
<ul>
& I. I2 ?6 n7 G/ c<li>ls path:查看当前 znode 的子节点</li>4 }/ `& {$ z4 [* {6 K7 d0 p
<li>get path:获取节点的值</li>8 I2 O+ U- S5 E& ?- y# `: v# V
<li>set:设置节点的值</li>6 J5 q( m+ D5 E4 O1 e1 |" [* B" a. T
<li> create,delete:创建/删除节点</li># B5 {+ ^9 I, e2 m4 ^0 j3 V. s
) d6 y- g. |4 k* w& i! L8 {# K
6 y& B; j- m# j. @; O) [2 |4 c
</ul>
$ h8 o- t! @$ Z- {- x8 t3 b- e<hr>
/ U7 I( S8 Q) j* B  d; T4 I<p>会话Session:</p>) M& P# m. x) X; G5 n
<ul>! g9 J$ o2 z# S+ b' h0 i7 }& Q
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
! t% t# p3 V. U& l4 j8 a( P, E9 _  _& j* s& B! N& J

6 W4 e' f* U/ v) V8 R</ul>
& G7 g4 D* v! x: r- e<p>&nbsp;</p>5 `, E7 w$ f' B5 t% x5 X
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
, t2 G+ S! Q, m  [$ _% W
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-2-27 07:22 , Processed in 0.070881 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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