飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

9221

主题

9309

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
29993
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
. N! R8 m, S3 L4 [2 e+ Q  P
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote># _$ |( ~$ c5 w: Z" s9 Z
<p>&nbsp;ZooKeeper 是什么?</p>
. O; z) J2 C) }0 e; P1 v<ul>0 K6 j+ b& R* L' O+ k+ J1 |7 U; e
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>- A7 {$ ?+ ~' h% C# m
<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># ]) [( N; {% A
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>" n/ X# @9 |+ m7 b+ i; k/ G
</ul>
' l& n1 o4 m2 F, |; {1 j<p>Zookeeper的作用(应用场景)?</p>! p; k* y4 R5 f4 y  y: O
<ul>1 B* p; \7 p: V" ^
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>, O3 P) G2 E+ q% G9 x0 y% _1 L6 R" B
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
' k9 {9 l6 O& Y& I<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
9 t9 E/ t5 \2 H8 ]<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调用。) S5 q% w& T5 r' M
<ul>
+ o; |5 O( |* S' r  X' S0 \<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>7 ]7 i) b9 D. J) y/ Q
<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
$ y/ O' k% f  L1 e7 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>; u/ n& L# S; t* n. Q" N
</ul>
# s7 C3 Z: c* o( R</li>
! ^! n' L1 y' N( g; O% v<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
: R# y1 [# ]( ?2 |* X7 h( l0 E</ul>
/ N; t% E: D$ d1 {9 j4 R2 v<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>
1 J& G( r+ `& f- K/ z- ~" n<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>" b# A8 O8 S  j# {; }3 N# w: W
<hr>% j$ R% Q  V+ _9 N% j
<p>&nbsp;文件系统:</p>1 {2 Z( F# D4 F2 M
<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>
! I% Q: e: N& {3 I0 v8 `; N<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
( @8 u) u( v/ [2 m, Z<p>&nbsp;Znode节点主要有4中类型:</p>7 I4 O9 T- I6 F3 z. k$ _; V8 G8 w
<ul>* I3 j% B4 H+ y# B
<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>. s+ B  w7 t3 M- P! M
<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>: Y/ y) _1 @' i
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>! h2 f, I* ~+ d+ U" j5 _
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li># r8 b, w6 ?$ `: F. i" P. k
</ul># W( d! u) Z; b+ e
<hr>
$ j) i* n" d% f; D<p>&nbsp;通知机制 (监听机制)</p>7 m# I2 V# S$ }+ J# u
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>/ C: w' W+ a, J( I! b
<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>
3 Y2 D4 F4 g0 d4 [<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>$ @' q, O& a. I% B
<ol>
' X  M3 o8 u# b5 K# m7 s  J1 Q8 h<li>客户端向服务端注册Wather监听</li>
% l) _1 `# f& R9 F' y8 y8 [<li>保存Wather对象到客户端本地的WatherManager中</li>; f% U4 @" a* O% Q2 L5 x
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>
+ P0 t' S, q* C5 j2 d</ol>
6 h! ~! c; d% t' D<p>&nbsp;主要监听2方面内容:</p>$ `! L: ^1 U9 v# p  d  Y" X* w& ?9 ]. |
<ul class="list-paddingleft-2">
5 r# E1 V+ P& P3 s<li># z& h7 ~4 e& y6 Z% h
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
' G9 ^+ v4 P4 ]1 k0 R; V</li>
) P  y: z5 _# @# _' V<li>! Z( O' j- s. X
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>. T, o8 y' K- h5 e5 C; l
</li>
$ a) F5 x$ Z. f8 G9 `& B. x8 W</ul>: [/ v# K& w. s4 }
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>4 m. K  U) B3 |% S/ Q
<ul>
# x4 R& v! ?9 N! Q' }<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
) ]+ W$ i$ _7 H7 t* l/ [6 F0 X<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>7 Z0 q) F6 g- q9 u6 |6 k& Z
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
, ]) {0 y* _8 A, f</ul>6 j5 ^6 t- }$ E! ^. W
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
+ N$ ^3 U1 _+ e9 I5 _<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>. I% X+ k* U6 e1 n
<ul>0 Z' v3 ^; W+ J, U
<li>Leader:负责写数据。(写数据都有事务)</li>8 l4 l" I/ [6 D  F; x# Y2 ]( D" P
<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>
; ]; K4 ]' @' T4 E% P<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>
" J: \) N3 O3 b1 H% q1 n% m$ y! _( z  b% {* e4 c/ }
</ul>
% N5 Q# d! ?3 U: V<hr>+ M* i. |4 R/ h
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
% {8 ]5 S4 ?! L9 f( X2 v6 E<ul>3 c9 P. m# [$ f6 y* K) M
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
% l% S  Z( H0 g' U" O- [<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>
; n4 g5 h$ e9 s0 m( o  `) i7 i<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
, D: g) F& Q% m$ z1 q<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
# ~( F4 }2 w. U  s* r6 ^# K- w8 U. a1 y& d7 t
</ul>4 G# E$ A5 i, @' V# e
<hr>: w+ z  x! V$ H5 X, u! d
<p>其他概念:</p>" L5 n" [$ R) W" a4 [2 M; Y6 ?+ J
<ul>
2 i+ ~$ h* k- x, ?<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
/ h. Q4 Y  n7 X# r<ul>$ ?5 h: W& {; k/ F
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>( `& T* R% h" _/ d; h
<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>% D! B4 k$ o0 q+ k
* Y/ L; |1 e, @
8 ~% S2 U8 g& D
</ul>
4 C8 Y( Z. y* `9 P2 r. ?; E8 a5 t( L& J' V' G- F, M
' Z! z. m" y8 I$ u6 W! P
</li>% G( s: ?/ ~! t- O0 J7 m6 r
2 c7 W5 J0 \: b0 l9 j

/ O2 J& u: Q: J- I" b; A+ I9 X% \</ul>. Z9 U; l5 U6 ^* r0 p- J2 X
<hr>% I, S0 e9 p( A7 W$ }3 U3 w4 d) O+ o
<p>写数据原理:</p>5 l/ d! h" t& ?, e0 M
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
! O3 h2 ?  _5 k<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>& N" s- i2 C) y9 I3 F$ x. ^& ]5 s
<ul>
; ~1 Q* Z7 ?( h<li>写给leader,leader再通知其他节点 </li>5 l  c: p! y# U! X# i+ v, L
<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>( l4 `, f% `  {5 T" A
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>, t( ~) @1 ^: z- {
& W( E: P; [. D) m( M3 ^3 S9 O0 I
4 V' w6 B) J) B5 l" U9 X
</ul>
( `, A7 W. j9 y<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
5 |1 M' `) O9 v9 _<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
8 l1 l% t2 W9 j3 a4 F, B<hr>
3 g5 e# T0 [: a# `6 e2 P<p>&nbsp;ZK怎么保证数据一致性?</p>' S  s6 Z3 B4 M* Z+ ?# g8 G
<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>5 F/ L/ E$ T; ]  d; P8 }4 ?2 T
<p>&nbsp;一般我们正常是消息广播:</p>4 a( P  Y* B0 F
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
: y/ x" j+ T5 ^: s<ul>0 n: g) V- A2 T- U: [7 Y% H
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
* |# T$ v7 R& b0 q  C6 c; z2 g<ul>$ o! I5 b- k/ v1 F( U* o+ z  H$ ]
<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>8 ?" q1 `1 i( k" \9 G- m
<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>' A& t4 G  B4 \. c. ^  k

* J6 f5 `% d  v/ w. p
/ z% ]3 j: h, {3 T' f7 Z/ C# e/ D' {

; ]  |7 ~' F' K; `( [9 \) z, k
7 @. f% j5 f& u
, j" {% v5 q2 T; G: Q$ v, g3 {8 q</ul>* i' G8 G& G+ F$ D* I

+ F. G. f. g, G9 t3 e6 l" ~5 s4 M
8 P: L, z* P: j1 ~+ C9 Y* Z5 l5 ~, {

4 V4 K/ R; x9 @, i  l
  B9 ~3 z3 u0 `. a# n3 i2 l& T4 y7 p* H( X3 }, r1 N4 B6 J1 O, S
</li>
% }2 @# a+ J  F) \# j' r! Y7 }<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3- Y7 T8 R$ M; _* ^! y
<ul>! l3 D, G$ ~' ]. E$ D* \2 X
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>
$ E3 k, g5 t; o& D. v2 G8 ^+ q) @0 R# @, e- @! L

% |- ]  m0 |8 P) x, O  [4 F3 _5 R, r, |0 [- G
3 X3 s+ r- {  c7 c/ B5 C) Q

  ]% i. u, r% w3 G! O# ~+ `6 E! B
5 h* A  {) O9 F</ul>2 b; k" y5 u* m& I1 ]# r0 K

4 ^) {' l9 S% d9 P* W* S6 v& j4 w- [. s$ g3 }' R2 z" d3 g
  W" L' z1 |% ?7 y3 R( y, q4 A* v  Q
2 d$ X, N1 p+ f) b0 |

' H- x! t" }3 x, M1 S. F- C- A6 s9 i" @# i) ]+ k8 p* e' J
</li>
0 d1 {& [# g# L4 w7 p
, T- X% F* J* E& h# c
3 i7 @' m* L/ f! u7 k
% q; Z8 p+ d3 d$ P4 ]1 O% ]
& {8 F. k- Q. N4 q7 V, ^/ Q& t  M0 c! l3 C0 T  G( \

* J& L$ @: P* B% z; ?9 v6 g</ul>
5 z+ Y1 o5 t$ ]<hr>/ W8 W7 }4 o" I6 O
<p>Leader宕机了如何进行选举?</p>
! B9 r, @6 j1 q<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
# [4 D/ o; v8 q! `8 w<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>$ u' C0 D4 B7 N  g7 t
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>: w+ u* B: _+ i( j0 t
<hr>
) m- U0 z( H. d& H0 m# |. C<p>选举后数据如何同步?</p>
$ ?/ Q% x1 P, j* q; d- q6 t<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>
; E/ o  s' |6 `* Q+ }' a<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>1 i9 a  {' V9 G4 J
<ul>
* M8 q  e1 Z$ z6 {5 ]5 ?<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>, J0 K8 y& X+ F4 h( R4 n
<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
: M/ t0 U' J. C: ?( {9 Q+ J2 ?<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>
8 V! ~& ], B8 G$ \1 }2 P! t; L. v
1 ?1 p# U+ u- Y1 J2 s3 o$ e9 c3 I9 Y# F0 h& G# e7 B; A# W5 r5 N

, A1 k' b& N* E9 ]1 b- o# U9 q/ Q3 _& V/ r+ C# `
6 T2 z/ r* A4 m; d. o' M5 ^
9 `1 L; T; @1 I2 E+ V) w' \
</ul>0 t# J* @, ~6 N1 G
<p>同步策略:</p>6 |$ |( x, O7 @  E& Y
<ul>. _6 x8 U5 k$ n; G) D
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
. ]- K, [% [4 ?/ _# a/ X- 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">首先Leader向Learner发送DIFF指令,代表开始差异化同步,然后把差异数据(从PeerLastZxid到maxCommittedLog之间的数据)提议proposal发送给Learner</li>* t) Q. e. j* \$ j+ K/ 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>2 Z: S  J: [  e1 L$ [* d' _
<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>
  a' a4 @/ p' P) C/ D5 I( Y+ I0 n! c

4 r  J3 m1 |0 ]8 r, a+ w% O. d4 C" J8 a8 @/ q

# Z. H! x2 N" i; R9 W</ol></li>
% c% c1 m' ~. C! {% C# B& z<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>
8 O  P4 j+ ]0 E<ul>
+ Z+ \& ^, ?- A' H6 a<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>
4 `; C$ N0 A/ j) ^! x! U1 u<li style="text-align: justify">2 q  I1 F! u5 A1 d# g6 ^  k
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
7 V8 g5 O/ I# J3 W. q9 r
+ D* @/ ]8 h" B/ X( d: Z- g6 I' K2 w+ r$ Q& l
; ?1 S6 ]( X4 Y  K1 {: X1 c9 q

2 |/ m; Q1 |; R3 t5 q</li>
' U3 E% u) G# U1 x  J. C; @, G3 l# I% Z
* E# D( |0 E3 U; O; ~& U

& o+ M3 X; z' G7 I; `( J# ?- }7 `. h  p  ^1 ^$ a6 J$ M
</ul># }# y/ a# X/ \* S$ u* N

: U: `7 d( P/ ]1 Z
8 r1 r7 T4 A) Q6 D: j- T
8 m$ {8 X1 @- ^% b/ C" [5 q# \
; c/ K5 [! I' {2 o</li>
3 A+ v" M, ?6 B" M5 Q4 x" B& o<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):% r! N" {8 ]4 Q' {- A! l  u
<ul>3 P; A$ B7 I/ Y  S! h' a6 ]* @0 d! i
<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
2 c4 \$ D: |  [  g7 L<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>' a, h3 h3 t3 m* a) \
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>, q0 m. r0 J. w3 h' c# E" i* N$ u4 G2 t

& o( ?7 d' w' _0 V
5 s, F- w0 ]6 ]
8 u: W. y; F! u: C/ E  o8 z3 P& P: O% l3 l, f
</ul>" t7 A1 q5 ?. R& V+ ^' f
# z, x! {$ M7 ^3 M
8 j' J& J7 q" d) i$ _8 v) L

) F' ^0 m- h! F( `3 {: L8 B6 a- |" T; _2 U3 i
</li>! w: p2 F- c7 L/ r* D" ]
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):& y1 c+ T" G  F4 W
<ul>
. ^1 _0 I6 v! P. j& ~  q, o<li>
8 R2 W! p# k0 A  Y7 f; r! [<p data-tool="mdnice编辑器">适用于两个场景:</p>
2 E! i: J1 o, \; N+ ]<ol class="list-paddingleft-2" data-tool="mdnice编辑器">+ D4 R4 O/ ]" f% L/ T
<li>PeerLastZxid小于minCommittedLog</li>8 |8 ?: U/ D; t# r7 O# G) i$ I
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>  p" _2 R1 h$ N

+ ~" J+ O7 ~  Z* c+ J  E" O  `& b5 K3 R9 i1 a; v% Y/ a
' h# r. F# x* D* G/ g

: t  h8 q  U* q3 W$ M+ B0 R" i</ol></li>9 O  [% t; @$ }5 |3 g! G
<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
! ~' ]# z) l# m- ?% K4 s  X' T/ N4 ~) p4 q3 F

8 K7 y" P+ G: o4 ~% u7 e4 |2 q
% x2 `) N" b+ O- Y! V4 c4 b3 {* u) u# l
</ul>
1 |' `' O9 {( u( Z; B- L1 V5 F1 v6 V: x. {* l7 ?: A$ {/ V
, }: M) L3 E" ^
+ J5 I5 O* @1 @1 d. R: ~
3 e7 R/ B; @. Y0 L" L
</li>. b* A! z/ z9 D) c8 V; k$ E2 ]% `" m
( m( v5 S" a1 y9 j& ~
: A# F, G2 }  t" P! K
. B  E" @9 p+ I4 J, S3 u4 F

" E# f6 y" T; O, H) E3 f+ M4 {</ul>
+ W5 o4 X" e6 `9 [; t* h<hr>
& [' T' s$ \+ S/ C5 e, r# Y<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
9 l- r8 H# e# |; m<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>6 Q; b9 d7 N) e& o* c
<ul>8 Y; k+ B  A" k! e0 t, U+ Z; Q
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>% g" w+ K/ t& n) M
<ul>( r; n1 Z; H2 n. D( V0 l8 c
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>, J$ u8 K# o; e" @* E* F4 Y9 P
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
# c$ z* a, ^) L0 e9 W% Y9 K& `& b

3 R, F; }. }% I  A3 t$ k- N</ul>
# O# d; ~8 f2 r/ ]1 n, i* k6 a& n) ^
& d! n" z% O# F: X* u0 y1 J5 w" H7 w# ?" ^
</li>4 F3 r9 d; j: M% N
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
% |, d. b3 j2 Y1 h( p$ \, U+ T<ul>1 i& W" W. o2 L, f6 |
<li data-tool="mdnice编辑器">
; g& C9 V; Z( q9 \  o0 }$ k( V1 s<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p># U$ Z1 Q: D2 z( Z- X4 @; L
% o0 @# M# }9 ?+ H7 D

2 _3 X1 Y0 t& D# ~9 Y. d1 \) }2 D</li>
2 M1 U' Y6 {2 L7 A) K<li data-tool="mdnice编辑器">/ S; @$ B1 }, W$ M* j, W
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
9 `# N! U/ ?( v5 l, c! U
5 a6 i2 C- J& [2 L+ k! B1 B
& q* ~& g# F, J* t</li>' I7 \$ g& Y: J
  A- ^  E' ~2 i) `) T/ ^
) u# j( \6 q8 ~' L" e, g; ]
</ul>
( p* C+ H/ F1 m# h- F, v
- T$ j4 N$ O5 ?1 Y/ S. b" _5 ^. L' S2 o; ]: g! O7 V! y2 j
</li>
& w( l2 i/ |4 S7 V+ V0 S* y<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
( A; \) R* u) b6 w. @) T9 g<ul>
( m2 E5 k* M; p) p! ^1 C<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
2 U( E- G0 [  a( h7 O3 b8 w1 x4 V2 S6 n/ B/ j* V
, U; R3 ~8 P& V! ~1 C" |
</ul>" C2 L2 V+ Z/ u; ?1 S, ?

, `0 U9 ^/ O7 u, m5 Q% Z$ S/ }
  Y& F  N, h3 z+ d</li>
$ V( \6 y6 K5 W& y7 U' Y5 @" c: W, ?0 y2 Y6 ?/ t8 _1 m! j2 M
2 d- t# D4 i. N8 A' ?2 d) e6 l
</ul>
1 a  U, u* `$ ?<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
/ O9 E; o4 ]# B7 A( H<p>zookeeper 是如何保证事务的顺序一致性的?</p>
; _, w& a. ?9 g) H9 z4 T) n, X<ul>2 ]+ M; k  r1 y' M8 w
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>6 z6 S0 F  x7 ]* q6 Z

8 P2 w* _! E3 v+ P1 \
9 p5 V/ X% l2 Z5 h  j- W6 m/ n</ul>( e& O/ Z  ~7 R: I: O; N* M$ f
<hr>& l4 Q! G3 a9 U; |: P
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
2 A3 v/ ^- `; v) }<ul>
8 C: W6 H) E( U0 I! ^& c<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>% l, |& s: `" L8 i; H! j, _

+ W# T6 g# M6 B9 m- [. Y$ S
" o$ s9 F% R1 V3 R$ t</ul># o9 u5 L5 W8 x. q
<hr>
) V% }; W7 g' n<p>说几个 zookeeper 常用的命令:</p>
* C1 e8 m4 |) k  y; z  u$ J<ul>: b9 q& |3 @2 ]
<li>ls path:查看当前 znode 的子节点</li>% P) b% J+ W: V0 {* o& e5 e% @
<li>get path:获取节点的值</li>
$ u9 g: r& \3 q7 l* i' ?<li>set:设置节点的值</li>
0 t# f8 ?) }4 f, m<li> create,delete:创建/删除节点</li>* X5 F3 Y5 `+ I3 i
2 F6 `& C, p, t# r8 Q1 h  N5 G

0 j0 ^5 u$ o* e8 J6 l</ul>
9 `& E. ?  ~% y  d<hr>
5 L6 A- M. @' d  g<p>会话Session:</p>
/ l3 j$ A  Y$ J5 p<ul>1 L2 J. B$ ]) _4 R2 x5 \0 z- R
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
2 W2 I# E( L8 V+ U) W
& ]! ]' K6 c+ `: C( T, i7 @' c7 w8 D
</ul>3 o  k, Q8 ~/ D- J
<p>&nbsp;</p>
* n, H# ]! M6 a7 o+ n; G<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>* k* f; t3 }4 H; S3 P  k# H. X: Q
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-9-27 21:14 , Processed in 0.098039 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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