飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

9321

主题

9409

帖子

3万

积分

管理员

Rank: 9Rank: 9Rank: 9

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

" j/ Q/ j! B: }6 H<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>
" p; w5 e+ a- H7 J; s<p>&nbsp;ZooKeeper 是什么?</p>
9 ?' p! |  [6 O3 a$ A- \! t<ul>1 z5 @. X7 l- y
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>
  K! B$ ?/ o7 h6 C  s<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># D* W# k& K" A1 w
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>
9 S; e9 _$ H  u- o3 W1 r: Y2 Q( K</ul>5 j, Y/ w6 g" C/ [- Z' y
<p>Zookeeper的作用(应用场景)?</p>$ p) @$ j5 Q1 {3 H0 e# j
<ul>5 }) H' J: k3 d8 G! u6 d
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>0 w: J0 |. }$ |
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
; i0 j: K$ K: U) N, h8 P<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
. U- x1 a, R2 `) 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调用。8 q5 G0 i' g- S2 i  Q
<ul>! @  M- z- J9 z7 [) l. H
<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
6 j7 E9 M) Y( t0 `5 y3 \) X0 o<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
: p6 d: E1 r* m6 s7 {+ J) X<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>$ ?$ S( V2 t4 Z! h2 U
</ul>- }) Y$ n3 B0 k6 _% h) K4 ^2 P& w
</li>
( w" A- L/ l* E6 b: T+ D: B; K<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>8 T0 k0 r$ j, {" ^
</ul>
" @. |+ V5 Z! n. W4 r<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>7 U3 H6 a" |1 t. x0 i: K7 m& S& j: A
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
' g, c: n0 a: L$ F<hr>
$ I7 m* m, c4 |2 I<p>&nbsp;文件系统:</p>
6 c$ s' o, ~' F1 V# f<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>
* z, b5 g/ k9 k<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
7 {! s( @8 U- z6 L$ ?" E<p>&nbsp;Znode节点主要有4中类型:</p>' T$ W9 ?8 Z' ]! [
<ul>
( M# f" Z7 f/ [$ S9 ], W+ a<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>& k+ p  i( v2 ~5 y$ O
<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>9 e. Y  X; y! j( p* F5 Q1 S$ i" Q
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
% A, h0 |  d0 a* B<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>4 E* D3 ]: j* F0 {
</ul>8 `% A" Z2 j; h1 `$ z% n9 ]( r
<hr>0 D, h4 {6 X* D% p
<p>&nbsp;通知机制 (监听机制)</p>- Y2 |  s# l& X' P5 B2 C
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>. E$ W% Q8 F4 ]3 {7 O
<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>
2 G+ y( i" W; S8 Y( [<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>. s  \. [( k8 d' f- c; X1 D
<ol># m1 H/ _! {0 f8 k' U
<li>客户端向服务端注册Wather监听</li>
9 j$ ?$ Y3 {- n! ?3 k! u/ }<li>保存Wather对象到客户端本地的WatherManager中</li>
, t& Y2 t3 @# W# f( }<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>" G6 v  c% X; B  r9 Y+ ?' @: P$ K" _
</ol>" A5 h& D  y( ^! @+ f/ d3 g9 P; p
<p>&nbsp;主要监听2方面内容:</p>3 }$ w6 E! D4 `- R: D
<ul class="list-paddingleft-2">+ u% X" n: k& g1 G% M
<li>* u# y" _! A- E( C+ J
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p># G; ]2 W  w8 F, l6 K6 I
</li>* F! K# u& e  v* W8 Q% j+ s% a5 k
<li>
3 w, j7 p6 q+ Q2 F3 V<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>: C5 K& @/ V' A0 ~' h8 `
</li>
- A9 W, ?/ Z  ~# ~$ R+ W4 y2 h</ul>! G  K$ x2 N, y. @% R6 @. _
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>
! c  L) v5 d1 {' z. W1 j<ul>8 C( J& I/ F9 P- C
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
9 t  a' j. o. P; f  g<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>, M9 D9 a- }3 e1 b7 {
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>: K& `1 v+ T0 A* n7 _
</ul>
$ S1 t4 p) j; h6 h; F( Y& P8 X+ [<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
1 f6 O7 d* F# x/ i<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>8 X" m, V+ m& b) R, m
<ul>5 W; o$ V" X7 R" p
<li>Leader:负责写数据。(写数据都有事务)</li>& _3 c) ^9 Y* k6 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) E& q  U% d
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>7 H: P4 r% `* v* U( }% M7 v7 q: u( q; k
9 A* O. ]! V& x3 }
</ul>& j9 C1 l* D* S( E
<hr>
# p2 O6 M+ G+ T3 c6 t; C2 f<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>5 o( t. a0 r" o# p' C4 V3 p7 |
<ul>
) d8 v: Z; {5 j$ L# p" S<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
4 T3 W/ x# s6 b! [1 \8 V<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>
4 c" C8 B6 ]% U9 `. U# `<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>1 p2 y& _6 c* D+ t
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
8 M/ J9 l6 e! C$ }( y5 K7 C, D& \
4 P( n: L1 @+ q</ul>% V0 Z. ]+ O' k4 t8 l, ~8 B6 {
<hr>: U7 p& ~. I5 t, Z
<p>其他概念:</p>
3 C& `$ r9 b$ h1 a<ul>
9 m' \: t# ~% }+ O: l  A<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
6 s# `8 j# ], A$ j1 U! T: |3 f! B<ul>+ }2 V' u1 s6 k
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
% k$ {' L: t) Y<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
; s7 U$ q: D0 j; a4 ~! Q4 [& }
' d+ ]# D; X; e+ ?: w6 a7 \: [6 d" q9 B9 t+ J
</ul>
) j5 k2 C# Y+ ]. R9 D1 m9 |, r+ y3 @- H

3 f. j. U" n( r% s5 V% ?7 P</li>
! p4 X% l: _7 u
1 K  K1 ]+ I4 w/ E3 [, o& H" m5 h3 i: q* H5 q% P
</ul>8 Z6 F4 j9 [" a9 W
<hr># t+ x# V' j+ H1 v3 b! g
<p>写数据原理:</p>
  _2 J9 _1 O  C+ U4 m<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>6 M! }# `8 @# E( I: Z# E
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
1 p. S  x: Z, J" I/ M<ul>+ n2 F, g* |* X6 Q) r3 v) k
<li>写给leader,leader再通知其他节点 </li>
& a8 L- F1 S' c7 ]<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>4 b3 s, s+ X1 x; t, p! K
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>
9 O9 N" T; ?7 Z) H; O2 i' Y) R. e! l& d, ^1 X
. m+ l$ x4 c. u6 t
</ul>2 C/ h% r6 |/ j2 ]; T: M
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>& m7 b9 T+ A8 z/ w+ S7 L
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
- x9 g* L! ^" ]/ T<hr>
1 S! q5 Y+ j/ W4 C! A+ H3 A  {7 i1 W% s<p>&nbsp;ZK怎么保证数据一致性?</p>5 Y0 `4 D4 Z% _9 S& F, Z$ l  K' 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>1 |) v* `/ b& ^- k
<p>&nbsp;一般我们正常是消息广播:</p>. `6 w- ?: j/ f
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
" I, r# _) T! @( `  Y5 h<ul>/ m9 h3 B& I# n. Z( Q
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,21 f  Y: D) j4 t! N9 H, E6 _
<ul>
! \: l& o. Y  U. b  @) g& E  ]<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
, Q# m, E1 r  v3 ]% @* a8 @<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>, ~8 }6 d  c/ z$ J/ |# W

8 q# y+ }# v4 i' R
4 O% h, m" m" b0 A8 K1 T+ |* T3 W- q5 [% _
( f) W2 I- E+ l* W1 S
1 [9 s4 T' {: J7 [2 _! x' {) ^

% f# K+ }& _, ?. Q/ w6 @0 y. Q5 g</ul>9 c, l5 F) H: ]4 f. I" d# |
& ?+ F+ {% v. \1 K" z

* b* e+ j3 }" ]; U, C5 u0 _/ T8 Q6 D( y% Y( m

) |) {1 b- v' X  N4 Q. H! U
/ i9 y/ r/ d2 H- q9 f: d) t1 t7 K7 {5 B+ p& y
</li>
: X# l% @' p/ R<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3" n, w+ Q7 r8 s$ G
<ul>' x' h, q+ }2 \# ?/ A# Y% [
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>
2 j' {  l' Z6 |+ O3 B; k- p$ r8 t) x0 ^! X

/ W1 t5 ?% v- T2 D" m8 v' b) m! o8 z" u& a9 C/ O  P

; @4 e1 _5 g2 D; V/ H. G
0 l+ v  z7 q& w8 R+ b# l( D& L- ]+ u% g5 X3 A
</ul>
) Y& h5 U& \/ J" Z
# z$ H6 m( Z2 t# r! O9 d0 W$ d6 r8 e
! M8 Z4 P4 P+ E0 x, o( f5 o6 J
2 ^% S3 I5 a2 F6 o& T

9 B, S) T: s/ L) h( P, i3 W* m6 Q# f8 j: }$ E: z) {
</li>$ l# h1 x" u9 |# K! D9 Y

/ n2 ?9 D/ n; }0 O' W  w5 ?' z3 K5 P& z  g6 r+ b7 J
3 ]& T; H! M" N, N

$ ?/ t8 \% ]: {- I+ f4 _8 q0 L8 k4 R: C+ ^" @& A, S9 N$ ~. S

* C; A+ d2 L' C" u</ul>
( `' D- X9 b0 l( U9 y! g<hr>0 X  E3 T7 M! ~6 k2 f+ R& Q, u$ e
<p>Leader宕机了如何进行选举?</p>
7 Y* @/ Q% T0 b- x* C<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
3 X+ ^/ w5 T( L; v<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>
) ?! t7 w  z. r6 c<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>9 O2 ]4 g& Q1 U* r- G& o. _
<hr>
) r7 P; v) p5 e<p>选举后数据如何同步?</p>& U" c/ D1 D5 a  P+ ]% S" j
<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>
$ Z& m% I5 M( m$ q% [  T, J8 M<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>! @( m/ ~7 R' W- j) A
<ul>
5 y8 G0 Y" ^6 v/ E1 Z<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
9 c: k! }5 G% \0 S" b4 e1 ]+ s<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
* h$ T7 r4 q6 ^4 P% W6 |  p<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>
$ Q: e' p" N0 V3 a
7 n/ U9 e* g& Y& v2 b, L; y9 {/ E2 A/ {  Z- s

, F/ ^' n3 @9 O7 z+ X( I
  V" i- V. W0 X+ _2 B  o5 o0 r6 J; D5 U: @& Y; q* o, W; V

. x0 _' x# ^; V0 P# z</ul>. q: V* Y* ~$ M6 V# P+ A. }
<p>同步策略:</p>( u& C  w8 {) q9 [# r* R7 R
<ul>
6 B1 k/ x# O. W+ Y  o. T0 D2 f5 N<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
+ l1 K! K6 a" [, u<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>
) N$ S: [9 h- z& R' J/ Y* 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">发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步</li>
6 C6 g- _6 S; V; e<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>% u3 d' D. G* H' D
. C$ o$ ~7 D# \0 b

# b! J5 ~' @& |- e6 T# X3 t9 O* w/ X1 w% ?

; @* ^! D) k/ ^. x. |</ol></li>' E5 O+ I8 U, {
<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>9 L' p+ u2 u& ?1 T
<ul>$ j; E0 S4 M* a3 F
<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>' Z* E* J4 i2 D9 ]' S
<li style="text-align: justify">! I+ y" P% h8 |3 Z  |
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
1 o" F( p& p( E- w8 o$ T& T! s; e5 f* n. O) h1 `. m
+ W4 Q8 O9 G" ?8 E
; j( |4 m/ F* s% A# D+ h

9 [3 Y7 }4 P& Z/ H</li>9 K' F. ]) {' N  \& t% n

8 I# ~) a) M4 ~, K: C6 h8 f
; m, l4 u5 Q6 S$ {1 w1 E2 d
+ @6 e* C0 R' A* K$ U. }6 {" ]  x, ?/ J8 e4 O# [( m3 i
</ul>2 T/ j: `# {; U1 d% t8 F; ^$ }

; i' k: Y* a& N( z
. ^6 r( Y* ]$ C, U8 y" Y9 |! L# H. R5 I8 }3 B: c( v* O' @' W
6 ~1 M/ e) _$ W* r) j% O
</li>9 L4 A' l: ^$ k
<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
7 R: r  r2 E1 e8 u9 P1 c<ul>/ T" l4 J- i$ u
<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>& V6 p; s6 F( U7 |
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>* D2 S' r. R0 \
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>& E$ j$ M$ o1 c8 B. e( }  D
3 F. \" T: z+ L
- Z. O5 ^* P- K0 M. P2 S, ]

- i) O: |, c( t% y' t, Y3 W  ?3 h: w  y
</ul>0 p2 i5 E. X- N# r

2 o! d+ G8 ?* P& H- n$ R. V# i/ @
& O) l, `5 D4 K6 g( d1 |: [+ X) x) s, R2 u2 L# s

* u8 K. `7 N$ u5 e</li>
5 L& J7 Q' O  R- O- e$ i! P8 f<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):$ L) c7 x) M) y8 M# g+ X
<ul>
- D* u/ ~/ W; y& K. f! D<li>" d; h/ F& l' `! J0 j9 g
<p data-tool="mdnice编辑器">适用于两个场景:</p>$ l* V, C2 }/ V% k' |  V  C7 z5 w$ |( e
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
3 ~( e7 D( N& l3 h<li>PeerLastZxid小于minCommittedLog</li>
1 n/ j: Y$ ]# c# I- ~& H<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>
! m0 R; [' S/ f1 j) [1 F. J. n- U$ r* A7 L( H

' g1 O( k% n5 B+ K6 r
1 b" z. C* A- b) P0 L7 Z' C8 s% u4 C& H1 w/ K/ W
</ol></li>
& R/ l6 M; G* H- _<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>2 ^$ R1 Z) t% e  k
7 O- e# q, r) E+ A
, R" Q, Q8 f6 F4 i9 y; Y4 \- u3 D
9 M, V# q% G) X, e: P  D+ Q
: i  p% W" u1 ^9 g! V
</ul>& }7 c% D. X; y7 K3 y; _2 @  E. O
# E! t& k4 V( p% \

4 b* Q$ S5 X8 A! O
0 ]; _( A: m8 U! U$ ]0 w! Y5 B8 @* L! F# [/ t" m
</li>
9 l2 [7 i0 v& S( J. i& u4 ?, P  S) N* D& m0 q5 k  I: B
  K2 j6 T* E! V" t1 @; q

: Y& X4 Y" l1 L" g3 Z
" L9 ]0 R, s/ a5 J( m) J) z</ul>
3 ?* t6 Q: W) `" Q% l; g<hr>4 |, |4 J( `7 x
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>; f; U, }8 c  f& |6 e4 k
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>( x5 T- w$ F# H0 r3 ~% E) k/ @
<ul>- b+ ]- i+ \6 H8 C0 ~
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>9 Q+ \& W  [% j6 S9 o* `7 B  ~
<ul>4 q$ O9 H; }1 c) A# X+ a
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
8 [3 T, q$ I& I/ r0 Y" I<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
0 j: b& v) V3 o4 s* }3 f, T; J9 ~0 o/ m/ N; H$ X
5 x0 i2 t1 {  A0 }) ^: V
</ul>
: s) @( K. u- y- I" W6 F, |2 a( j2 p: F9 u; B, F
* I% `/ L9 {" C, g$ h1 @
</li>
1 z6 O% p& H# ^<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
  V, ]8 N4 Z9 \. E<ul>& @  o8 E1 G( y4 B- b) G; q
<li data-tool="mdnice编辑器">5 t4 r# J$ x/ s" Z5 ^. F  ~# }4 T
<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>2 a" F+ Y+ e) b1 r% d1 d# T
5 i% b# B) G7 k5 @, l9 y

1 z% B, h7 ], i& F/ i: d9 s</li>7 j1 A, B% n9 A. q; [5 a1 J
<li data-tool="mdnice编辑器">, k( q4 w. c' q1 v( {
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
# R7 b& a) O/ K( v7 e- f$ e( `
5 q; N; l$ q9 W% X, W
7 \$ r. K! F0 _4 e* {1 s( i8 C! `</li>8 h" @/ }  z8 t

/ G  G# f" u7 l; Q- c4 h% K" m9 A2 d
</ul>/ `: V6 K; u9 F+ A8 \, V  q

( K' B: H' b5 `# N
, A: o& j3 A" G</li>
- z% ], D. L" Q' |<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong># h, t+ J7 ]! }! y
<ul>2 C: M) W& \8 p  ^7 ?1 V
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
& @! F5 }6 X% y. i9 G( D$ L# m5 @, s' ^+ c7 G( g3 p* z1 E; y/ ~

( }' P4 V& w0 i! J/ J% Z! Q0 P) Y</ul>
( E# ]5 H' S% N" y7 `
( ]  {5 d. e$ K# S
& N# b- g. }$ p: _' w. e</li>
. c, l2 Z9 V6 b: e! Y3 V/ U: c  ~" w& a1 G
6 H4 ?1 s; O, Y* [2 l' C8 b
</ul>" P" r+ }. Q. N4 o' Y( M; G, M
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>$ J! z& @) M6 _# R$ u
<p>zookeeper 是如何保证事务的顺序一致性的?</p>
6 l, _" Y# p- A8 q3 H' b7 a' b1 E<ul>
, x( f/ b! t4 I+ Y3 j<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
5 r/ n% o( N8 L
/ v7 L; p% Z8 W7 \: }) J* e$ Q: b& {  p( ^7 p) n# R. V
</ul>
# u" C' O# |! V- c& q1 F& m# O: V<hr>. x- @$ N6 p8 a6 U
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
1 _& s$ D" u: S/ s9 @- v* x( N<ul>9 o+ i/ ?3 v8 c9 V6 d0 X! D! h8 {
<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>! a+ y  k4 \, v+ G

( Y) E$ o; l9 a& c) B- ?& p( _$ z& ~5 ~* ~) q0 `+ R1 t
</ul>/ B5 u1 \( [$ _
<hr>
. n8 l4 y. I0 e' L1 n4 Y# `4 `( ^<p>说几个 zookeeper 常用的命令:</p>5 C: }3 w4 y+ K
<ul>0 H9 t! |6 ~7 g. S* C
<li>ls path:查看当前 znode 的子节点</li>
. r% m7 B2 O. a4 O- e9 ~<li>get path:获取节点的值</li>
9 U1 `# S/ r1 R<li>set:设置节点的值</li>
2 F( O% S% ^) v( ^( q5 g<li> create,delete:创建/删除节点</li>  d% U, p- O8 {2 v/ R9 @+ E

( j* k+ V# m7 }* t0 J" L! a# v) c9 S
</ul>
2 X" a4 d! D8 V1 E4 N# e$ ?% J; P, g% b; Q<hr>3 w$ e  f& r, Q( T
<p>会话Session:</p>
7 E- L' f6 `9 x% y/ d<ul>
3 i7 M2 Z, }' {& M# I<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>: Z1 I9 N( Q( {0 `  X6 Z; e# a0 y

/ n. u( f! s  x5 E3 S0 U7 n
0 s7 |) R; i8 W' U</ul>
* p. @: V9 a- L* [  T. d<p>&nbsp;</p>- f& t$ ]" ], F0 J1 ^
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
2 _" u9 ]. t3 I2 R
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-11-4 15:52 , Processed in 0.065988 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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