飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

8590

主题

8678

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

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

* a" C' |0 j9 P$ l5 k<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>8 W- F5 D/ J9 Z" B3 V
<p>&nbsp;ZooKeeper 是什么?</p>
, S, a" t( P% J9 q5 b<ul>
, e$ G8 W1 [5 T& e7 L; V<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>
. A/ A8 _& l9 q; V0 ^9 ^<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 \' v) d9 k/ V/ H$ Z8 {, G9 b( r<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>0 Q' t6 _+ S8 e. W
</ul>
, I* M, H! Q5 C& t<p>Zookeeper的作用(应用场景)?</p>) B0 S, @8 J+ l
<ul>
5 n4 |0 F2 R! P. q; i; E- C<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>$ M: w' D/ p* J; w! d: n- a* A
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
  Z! D4 c  }) w* D& d4 T<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li># @8 i$ C' F4 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>就可以通过ZK调用。
* Z. Q1 c# d# `  D<ul>
8 T2 e; ]( T5 ?4 N<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>4 d. |( X7 `2 l  J7 R
<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
) u9 l' X+ ?& q( m* R3 I<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>
5 Z: \. Y4 o# W+ S, L0 I4 e</ul>
8 t. ~7 p. p( [2 o* W  i* Y</li>
8 e5 H  z; T) S- W<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>3 A, t7 d9 o" w% I5 j
</ul>( m5 H+ A2 g) T& F3 }+ [
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>  L: M/ i- U7 q7 B# e
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
( t2 x# H$ |4 ], d, g/ v/ u<hr>
" {* b! z+ X/ I<p>&nbsp;文件系统:</p>
% w% g# Y- W8 w6 `<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>
1 w/ w* E: F4 [2 M- m9 M7 x<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>) v3 }0 w* s1 p( x0 J8 J$ X! I- p
<p>&nbsp;Znode节点主要有4中类型:</p>
$ O$ h) J1 y1 A) {! `<ul>
1 ?7 l/ o$ k* i<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>2 N) H1 @1 c. p
<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
. q8 |2 B, F9 Y+ I<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>) }/ \7 E' X3 f; ~
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
; s: d" w8 E  b% C8 m</ul>; ?3 |: y2 o0 T2 C6 Z8 A
<hr>* S# L6 m5 e/ R1 r& G
<p>&nbsp;通知机制 (监听机制)</p>  g- `* `1 D5 i/ m. _+ |
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>; |# u; }4 ?( C2 j
<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 D& y9 u5 I* ?9 [
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
# a: G# C4 v- _/ L$ h<ol>8 c6 K/ A- a( c5 u  L$ F/ C4 `) u
<li>客户端向服务端注册Wather监听</li>
+ d2 }) M$ V, n, ~) Y8 U3 D<li>保存Wather对象到客户端本地的WatherManager中</li>
/ S$ z4 J3 b1 w: }2 c( l<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>
. M' |2 r% Z0 N; b6 P; n</ol>
/ w: g: Y; G  X4 K+ @<p>&nbsp;主要监听2方面内容:</p>; \2 x% \3 K' K: _% p
<ul class="list-paddingleft-2">
2 [, \& Z) f. [" ^+ p0 c8 V<li>
# c' {& \7 p" T5 c. z<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
: Y0 |* j! P/ j& q# l" M$ p" p</li>5 ?- L) z' e. Q5 @7 S
<li>8 u1 d: Q; p' `9 `) T& H6 u
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
3 @; R, ^9 f7 h/ r; t+ j</li>8 z5 l7 C! y  H6 @8 n4 n4 ^
</ul>
5 Z" q: z/ @+ o, Q0 r) _0 x<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>2 m9 i+ q' ?7 ~
<ul>
' W: Q5 P* Z$ N" `' g<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
! t- L% {0 f( S  Y<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>( X' |- X( w- T- H! D2 ~+ M8 P
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
: N7 f0 A. B  R& f8 K# F</ul>3 Y4 Z! v. o  E; L1 n
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>- W' ^, y1 ^9 V: d! \3 v
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>
- E' G( W% N0 l- S$ e; o* h<ul>
! _+ Q3 }% e* G4 j" T<li>Leader:负责写数据。(写数据都有事务)</li>" ]  x' i/ h3 s/ g4 v
<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>) o; c: Y6 k4 G! v
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>8 O6 I' X- P" x5 Q( I1 S
4 A4 F' L& H0 H& T( e7 v
</ul>6 {9 y, K7 A, B/ T7 b' i
<hr>
8 {, v5 H" z; y' G- D" x5 r& R<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
" f8 [( a+ C/ u6 g0 v4 N$ r( w. n<ul>8 @$ L' t. K, z1 d6 M
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
% K# _3 R; i; x0 K2 r  |; P<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>
. h* ]3 k* x2 N; g<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>3 I, s* J) L: ~8 U1 k& A8 T6 \
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>4 P( w( `5 l/ \+ M

. i2 Q; e" f2 ?% T</ul>( `0 s* Q+ N% p8 m$ y# ^
<hr>
* p- I" b" ~2 V# E' Z: N0 u<p>其他概念:</p>
) Y8 a6 T/ f( _: K( H% B<ul>
% X/ d4 ^' d. T  y  w( y2 w- R<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
& Y) v7 }% S+ n, I( ?3 E<ul>% D$ h( ^* w' `& ^8 }( s
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>: ~8 {9 _; n- W. L. g# ~& o
<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>" ?/ w- F- x; |" n# Z

7 u: H8 \& V2 U5 F7 i* a3 Y0 ?0 j
& j% I" Z& M3 d. `/ {7 _</ul>
2 u+ ^8 q( K" T
! b; H3 C! E" w& c
. C7 [7 `7 k% K- \- i</li>% I$ A0 @! R$ @0 W
+ F4 N7 V) a/ E% m; [  k- Z1 G

2 _7 X8 g+ A! v' }</ul>
: l  D0 k% Y6 k- z<hr>
# X" ~. M* i" x; ], _<p>写数据原理:</p>- b4 D7 R3 R) U% i, c- Z
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
; _3 E1 O+ H' {9 m) s<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>$ e" V0 [: A6 k: j  E) ^. ~
<ul>; x6 L/ k; F" W! l& Y
<li>写给leader,leader再通知其他节点 </li>
! c/ r0 q. [" {<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>
) W* _2 [( |# E3 U  `<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>6 V  h' k3 b7 m( L& ]+ F: P- r( a" \8 [

3 G7 s8 E9 A8 x. B5 }& U4 A' I* P0 N5 w& K! ?" Z0 R
</ul>4 D- F# w. b. [& {3 o' D. k
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
% R3 }/ V9 P1 r9 j<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
7 y$ i8 x6 M+ c" Q/ U0 Q6 e5 ~; ~<hr>* o2 b; V9 o- u$ i2 w& R, `
<p>&nbsp;ZK怎么保证数据一致性?</p>0 ~- B* M; R) s$ \! i$ i/ x2 ~
<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>
- A+ _) a3 f3 Q; i<p>&nbsp;一般我们正常是消息广播:</p>
7 G. u+ r$ e+ B3 H<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>, H0 M9 O) K4 [, }/ J
<ul>0 }6 e& v4 V8 d  \
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,26 L2 |+ E0 P7 H5 z3 p: Y
<ul>% A: R$ @% O* U6 U1 [( I
<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
6 H0 E, |2 P3 ?* r3 b( g% v2 P<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>7 q5 s- O1 h6 ]4 o

3 D- Q' }" R+ J5 v! i8 p6 u2 y6 Z) x
, U6 V' @' r; m# f( o# d+ Y

' Q4 ]* i$ g$ v4 `  K) n/ d/ a; m0 P! H+ Q9 {

7 k! Y! A! x. J/ e. Y( |</ul>
# K4 E6 g9 v9 t% l0 s- \! l+ D: S0 w8 n# X& S: C
) r6 L8 `9 \4 n  U& g5 y

& b, Q8 Z; q: I/ E/ a3 z7 e. f5 y4 v/ b/ }0 T
1 w& c% |4 x; o1 c# i

3 N' W$ M: w/ D- N0 O</li>. z$ u8 F' y5 P
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的32 W8 q8 J* a, v0 u3 c" [
<ul>
0 x% ~) w9 c* M8 l" e5 |3 R<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>/ w( H# }1 }( c* m: a7 n- \( t# X
) [, a% p' w$ H6 }" q9 F

3 z- B% ]& L0 U6 M3 K
" n9 [: s: n! r8 v0 C" l: [# L7 q: \6 y/ |9 Y3 w7 m; t! o

$ [' X6 ~+ R0 o: B* |, Z  W" w8 q! Z" z  I/ f/ b4 ~
</ul>4 _& U/ L3 z- J

; \$ Q! R% M  h( N2 H8 X1 W
9 r& _! ?# B$ K+ n6 b/ ?. Q6 W5 c/ ^9 [' s4 ]. o
% K2 w. X0 L' @; Q: i# }, J
. s0 ?& k! R9 [

# t! z2 X4 {/ I4 b</li>
/ Q3 Q8 o& X$ }5 V) \- K/ _  x" `) f7 M( Y) S4 K7 `7 ^

1 U) J( A+ j6 M: Y# N: r, K" Y) `: {: [: Y4 m" H

; B7 O, w" [0 U' X  o1 D
  D$ R" g, r- E8 p" T: g/ |: F0 z  H
</ul>
; x1 U' B1 S5 z4 R" v# p' G<hr>
/ P# r" N  ^/ G/ `2 x% R! y<p>Leader宕机了如何进行选举?</p>
6 b9 o: b! j8 _5 v- E<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
4 ?: U) ^- w! D* x7 M) B<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>: c2 D* j0 M  h' I$ f
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
+ V% G9 U% ~" R8 g6 k<hr>
  `% @! U: [. r2 y, }<p>选举后数据如何同步?</p>
2 h9 U7 a: {+ }% |<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>
! ]  h& a9 H/ o: P' A+ X<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
8 S7 K. w! f& U# Q$ T4 s; A<ul>- c- ^8 |3 \- X$ |  P2 `% y# |& s
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
, ?; l& ~; G) p' W5 ?<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
! v0 B! B8 M& N5 o<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>) E/ x# q; o, J" [4 l- C4 b: w

4 e8 K' Z1 p9 v0 f6 A: J8 \- K" A
" x7 `  d; ^$ _" _+ n% v
% _6 B" C% r6 Q/ p" J& ]1 j( Q9 l
) ?) }3 ~6 L( ]0 f+ D! H+ l
- a- m2 r2 U0 w# t" J. X
</ul>6 U' x/ d6 j7 ~/ W
<p>同步策略:</p>0 G! c1 n/ v2 [  ]/ a# C. h  q
<ul>3 t" N8 w4 D$ {, R5 n
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
( M, n$ Z# l$ O+ z. `$ i# \( 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">首先Leader向Learner发送DIFF指令,代表开始差异化同步,然后把差异数据(从PeerLastZxid到maxCommittedLog之间的数据)提议proposal发送给Learner</li>5 _8 L" n# ?+ b3 H
<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>1 `! O' S7 Z$ c1 h0 w* G
<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>; H) a. K2 q$ Z8 M: W

( `& u7 n* O4 B2 A9 I! g4 \1 |. p3 l+ |; y- |8 g) t) d
1 `% ]. j& z% ]* Z$ Q3 f

* i& V. e$ S7 O9 `$ g</ol></li>
8 e2 `1 E9 q; b/ b6 d<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>
2 F/ x+ o, \( _4 @9 a! W+ @<ul>- Y$ g' J  H' g4 f" I' A# Y
<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>2 @+ Y" w$ d1 P2 L0 W& @# O
<li style="text-align: justify">$ |/ M3 D! S! I/ ?1 r
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
3 \; J2 i1 ~# p) s( X
+ x3 I( ?# |& B& u
& X0 c+ w5 Q# v
$ B% k# n+ H1 ^7 A! O$ F0 C
! L: `, l; l" q8 p, b7 d/ M5 h6 g</li>: k  V% h' V, I

" U" Y1 x1 {' V- e* Z6 P6 I1 w5 [
7 O* o+ v* a- C% U
; ]# [+ V: `  u6 ?+ o* i2 V- l
: ^$ r" ?. i# \6 |</ul>
6 R. i; g0 l  p4 B* K' p! S
$ V7 X% p5 o+ d+ R" Z7 X
4 p0 U  @0 {* u6 g2 z$ R: K& k# @
; ]! i% b) ~& P; N8 y$ T% B* z% w* x: |0 x. e/ M: K0 @& x
</li>
- \$ l0 T& B4 C- k/ ]<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
! ?5 I; `/ m8 @; q3 O; K0 i<ul>
' \% W3 R& ~0 k1 Z% Q<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>  A' t  L# a4 {! M8 ]
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>1 p' b! ^  C. z! r+ g
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
8 p5 y/ m+ d8 j
* A1 {) {' z5 h* o
6 t. B5 V& Q! n3 T5 `
! H* A, g0 h$ V9 _+ `. O7 n! s: U7 c* K, c2 n$ }# R6 E
</ul>
; y  ~! f- f% |; Z3 I$ `
, F! o: l9 G0 O7 r) n4 c
  U+ B9 P* T, b6 J+ R1 ]+ Z6 {2 k' f2 _* ]7 M, H

% J7 g, O' l- v; o* i5 @, C, q</li>
/ e4 c8 ~+ j6 I7 w# u<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):
" D; Q: z: A9 s- i8 B0 C<ul>
* F) u) K# j- |' e; x  H* D<li>
7 U3 l( r4 g' w, {) j# o3 x<p data-tool="mdnice编辑器">适用于两个场景:</p>- a/ F* {/ {! |9 O! h
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
( E& U& ~( y5 {8 Q! Z7 h<li>PeerLastZxid小于minCommittedLog</li>
0 ^- {+ i# Z8 {6 L# [8 o<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>  l! F+ N8 \0 K# }
( e6 V+ x& x& b& r

' `+ }& c  ^0 l2 k( k0 m) R$ R% Q) w5 V' @/ T& F; V+ F

; u- w; \# h& u2 x5 r' C! J! g</ol></li>, M* W/ X+ u! Q
<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>6 H, S( I. k3 a# v( I( ~4 t) k% i
! h4 p+ v4 v' D3 L; G4 k; Y3 b
+ m% Q' K( ^- D, @; |; o2 S2 z

$ f) D, i, o9 H
. ?8 c+ S" B  h8 F- w: ]</ul>0 R6 l, R7 h; p

8 H* O6 D  _1 V  B  N% }
; C( Y4 X8 y) `4 _1 `1 h- X* s5 H( t4 W  I* m8 g% i5 x7 A# b" o

- a+ d. E# n2 e& b5 K8 R</li>0 c% H: N  D. b  P! ^

. P! d$ b. }2 [: b  [' g
0 K# w# `# Q" ^3 _
5 |/ _6 L4 Q( l, x
. V. t8 {- u2 x0 H# K' F</ul>8 ]6 `* Y% a9 o) S" o( Z
<hr>
3 E9 Z/ w, o' E, l3 J5 d% ~8 L<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>( V" e" ^* O* f+ t$ e1 G2 E
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
* h; m. A6 |) G; H5 M0 u1 G<ul>( T; w; _3 B: t7 U: p
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
5 u* M2 V" {' S<ul>- W1 j5 B' u2 _% P
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
8 D1 W4 |, N% n9 E0 N: i<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
" s- F- v' _* i0 {0 `. V# q4 |- y* E; n; v

: @1 T% d0 w" b' a+ _' g; a( b</ul>
: l' I' W$ }! Z) |
$ d$ i+ e2 w4 u$ |) v
7 E7 Y  B% i, T6 d</li>' a- ^% O2 Y2 C5 ?8 H
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
) @3 S& M+ N/ O4 R; g6 g9 b2 z<ul>+ k. b# x$ a8 o& J9 [( V' w
<li data-tool="mdnice编辑器">
9 z- P) [$ S4 A<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
3 A+ a% r. h3 V: \/ \. C2 m
) F' w4 [" Z2 o* N, k) G/ ^
, O! y. |4 ^0 Z2 L) d4 C7 Q</li>
" U% M  I& `" Z) t<li data-tool="mdnice编辑器">
) r& B$ t. b, X5 s<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>& _; ?4 e0 U9 L% O+ ~

5 N% K9 E" H  ~& o% {4 x* k5 D) s' P$ n5 p
</li>
4 K  M5 `9 v# h
! e" ^3 U/ `& A5 y8 N* r# l5 g# }+ K) w
</ul>
" m6 b2 s. r; ]# ~9 E: K% b( x& g* Q# |; g- L

4 l7 q  F; q7 ~* e</li>" B/ x% X( i( e2 {# G! r& C
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>3 u+ l. ]" E" c* M! w
<ul>
! A" c: g$ f# ?7 H$ g0 J<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>8 t: ]  @% b% t8 Y+ Q
2 k6 e/ ~+ `' v% Q
! d# ]7 }; P# P' o! l0 c( S7 S: y( _
</ul>
3 z3 b- `' ~+ B/ I" K/ o$ ]
2 J  |: W6 J+ m
* \8 s* m# j2 m</li>
& [7 f3 N" y2 S0 r) h7 O- E. E8 S2 P( g0 N; }) Z$ K- H0 ]
4 t% S/ t( p$ F" u4 K
</ul>
. S3 m4 W3 Q; g& p0 X$ ?, k<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
. B- |, h) _5 C  X<p>zookeeper 是如何保证事务的顺序一致性的?</p>9 L' a: V# U1 P1 t8 }* y0 A9 ?
<ul>$ F/ D" [7 g, R# M
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>1 a( w, S$ y. u0 s# i8 R

4 K9 t' [$ U9 c
* N% d2 n- ?' u& U% e</ul>. t8 ^* v6 s; @: e! T( W
<hr>3 n+ I- H" F. f! S) h
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>( u9 V! i( u1 G" ?* }7 i
<ul>$ g- L; n4 v, Z
<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>; e; }8 W: R; j5 g# f7 F& I: d

  D% ]- w& g0 P( E7 q7 s( m) _' g$ ?- N
</ul>
) x4 g) u4 b; I$ P' x6 N9 E& ]/ B  N<hr>; Z& j( z3 ~6 F  C/ x' Z8 O5 N, p" c
<p>说几个 zookeeper 常用的命令:</p>% ~$ R( Z& Q' X3 z6 }# x  N
<ul>5 ]) j* p8 Z. P: c  O
<li>ls path:查看当前 znode 的子节点</li>
1 U5 ]% ~3 {# c. B3 o1 m<li>get path:获取节点的值</li>
& o1 k9 z, E- ^( ^& g0 G3 \2 x<li>set:设置节点的值</li>( x0 E- b4 N* {: i% H$ A" K6 C& }- @
<li> create,delete:创建/删除节点</li>! x/ @- p8 f3 O0 b+ J

3 D1 q, E3 ^6 }1 K0 F  t/ V
" g0 K$ H4 Q: u. E</ul>8 ]6 v, V5 m4 \/ w8 y
<hr>
  `% `$ d6 a/ E- b<p>会话Session:</p>
/ Y5 h9 P# L4 F9 {6 C- J! h<ul>
/ b: c4 C3 R8 m/ c5 S; [<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
% o- h1 i/ U3 _( N# F+ a
: _  e9 W+ }( u2 l' S+ m' ~7 |- ?# D: k4 ~$ d- z% A; r
</ul>
( c  h) C* A& k) ~: @9 U<p>&nbsp;</p>8 J3 J: m# _5 E9 T$ P1 T* M
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>* [1 \/ |) j5 @6 a4 V' l
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-4-30 10:46 , Processed in 0.101446 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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