飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

8267

主题

8355

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

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

9 N4 n: E" b# U; ?! c" K<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>) }% m9 _1 {  x! e& Q
<p>&nbsp;ZooKeeper 是什么?</p>5 k% `5 d, E5 t' z6 I0 n5 y
<ul>
/ K  d% d, Q( R" c# `' V5 z<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>9 y2 m( o; g- Q- F
<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>
- x" i! L9 G# u5 e' N) R<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>  L; n! g( ?/ Z1 z0 g
</ul>" ~5 }6 Q, i6 ^" U
<p>Zookeeper的作用(应用场景)?</p>4 {; @* R5 j% [1 |
<ul>  [! w1 l7 x5 u$ R3 \7 z9 R
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>. }" X% Q) S2 X# n8 W2 A* Q0 E7 Y
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
. ]4 {6 w: ^% V3 E<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>& {5 v! E: y8 {) o# b; y" K
<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调用。) j6 g, H8 q% s: D
<ul>( x  [; `0 m$ k; J
<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
% _, [7 ?0 H* Y- Z; t# Q<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>3 z  o: o3 A1 D, }
<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>) F6 z" d/ O$ l, _1 f8 z6 }6 t, i
</ul>
& s6 U/ R9 A( A: }  H</li>
% ]) L8 E1 `: t" `<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
% G; u$ z( ^8 \5 N4 M</ul>
* `/ P* ?% ?5 W& }' B* w/ t. z5 a$ _<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>
0 Q+ R3 x/ V4 x1 M6 C; g' j9 I<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
* D8 ^: F; S( @  d/ g<hr>6 I& Z: _+ g5 p2 S9 z
<p>&nbsp;文件系统:</p>% b1 ?% M/ x0 H% T2 l$ p3 S+ U0 A
<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>" D! G! j9 |, R8 A* G
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>! f0 t3 L% b+ Y; Z- M$ ^
<p>&nbsp;Znode节点主要有4中类型:</p>
& F( b# U4 U0 m9 t( f' B. K<ul>, w) a0 }8 m. F7 Z" ~- Q
<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
3 \: o# I: @; _2 D5 `( z, m6 J; p! z<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>% [) U' z3 ?& \( H
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
# {9 D7 {6 ^2 N# S0 r) Z4 ^<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
' p# s; q& V; r</ul>; n! P' c7 x$ A: g" c% |/ c) r1 ]
<hr>
4 w) Z0 C) S% ]/ A, C# Z<p>&nbsp;通知机制 (监听机制)</p>) C, m6 R0 z/ g& L  l
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
2 Q, e$ F( q, C- L. N$ W5 }9 ?- ?<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>
( m" d6 v( _1 D' W0 y# _6 l( Y<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
# s- l9 d/ p4 R, o7 N; n. `4 y<ol>3 U" h. F2 T) e+ U1 ]
<li>客户端向服务端注册Wather监听</li>
0 t8 r- S5 W2 o2 L, U+ X<li>保存Wather对象到客户端本地的WatherManager中</li>/ {$ [) e/ ?* w* f% L6 i1 b; Q# r
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>
% T- j" G. h+ C! T</ol>
# x) ~" w! |, M' U0 Z' V% Z* j<p>&nbsp;主要监听2方面内容:</p>
9 F& I( j. k/ m" a* g<ul class="list-paddingleft-2">
; c2 f8 c6 o  u1 c<li>
& t+ @! N: O) M" r3 @<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>) t& f/ C% W' b: Z5 F. @% f
</li>
/ h9 V6 q! f% w- e4 M3 `<li>
8 F5 M8 `: {  G- Y; d; K  q: K<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
3 M& W4 T! m- L" |</li>' [5 S! f* ?, i) o  S2 r0 t
</ul>
& a+ a8 W/ F1 e) [( f7 C<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>( n# }) m: v* j- r) n* O
<ul>
5 {: C* m/ u$ E8 Y/ c<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>! T6 t) p" r* L! Q
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
1 C. {5 P7 V( Z' q  B5 X: y  q<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>( `7 ^4 e: E! f7 s; {8 P, t  ]- O: _
</ul>
, z9 F8 U2 `4 ?1 ?& K: Z<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
5 V+ s7 }6 W$ G! h' u<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>! f/ g/ g. C+ l
<ul>
5 g' A2 K5 S; m/ g8 v0 l! p<li>Leader:负责写数据。(写数据都有事务)</li>& F) [1 a9 R* N
<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>' a! U  w8 e2 O4 O- ]
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>
* V" U0 w& j/ e/ a
0 l) m! P5 |6 f, J5 u- R$ P</ul>& |" D3 K: C2 [; [! J
<hr>
% h9 L9 V5 \% C! [<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p># E' L: ]7 e6 k6 R) l
<ul>
- y9 s( N/ c1 w4 j<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
1 @, `6 x6 M, h7 t: j4 w<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>
) A: R! }! R0 k( w<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
; N- p# N- X4 ]* @/ D<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>  j8 ?+ ^5 Z& G  Y8 N2 B+ @9 M

1 ~9 ~1 J, s' D</ul>* Y, g+ L; n; R$ p, Y( A" \. t8 P& v
<hr>
8 N& K( G; s0 X2 S* q7 \8 k<p>其他概念:</p>
( H( i2 U4 D+ }6 t3 z# Z% N<ul>
, s: g9 C/ \! c" m3 @* F<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:+ k5 [- z, f  r% D6 x
<ul>6 u  H) t* w3 x6 W8 u
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>8 _% ?/ k7 Q, E' c) ]$ A  `
<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
5 ?4 N1 L. n$ h0 t& m* f6 h
  V3 S- t$ u6 n  Y2 L, w* _% \+ k# A# y4 l
</ul>
) ^8 |1 z, a% c& @2 p) G# x7 A; P% V4 O  o  c: Z  N4 |- S
+ P4 N7 y- t7 B7 E% H
</li>
8 i) ]$ s8 b* s3 X  h/ M6 l( Q! q3 A9 F$ |

) o8 B3 d- \/ m" u4 x+ Y4 [</ul>$ Q' U  T( T3 _. L* b" {. n
<hr>
$ c1 U# L- X$ K: F. N- Y<p>写数据原理:</p>3 H2 W6 h0 W# b3 b' U5 s4 S
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
* Y8 z/ \! Q* e# [9 s<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>5 X) L1 Z3 m# j! z$ U4 Z
<ul>
* b! a! h  W+ w<li>写给leader,leader再通知其他节点 </li>
9 m5 g6 g; t" x<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>! q9 }! b5 Q4 J" V: E
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>9 c" Z# D/ w/ }$ K7 r

- `2 S6 @% c8 l
! X6 s% |. k& O% i' u</ul>2 n. j9 ~- r( J1 h' ]4 R: }
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
  p1 H: W7 ~6 Z<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>2 f( i8 `0 s7 C5 O5 s8 E
<hr>8 z% S4 q- [& d0 a1 V/ H
<p>&nbsp;ZK怎么保证数据一致性?</p>/ S' I& t. H$ N- u: t# A
<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>
2 e, j$ t3 _" s$ z<p>&nbsp;一般我们正常是消息广播:</p>
( F& C' R% Z( j" h1 W1 q  E- Y<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
& c- |7 K# y% t4 h+ }2 M* I7 k<ul>$ I/ `: t& Q/ }8 J5 |9 t- I
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
' \; g  {8 }: ~* [1 x<ul>
: p0 F  v- r/ G/ U/ e<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
6 L8 d* |# F8 s: i& V<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>$ A0 x9 A$ Q( e+ V
% ~- ]& @- U% ?) Z0 ~6 I

8 C( J! f4 l- F' u2 W9 Z; H/ z: T* s1 k! i  t

9 c4 g8 s2 B# I
+ J* }( l" S% P, s& V' f
+ H; A" C; u) ~& F$ Q</ul>
( [- N* G( p; I4 c4 c; V+ k1 o+ h
! B1 F; k5 X8 Q% j* G4 A2 z: Y% S- {/ J: q! D3 ~0 O
% }# d. E- z" D$ }: W) w  J

) `  [2 V1 f+ f) X  i- h4 H  J, [( K' O

- d2 u/ |( j( [</li>7 C/ R" r1 j1 `. e  z5 [: T& h! ~
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
, s& Q+ n0 h  {- f' T: Q  f<ul>
1 r. s+ E2 m) ?, m) ?<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>
5 a& x! P! |  s: x
  B% v4 t4 y/ z& W8 G+ o- U! z
) a  r* j& Y/ W, Q$ f6 O( E; ~: k* }
7 ]8 F! n3 x& i$ `
. M& F5 ]* b+ Y& O; O8 S9 n. e' l. ?, U+ g$ O& `

; v4 R* v! `) }$ r0 R4 ?</ul>, K9 Z/ T7 i0 t+ {. j

1 C$ |! h4 G8 h+ w8 ^+ E( k6 M) g6 Z
1 m. w( K  P% A" Z
* X  x- L# v! O: f/ s0 z4 y9 ]
( t# ?! w/ h, X+ N9 |
6 J3 m* l1 g; T
</li>
8 @' k$ a1 u  c) C& h2 ~
! w/ w! O: Z/ S6 E; w* U+ L! O: z4 u2 f! F/ E& I

  P% a% v! ?8 v; X0 b( U: O
7 m5 l. [7 _1 X" g- M
0 X" H( _8 K, w% {( d( `
0 G2 j! Z1 @" ~/ _$ j3 |8 p</ul>
% x: y* w  e: w  t( l" R$ D6 s6 U8 }<hr># J* o* q2 |; v9 n
<p>Leader宕机了如何进行选举?</p>3 F2 |' o/ d- C5 g1 B: P) l* X
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>2 N$ T; E9 \9 X$ X
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>
" s: c$ e; i, w' w* Q' V<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
/ M% Q. _7 b$ T: C2 M) J<hr>
4 y$ I' R1 b7 o8 g<p>选举后数据如何同步?</p>
* X0 V( f6 c" d" \<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>; l  U) Y( T  {5 @$ o: m
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>* q# z9 y0 {% Z9 W0 s8 a2 x: T
<ul>- c+ F+ C4 E- m* i- S
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>: r& J5 ~& w9 @  g: }9 q) [
<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>/ E$ Z7 X' E* o  L
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>! |# g( Y3 r* l
0 Z4 D! z0 R" B1 e7 D

( k' H* j9 x" U% n% p# [4 M0 p
- w- b7 b4 N4 U6 w5 }5 |4 s/ l
- {; @2 U( [" l1 v: a/ Y. G5 u" _% ^6 L
! s+ u3 x* `% g$ J# V: i6 s' m
</ul>
+ y- j. \: K% W) q2 G<p>同步策略:</p>
/ N7 B$ E5 v( L2 ~8 O<ul>
) B& i+ O) P! w) u' A# Q1 r3 O: F<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>( r* j! F# q3 I# n$ S7 s
<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 m, N  x( t) l; g/ I6 z
<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 D9 l. t# [/ I$ X<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>
0 C! `, g2 E1 I& ]6 l2 I
) w) I* {- P$ T9 d
! [$ Y+ {: T# A9 G, S# h; p0 o- O- }* n& T: p4 a# `9 r# A

2 m, p: A$ ~* i, c! }</ol></li>
, L: i7 ]$ p) p) ~<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>  h6 w; M; H& x
<ul>8 z. G& R# |# s$ d5 }
<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>
. _. y3 u: N2 D) Q1 ~0 c5 c<li style="text-align: justify">
$ W: G% N; `) I$ U( ^8 j3 ?<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>4 }* b1 P' j4 }( K! E% i

% i+ v2 a) u% e' C3 g% v0 U$ \; _/ r

, L- W* D8 r5 b/ h4 J* H/ K: h0 W5 c- @& \9 A, w$ z
</li>
+ h, W8 A3 q- S, s" i! V# e, C2 d7 X- Q/ s

; g9 ~1 r) G0 C  n, I% A
3 {+ g) I0 h: k  W2 n% Q) T" w1 W9 y
</ul>. D  \% Y, J- e7 |% U

" x# }) e! c) V7 [8 j- N( d+ y
: j$ P) g' Y& F; G* p0 x& R( ~4 a& `0 |$ f2 W. l

6 x6 F6 C) O" h</li>
& Q  J7 L9 S1 B+ S<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):+ x! U& e( X8 [. q' \
<ul>
- L" L5 }% ~$ S  n<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
+ v$ M8 ^# O* d/ n6 D0 H<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>: R: C. t; ]  T! a6 `( Q; W8 ?
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
' s* J0 ?8 Q5 U0 l* T  X
* d- p+ I0 T9 s7 J7 P0 _( Q+ t1 ^; V( d2 d8 Z9 ?$ e" M6 G
- J. {: v9 B. D

; r/ n5 {+ J7 H( @9 L. Z9 @</ul>
  w; ^2 n! T9 k# X
' N8 P4 A0 I/ O! _1 Q8 w" o2 |: e* k0 X( @- G3 y, e" H
) H+ F. E0 K" C8 ]2 u7 ?" \* f/ Z
, V6 w& o* C/ ^' k" O
</li>
* m; q. s  C$ l- [8 J<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):+ f) ^' D; N; q: b0 }
<ul>
! j! j- `) d6 r- Z<li>
, j- Z9 H/ A  y" R/ F<p data-tool="mdnice编辑器">适用于两个场景:</p>
) c3 g) c9 K9 a& c# j4 ~9 o' M7 Q<ol class="list-paddingleft-2" data-tool="mdnice编辑器">1 O3 o* t/ h3 G8 d
<li>PeerLastZxid小于minCommittedLog</li>
- Q0 i& h9 c* N5 W+ e2 g9 X<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>
1 [" n9 E7 _& r4 L# ~
- _8 o6 y9 K" ^" s- }. C
2 W0 d) ?2 K. f* H# x8 r8 e8 ~- a# a
+ X8 T# R$ ^5 R0 {) o! E4 x0 e& u% {5 O" D' R
</ol></li>
# e0 @6 A: L5 ~1 w& M# u<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
. l) {( B; ], W) x& ]7 M; w4 o9 H

1 T0 t& _" e2 G% J6 E5 g7 w
8 U# L* n) c0 n, _% v7 r& k$ x8 v( q+ U
! ~; s4 f. [8 A& ]) N</ul>
; H8 R1 L6 K; D
" S  [; N0 ]% x5 z. U3 {  s% Y) ?& g; F6 f  g
7 l( T+ P- a: N- n$ G

  h0 G0 d0 ~" b* J* v+ q0 l5 ?& \( g</li>
* Z5 t) l4 V2 [  q3 Q! i( f& y

) F$ `4 a, R) J) l: A8 H; U* t

4 z8 K; Q# g2 ~  |</ul>& j( ~0 P1 O) \3 T" n  d! c
<hr>  X- U; x" l# W5 a4 o  J; F
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
" Q: `6 x* o/ Y+ N% J. {" t. [# s. w<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>8 Q0 k: m2 C1 D8 B  R( O
<ul>0 i) ?. l  Q$ y- V2 }4 Q/ e! Z
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
$ G/ `! A+ Z' P% C; ^8 r<ul>
1 @9 b7 s, ^4 t8 J<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>7 @- h  H+ U! y) H  z2 }, L+ K
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>; f$ N% i! N) b

8 @" E& f6 H9 e- J2 a) q, U. D( b/ P  q9 ?/ r
</ul>0 T" z3 U; S, e, ]; [
% g5 j7 }8 l; Y' t& V- h) M) g2 c
* J, _. Y7 ~( ]) b" w) r
</li>( r# g/ F# D1 C) R& X
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>8 p2 A- ~$ M. u2 q
<ul>
  i. P  R3 r6 F1 [8 k3 L% y/ \<li data-tool="mdnice编辑器">
5 `% r6 `0 ]* [. M9 I' D! D<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>9 D$ a; Z( C6 V. F3 V

7 h% ~' j' {8 n) O2 L7 _" P  m2 V' h, `
</li>  _( ~; P$ L2 G! d. {
<li data-tool="mdnice编辑器">
4 h  b. W8 E- @3 t<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>. k- M" x1 X9 q

( d3 r& w( H  }( r7 Y
3 m% u4 T# R/ I* l. T  f- W1 D</li>
* h, \8 f/ x. E* X. I9 H' A$ o% h7 _; V9 f8 N) Z. [$ p' g. |' w
# i$ g7 _7 X3 O/ i' k
</ul>
6 x3 R* b) m7 G7 N! z
; {% P" b" O# q( O  X9 l4 R9 c. A  R: d; m7 R
</li>
- M% s' {# C' t) k* F. n<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
* B! h4 s  q3 u* z9 z! }( F<ul>: w6 V6 F% f  g. G$ U- Z# J
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
. u/ J' Y" L. v% k# m3 ]0 c% G) Y6 E2 @8 ]0 O

  o- v, y: F: a# P</ul>$ N. l5 D% u& X- t8 ~. W" x% f# e
* j% e1 W1 {4 ~" F5 D2 F/ z
# {" H- q# m4 w8 f* A
</li>( \' |' D8 J: D. ~, w6 O

. Z% b( ~  Z" T2 [5 B  n1 V, S- a
- \" U# f0 I6 `( Y</ul>) {9 Z3 G* H6 k3 S2 x  d) I3 t' p% l- |
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>1 w( r1 Z0 }( _. h3 z
<p>zookeeper 是如何保证事务的顺序一致性的?</p>" R3 I3 u1 A! M- q  \7 _* i
<ul>
9 A# ?/ _3 e1 @1 h5 C9 g. z<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>0 k. R  y8 k) I9 _0 I2 v
- [3 g% u9 f0 a. G# `9 t7 M! _6 q

6 p# [! h" v) {) h8 ^2 C0 Q</ul>
6 E5 x6 w: c, N5 R9 W<hr>
8 @! A( e& P& ]- \+ I& v<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>% q! w5 x" T6 p
<ul>% q; l( ?! V/ ?8 R* ^
<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>) a  A( L, `7 ?
/ r/ V* o$ m. _7 Z! ?6 T
1 l" w4 q. \8 O/ L: o  O
</ul>$ v8 i2 l7 D, D) q( ]' `6 E
<hr>0 A. q9 u0 O6 c- V" v/ p
<p>说几个 zookeeper 常用的命令:</p>
- P2 e: _7 P. y) F( j<ul>" X: i1 c- L' c$ Y" ~/ v" t" v
<li>ls path:查看当前 znode 的子节点</li>) ]% A  A9 J) J5 j
<li>get path:获取节点的值</li>  y% m0 j: e3 Q/ n
<li>set:设置节点的值</li>( W; Y6 O/ A& W" n) F, r9 X
<li> create,delete:创建/删除节点</li>
( t0 k5 [& X1 O4 \4 S. m5 o% W
+ C& C0 H8 A, f' S, g
6 N! X3 [0 k3 _+ ]& ^</ul>
+ A' e! k; v* S% y, o) ^/ R/ k<hr>, J2 c- ~; f2 X" K
<p>会话Session:</p>  l7 m7 v% x) n# M) z
<ul>3 z" e- O, l4 q. f( u
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
/ z" x7 b/ K+ v: j* p, i0 ^- ?9 s. _* H

' n- G/ F# J# e" _: ^4 A1 [</ul>2 R5 f2 s' X! H! E% \9 j
<p>&nbsp;</p>0 b4 {; A2 R; X) @* ?( U8 E  M
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
  I& U  [  J" w3 ^8 @/ f0 d
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-2-28 11:41 , Processed in 0.064471 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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