飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

8560

主题

8648

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

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

" k- d  {, |) g<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>8 p. n& |) Z+ e# @! i
<p>&nbsp;ZooKeeper 是什么?</p>
$ I0 j0 B3 M. X( \: D7 N<ul>
+ r$ z$ Z3 @6 G' P1 Z% p<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>
" M4 t1 n) L: k1 J<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>
/ c# d6 ~1 ~# `3 b  |- o<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>! E( T- N+ x# b0 ]
</ul>
/ H, s8 g' F6 s- U7 v# V$ n<p>Zookeeper的作用(应用场景)?</p>
6 l: m3 r  h) q' L- p+ ^- B<ul>
3 v- E( o3 }1 ?6 V$ c0 e<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>+ _( \3 X/ [3 w( e( q
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>6 u, O8 t9 s" y8 n4 ~
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
: \- n% t! l* n0 K/ o+ `( b  ?<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调用。
1 w9 z" z6 x2 U8 s4 o2 V<ul>
' e0 R" U4 R% }: j* k5 M3 ^<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>0 V! c  l+ e' j3 j& \
<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
3 N! v: ^. g( @. G7 i3 Q: U9 w4 K<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>: c- g) `, g6 g0 y/ C
</ul>; R8 Q& Q; b: O$ d0 v4 e
</li>" |8 u5 B- t; Z  i; p
<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
, u; |6 A  f5 u, z) a8 f" t</ul>3 X, u& |+ F5 S$ V- J; p
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>9 ~8 ~$ h% r* }/ X/ W0 i7 X
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>8 P/ t, m6 m! h0 C3 N9 x2 V
<hr>
% o( H! b% E! h/ U<p>&nbsp;文件系统:</p>
: X, o/ 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>
; y& ?- R) Y9 G/ A- j<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
( \5 s' T3 c: m! u# J<p>&nbsp;Znode节点主要有4中类型:</p>
; @4 c6 f: e6 x& \/ R4 A<ul>% S: C$ G/ p" Z3 }
<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>/ ~8 l8 ~; I' B# ^
<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
5 B2 y+ S1 Q& e<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
2 u# ]! z6 W2 B+ x) L, O- }5 f& I<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>: p3 m7 F, Y. a/ K* \$ n9 s/ ]
</ul>8 f% ]- b* o! L5 u. u
<hr>* K; i8 F+ v8 A2 Q+ W! D4 N1 I
<p>&nbsp;通知机制 (监听机制)</p>
6 W1 P5 ]6 U: y/ L( o7 g<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
9 @3 L; v+ Z: ^) @6 t<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>% ?1 `7 D5 O+ h4 q
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
# v. k# {) C, A  a; |<ol>
4 b2 j* A1 ], C0 _9 Q4 P<li>客户端向服务端注册Wather监听</li>( F7 ^3 l, ~. t- b4 s- i: u
<li>保存Wather对象到客户端本地的WatherManager中</li>
, L+ i6 V, S$ }1 P2 R<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>) `8 u; v8 B% z: u: a
</ol>
; Q/ a/ ]2 Q. W3 s9 a<p>&nbsp;主要监听2方面内容:</p>6 ?! m6 z" S! W' ~- @1 ^
<ul class="list-paddingleft-2">* X, m$ v# @2 e5 R% S0 S) N2 y) B
<li># P7 w; A" E- m7 L7 y
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>  M, O: Y9 T8 c) r- |0 ^9 {, [
</li>
3 S! l' D1 Y! c% K. b<li>
% K1 p. r% s# x8 y% l- a8 l<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
$ H6 N, W- O. c; `</li>
5 w& Z2 S. ^- i8 ?4 o# B</ul>
4 j5 u' _: S0 O4 _0 G! `) }<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>
+ Q9 Q/ T4 K: H4 N7 E# Y<ul>2 X- y5 K% L/ r* S  Y. a
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
$ \( O/ ?- [7 y- e<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
6 u3 w" ]9 P- B5 J7 I<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
9 c" J5 h/ g5 y! d</ul>' c% X" m( \& `% _7 o7 ^$ t
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
- v# O5 [9 o: O<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>
- Q# E0 S) I  Q  S<ul>
4 |% ^# b* s+ W- w6 Q<li>Leader:负责写数据。(写数据都有事务)</li>
8 {  L# q: y# X. j7 `! 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' v4 A1 I( ~# h  |<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>
6 U% D" y0 L& e
! c& k: ~  d! K7 |/ H- s</ul>
. Z0 y) z2 @# o* D0 B$ W/ }; x/ c<hr>
2 v4 [3 }' K  U<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
% t: D' W- q4 I4 ^  a1 r<ul>
, m  y2 b( o9 X& a<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
- n' w$ m; m% o9 m+ T8 T$ X; u# |8 u<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>8 O1 x3 f; V' [# @) J! X' j
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
! `7 b2 r0 w) o7 \<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
' o. Y# G8 C% p: D
8 R( A* b4 X" b: l* o</ul>
0 M- ]+ b- w& H- U<hr>
1 r/ X0 R* B* {8 G  n<p>其他概念:</p>( }" T; }0 L1 c
<ul>
9 {9 ^. I0 W( K7 d9 N9 E5 X<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:1 ?( ~3 v2 p, r; P
<ul>% p0 y+ v8 K5 d
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>/ b& }6 j: J& X3 G* n+ s
<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
" l; @' n( n- H% [6 s/ y
! C& C( w, j2 S$ t7 X' W- i
/ }; y" Q8 b6 i. j- b$ P</ul>( P6 Q$ O! v' V. m
, X0 x+ k4 ^; Q& E1 @8 \* c
! Z+ t+ a; p% n% g
</li>
6 j: i: I7 r& C# c- S
- X: B3 I7 j& R6 q: Z; X
: n% B% y! i8 A</ul>
8 t. U3 D/ W3 Q: a9 N<hr>7 u% F2 l3 S/ c5 g$ ^
<p>写数据原理:</p>, B9 A& @8 W, d
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
/ d; `, o" Y+ b/ L  D; j<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>. r7 I6 I( I! o# K
<ul>
3 F8 Z+ L& k9 K& R9 |9 h3 J# R" u0 w<li>写给leader,leader再通知其他节点 </li>
5 R; b/ y: |2 c7 c7 N5 f& _<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>2 h& W" n1 z8 k6 h' C1 L
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>
2 C' `8 }& m# ?5 ?% m7 y% c$ q& |
& G- X- F  _* Z5 ?; g
</ul>
3 a3 s8 r- n. Q% r8 i<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
" b# v# H: T5 n4 u7 ]<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>. W. z) v, D' u/ N& y
<hr># b" ]+ Y% [& p, k- L+ t6 R
<p>&nbsp;ZK怎么保证数据一致性?</p>
& W; j7 l, x' i6 u( V! O<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>, O& L: H3 Z$ }: V9 g. N/ U+ x$ h
<p>&nbsp;一般我们正常是消息广播:</p>5 Q; O- _0 B, ?# Z+ a8 W$ \
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>& `' C& W$ X+ V4 L" ^9 S
<ul>7 w% R, M$ h" @* U( Q* p  G, c& ^
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2+ y, V1 I* p1 Z2 e! u& v# p; o
<ul>4 L9 g, y, w) G0 F
<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
  b5 ?$ r. `  g$ j- Q: o<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>( R4 P$ s/ F  j  V+ O7 X( Y
0 V3 v$ |$ {- ?) @/ n! F9 v( o, v
  ?/ L6 C; _7 |4 c. c
3 _  T' ]( v0 M6 a$ {) x# J
6 T6 u4 r& I6 j# j, A' B6 O
+ S' n! ?- q2 k5 X1 q

( ?) [+ v. G: B4 X</ul>. G5 k  }3 T$ r' [; _

. P/ }8 h4 A. \
: \' Z- Y0 O& j; e# b: E
( `0 c( ]0 H! L- ~
% S0 S! O% n, f% s4 b! f# \( u
* d7 A: X# p" e8 s. K- D9 N0 B  K6 L! X0 }, r3 J8 v+ o
</li>% }+ r7 M0 N: T2 N& r
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
5 Y- C6 B0 O% J: W0 g6 v<ul>( W" ]: a7 Q' M% I9 v# Q0 ^
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>9 ^" O; ?8 r! r0 A, {

' C. g% k* g1 T8 Q* X: e
: Z/ B9 F& n" _/ w! d) A, W% i! y3 K/ i( ~$ N

) D7 X3 Z' }! \' b" \) {, E- A2 L+ o) e5 K2 L. b$ v! _
- L6 m8 m: y- D: H$ v
</ul>8 l2 f8 J, J, v. P, {

. K) i7 t3 b: g& ]' p7 y% X5 x6 k3 O6 J
* G1 ~9 J. g4 i+ U  z0 Q: X

+ _1 q: c) [: {. U4 f; c  s
6 ?5 x& g0 t4 ^# A/ v( l/ n; G! B# e' |! G4 r5 W
</li>7 q! k: K4 |9 j

( {  P) b* v+ n& U
0 ]- E3 ]; s8 `$ e! R& R4 P- d3 c& n9 L7 I

5 t+ P" w4 B1 n6 E1 z/ P& ?, j3 d  ?- N. C8 m4 ^
' V! v! }. O2 i: K
</ul>2 ]% H- ~2 M( K3 |0 j
<hr>) O6 ^2 X; J( x9 G; U
<p>Leader宕机了如何进行选举?</p>
* w# Q# E- B& |6 @: Z<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
) p( E0 ~. N# e) P7 _<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>$ |8 @4 _" E) I# Z% g
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>5 a2 ]4 N1 _) u
<hr>& k+ w5 F9 X9 d: I
<p>选举后数据如何同步?</p>, e# a3 B) ]: `6 Y8 X
<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>5 u; s6 B4 ?7 @! W
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>( ^1 Y2 l5 H/ H( Y  |- m) f5 ]
<ul>
1 u; z( d; O, b: @5 V2 n<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
, T* w2 s: L' x" s) i7 j' L* n<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>; @4 U0 E! `8 A" E7 ~1 w
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>: E, N3 l. h  p: w" k! `: D  P
8 W9 g$ D3 ~, d9 g5 A7 |0 _
6 r( ]4 s* R5 S0 ^
" m  T+ e' r1 m, \" J8 U6 G
9 W' w) a3 x3 ?  j0 a

1 Q% m- Z0 O! g6 Y1 J: U" d
2 T/ b7 t" M! a# R1 W- r" k8 i</ul>3 z0 `' U7 I1 e' H
<p>同步策略:</p>/ T/ \" H) N! K9 x4 k
<ul>
; _  `, L' ]) d: ~4 V<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>4 s) E- `! ?9 _
<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>
) z1 }6 ]5 p9 p% x+ z. 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">发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步</li>
4 ?( L* `5 W8 k6 u' z& e! h. g5 I<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># }& C8 V- F5 h( e
  Z, ^. T; H6 |: y

8 X+ T8 H, j& q  r
0 t9 H) d7 ^0 A( S% n, P2 g6 }' Q/ U' p1 N# h: {$ {
</ol></li>5 v7 T0 o$ X& ?3 ~0 g/ i1 k  x
<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>
+ n( z: E: a% w3 f# H% J- M' Z- m/ ~1 m<ul>
! W! P6 H" q% Y& g5 ]/ W- @<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>7 j( M# `4 ]7 z/ E
<li style="text-align: justify">
* J. W0 G7 D8 E) T$ d# f0 k<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p># G1 J' T+ h( e/ B3 A1 t& @1 o
! x# l# n7 m/ q. h) [0 V
2 W% j6 f: K( I1 |0 B7 i2 U2 O6 P5 i

  z7 k+ p+ o# [% S- H
; N  @0 ^2 z) h9 N</li>/ x1 V3 e/ R% w9 D
# D6 U7 r' z$ h$ v) i
/ }# G: D& G1 A. C- H) O

% o5 c) [5 y" x( R9 p# n( w. C$ H' u
( i4 I; C/ q( Y: z) D; e</ul>
$ A" P4 D0 y; i- I/ \4 }) r& s$ t' I# T2 y/ v
2 T, E+ b* J9 C4 D" E

! {# C. T. U! {1 J* z; [3 T8 W& y: D. i8 H) H( c+ z# D4 b0 X
</li>! r0 C3 D# S- I3 T1 S# R! W, j" A
<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
4 i3 M: x' m! V) |/ Z8 f<ul>
) D! }0 R; K, d9 x& I<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>* d" T" S2 j& W0 b1 d+ I
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>' J/ N8 }$ w8 R: G
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>3 w9 y+ m; x0 |  U, r6 P
/ X9 v/ _  u  \1 ?
$ d( b- Z' M% B# j2 E6 o1 u1 V8 F% g
6 |9 W+ u" v7 U. ?. y

4 c. u6 r5 s9 \; E$ |( H</ul>* B2 [, f7 \# o+ D+ ?. `
7 T' T- V7 }4 f

- B0 _7 q$ g9 V5 I0 K$ |# n4 p9 r5 t# z
1 V) S4 L5 v6 U+ f7 a+ e
</li>; H- Z" z/ n! n$ V1 b9 ?& @
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):, r7 @% N, i4 ^, W
<ul>
9 M, B1 F0 y1 p- z; @<li>3 U/ d, E; T* a& ~
<p data-tool="mdnice编辑器">适用于两个场景:</p>
2 O+ b% v  Q. g; u5 Q<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
4 {  }  Z! d+ G  G+ I0 a/ T<li>PeerLastZxid小于minCommittedLog</li>
3 Y' V- ^+ ^1 ^. R5 L' Z, v; m5 r# O<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>6 f; b2 A; ^4 K. w% O5 z

+ I; b9 M8 ^7 I- E5 w$ ^: n
; P1 t! d( `1 Q0 ~; w5 O7 l3 Z- [9 J9 r  n4 U

1 u9 _$ J: m$ n6 N, d- R</ol></li>6 P; V: u% C4 O9 t
<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>9 B& B+ d9 U/ Q0 U) C

1 {3 p$ g2 H! ?, o2 [' r# t% P
8 E+ S, V0 C0 {8 c' \4 T, ]8 o" ]- Y. b
4 h  ]5 H+ G, m$ V8 \6 {2 [) n2 f. \0 q2 V3 i  ~6 d
</ul>$ ^# t: x# h7 `. C# {4 `0 l

5 i) t' p, a( a6 j( G8 \9 i& {- _9 p) X3 D0 d8 v7 y
1 Y5 C" u* ?8 ]8 J. z4 t3 D  P/ h
8 F3 ^7 J$ H+ U& V9 G5 B
</li>5 {1 {' X, u1 a

4 S. r  Q* g* f% r0 Y0 b
) Z1 r3 k% c- u" r% Q/ s6 l& e$ n
- x% a, ]6 U& @8 |3 u
% E$ s3 V' }* e- u$ {</ul>  s/ F2 D4 B: R
<hr>
/ h# o/ j4 H) l) ]6 Q2 r6 Q6 ^<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>& k% J3 X2 @; K- W( T5 D
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>9 M  i; K# j1 ]
<ul>
" v( `% Z$ H& a' B0 u2 A<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
8 z# j: M6 c: o1 x1 ^<ul>4 @7 h. C/ z- Y: q3 L+ ^2 x
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
& g( `8 d/ G  j, w( {5 A8 I2 Y3 }<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
' X5 n! O' U; Z( y# F9 G4 a& B0 y2 I) ]" a
9 R4 Y: D5 x" ^9 L9 G& g
</ul>  G; k' S" [$ o8 {
; L1 t9 e% n0 E) k' |$ W9 V
( L# j# |2 Q3 T
</li>0 {: V9 g+ i% G8 U. f6 M* U8 i
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
* C) y, L5 }* F<ul>! P3 ~/ P  A3 J/ _0 r
<li data-tool="mdnice编辑器">
& _) T) {4 o3 F<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>* q& N" s: d  T
) p+ B  I2 M) o' W" ?& g% s

3 b9 Z. `$ U5 I+ `6 F7 v1 `0 k! J</li>
; n4 u( F9 ~" _3 m; o# p5 D, _3 l<li data-tool="mdnice编辑器">& \- C, U8 Q1 `4 a# K
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
: e$ M9 l7 M7 f& X$ e9 @% u4 E" L
/ K2 I" e- ~; T8 O9 h5 V! i" U: b' d+ t
</li>! h" V+ t6 ]3 I' u
, G2 m. ~' N0 Z
" b. D: p( C& O2 W4 E
</ul>
9 E1 F1 r$ ^. }1 r7 y/ t( r7 k9 b( E2 B$ Y0 }
) r- ?5 l* A0 u
</li>
5 h1 R& s% W' H9 \5 k2 V; F& U<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>: J9 X# ^+ F9 W! t; |; C* V5 _. \7 A
<ul>& W) Z& @7 F; F6 k0 X$ l
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
) B6 V* z: w, ?; ^* {
7 Y7 ^- t: f0 x+ f) B( t
1 f8 p$ m9 x! M</ul>6 A' O9 q  J: p2 o; \$ A' O8 L# B

+ D- B8 @' j  M" h" B) z1 |) y
3 L( Z  ^" z2 j) X</li>7 ~5 M" C1 p- T( G$ f0 ^

' j9 m' o) q; @7 }* W: V) R
- g8 j' T* Q5 n</ul>
' w  B2 S( `1 @+ L<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
2 E9 t  c' _4 X4 l0 C8 A) ^- c6 L<p>zookeeper 是如何保证事务的顺序一致性的?</p>
, `0 p- S& k- K4 {$ E2 P4 |<ul>( s% k! v7 u* U! O9 r% t
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>/ |) K# R, N% d. M

7 }. ]2 g8 q- j. ?- ^  Q5 }& G# k# u
$ @% Z# i; o7 i$ F, z</ul>- Z' {9 |  [* V; B! @; y/ J
<hr>
8 g' k6 j  Y7 y, u* M! Z# p  ~<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>8 h5 `7 A+ b& u/ X3 M3 k0 O& p: N
<ul>; ~! o5 m& u& _$ ^2 x% a2 t
<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
! \& x( T( P  r; m* i
; Q1 @& q) q3 {# @( o
! s7 F" |- g  D8 y% L/ }+ D</ul>
+ K+ J3 _! C4 m1 _; I<hr>4 k& c, {& h1 A1 Y1 ]& W, @
<p>说几个 zookeeper 常用的命令:</p>
6 {( C9 c5 T* v' ?. E# g( Q; g<ul># s- u/ \8 B8 |* E4 _/ Y+ z# E1 |
<li>ls path:查看当前 znode 的子节点</li>
* L) Z( e/ V+ J- y<li>get path:获取节点的值</li>9 _% k# V% p8 U% w+ j2 \
<li>set:设置节点的值</li>
. e) ~9 e4 r: T$ a& w<li> create,delete:创建/删除节点</li>* x1 t9 h# L$ J8 H. D
8 t' i' `; F. [) g+ [4 \

* F( H$ ?0 n  p8 l; B</ul>
$ ~, o( I5 z2 X/ m6 o/ H: U- U<hr>( `4 \& T6 \+ U7 ^) j
<p>会话Session:</p>+ {- _% f) t- r0 b9 N7 s$ i
<ul>& m; ]: W% ?  W1 Q
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>" T1 S' r% k- r% g
$ p+ s+ a- `' r" F* D' d4 ~: i: C, w

3 E: o5 E7 P4 S" W4 u4 s2 n8 f</ul>
& A) y, S, p2 {0 b" d<p>&nbsp;</p>  V, r8 P8 `1 |; v
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
4 m, [% j9 k3 n
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-4-13 02:28 , Processed in 0.063901 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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