飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

8117

主题

8205

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

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

6 b( H  r" A/ s2 _! t# |- ^+ h<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>
5 V7 {' I, r& Q# U! J; l# H0 g% I<p>&nbsp;ZooKeeper 是什么?</p>7 t. @) I0 j0 b: q, S* R
<ul>
# W' y6 D5 g1 w<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>
" S7 |) P/ X0 C$ D2 N( V+ }<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>
, Q; u) d0 J6 ]- @<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>. S8 p5 l; Q' N5 U. I/ y
</ul>
* b5 k3 W5 U/ v6 Z5 b4 `! l<p>Zookeeper的作用(应用场景)?</p>( u+ l9 c6 {# g/ w
<ul>4 A8 N, _: y, X2 u9 x7 X
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
, n8 L0 Z% L3 ~0 J' h$ u<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
% i) j7 b2 \: D( t, I0 e+ H5 a<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
6 B5 P2 ^% C, V7 h<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调用。! S1 }, b- v0 P
<ul>2 \  f0 @; t0 ^9 C5 y' ]- o+ w
<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
# b  s: N* |& [5 ]1 p9 x4 N<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
; I; z( ^0 H- I5 ^<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>4 c9 c% ]7 H0 q
</ul>- a% s4 P" ]+ p7 J+ R, v: E. Y
</li>
0 t  a# h2 V' r. q- B& S<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>2 M) h0 A, E# p+ ?2 E% c9 P
</ul>/ S4 B5 l8 X$ f9 Q) F% ~
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>! [1 H5 r- e1 P: F' c# P* Q
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
$ [/ K6 H9 m2 U( i0 w3 w9 K<hr>2 C) T1 _; s0 x( o3 q4 D/ H2 S
<p>&nbsp;文件系统:</p>
; X# |$ l$ S  ]: N' R! Q<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>& \  [* N) T/ S# H. `
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>, P" _/ d5 j5 n6 @5 |0 s0 E! P) ?
<p>&nbsp;Znode节点主要有4中类型:</p>2 O9 q. |5 q0 J# _) M
<ul>
$ c- e' Y4 `$ Z( ?; d5 W5 r* R<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
# Z/ N! A9 {0 U" i7 z6 p0 o% q8 `<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
# G7 N1 t) k' h$ @6 S' y<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>; Z0 W  u2 I9 c6 C- G9 w; w) D+ k
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
5 S+ O- ^( G7 k+ K+ I0 b" o</ul>
/ G* A# l) m* \+ V* Y<hr>6 R$ P* c* J) e
<p>&nbsp;通知机制 (监听机制)</p>
7 w8 o) B  }& @( ^4 a<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>4 t" ]$ n: ~( _; [9 c
<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>
" Z& A& }9 l: p6 ~<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
2 s( D6 p6 Z* [/ T; g! D- R<ol>
! f$ i9 j/ }! d4 _<li>客户端向服务端注册Wather监听</li>
) m* a8 l4 X' Z7 l; K<li>保存Wather对象到客户端本地的WatherManager中</li>
" D8 v# c) Y4 s<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>3 r! F* K3 S4 I
</ol>, G$ K- `# h4 m
<p>&nbsp;主要监听2方面内容:</p>4 r; k# f% E' L7 Y4 a% ~! d
<ul class="list-paddingleft-2">
# E& u: M: D$ U/ g  M<li>
/ V) M. s( W6 J' ~! m- h* c<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
( x" r: S: l) I1 z; n% T</li>8 {; n% K& H( B3 P4 t8 Q
<li>4 I0 e1 U: d, g' C$ X! _+ r
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
2 Y7 ], W  \5 h: q4 e) m</li>9 O! W, y& u' |& j( y* t8 o: k" @5 s* x
</ul>
- d+ G$ @, g' r<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>. F1 S3 P9 O8 n  V8 L7 M& D( b8 r
<ul>
$ R8 J4 ^& w" V. d<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
  i9 }$ q( N+ C% r8 D, i0 z' u$ R. i<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>8 D2 Q% E+ j5 o# O; }0 j
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
$ o: s$ X! R# k</ul>  L9 b8 D" {5 r' z2 u: }
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>0 E4 Y- Z# W% U- f/ p4 {+ c* Y' o0 y, r
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>
0 t$ K3 D7 E0 x% {<ul>
2 i& a* A, |1 J$ r) l<li>Leader:负责写数据。(写数据都有事务)</li>+ q+ x: g& D: C9 l. H
<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>
3 A+ ^$ j3 \" a  t1 m5 x- n0 l<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>4 ]& g! o# R5 \$ Q
8 K1 X, P9 W6 P& |5 e/ N  ?) u( [
</ul>% W& p9 g8 \6 I; y: ^2 p
<hr>
" n( |. F5 z5 |0 b  M7 ]. D<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>( Q1 f; T! R& f; m- S* L' K3 N
<ul>) k7 K; {4 c5 a# I/ B6 \/ {4 ]
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>. K5 p% G$ p" ^" y( T- |* N6 k5 J
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>
! `7 f2 O7 b( j+ h7 T8 B<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>! C/ ?7 Q3 t' J. a/ G
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
& ^- R/ B2 A, m' s$ Q# v1 A# z8 B' o- b7 e
</ul>
7 _$ z7 p. W* S) ^<hr>
( ?! P6 ?! n& f  ]<p>其他概念:</p>
. z. Q) Y& X/ L4 ~. t<ul>
' y* S) G1 h; V" j( a<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:) |# \4 M/ E. w! Y2 [* n
<ul>
$ b6 j8 I  |* \/ u<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
; m/ O& ]) ^+ S/ ?# P<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>* l  k0 [+ }( M7 v( \% A$ e
# T2 Y& ^2 k/ ?: f" b7 l
6 P4 S; E0 X$ H/ N9 D. y
</ul>
6 W. k- s- |4 A) y8 N3 X9 J
# I! e! B# p6 ]5 _
0 d8 B! W+ O7 A</li>4 t4 V' S. H- ]1 _5 s
9 Z+ K. d, l" [6 V- u0 u1 o
# w" v/ _* y4 [7 ]/ p1 c" `
</ul>2 z! H, b9 C6 n: D1 ]2 Z; M
<hr>; h* v* E* C: y* q% F* T4 V
<p>写数据原理:</p>6 g/ N4 r7 w0 V4 \- ]% @, A2 d9 m
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
% L' O# _& f3 U3 \( t<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
& r1 U0 g. Y+ ?" ~5 y<ul>4 E/ _8 ?4 N* L+ L
<li>写给leader,leader再通知其他节点 </li>
' E' C2 t/ Q: _- a: _<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>- }+ z- Q( U8 |) u6 i
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>
2 u, V3 _; T  g. ~* n2 M0 W/ Y3 I: V/ S' f# C
9 B5 t4 P% A$ C7 c" Y4 K
</ul>  n: n4 d" K, L5 B3 _3 `- b$ n3 s
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
: \/ Z. n7 e0 g" r, x; D  L! J<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>( }0 O5 t% E" }7 k3 D
<hr>
* J- F2 Y; |9 k( Z# B$ G$ b<p>&nbsp;ZK怎么保证数据一致性?</p>
+ y) T0 I, F7 G$ _7 \# 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>1 ^7 T6 V  U& @/ U( H
<p>&nbsp;一般我们正常是消息广播:</p>( s( J. E2 E, a+ D) }: C2 z( U
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
, i; K$ J% |$ _. m8 }<ul>
3 T) B' z( B$ O* C5 ]5 n<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2% b& l( i* Z5 m& X/ b0 h- D6 Y
<ul>
7 @" [1 W9 ~! B, Q; @4 Q<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>; S6 y6 m! b+ v0 ]# S0 h
<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>
, p7 k8 Z; @3 a, C  v
/ |: F9 @9 ?* S8 P+ w2 }. }: L; Q2 s$ B4 n& Z

; i  ]& x5 B! u+ R7 {. E* h
8 I& i5 F" o0 }; ^( f  r& q% ]1 Q/ d
: K& B8 f! L: g/ {0 v
</ul>, S" C+ `! b4 f1 k' l' w

* x; Y4 R7 T# }* ?1 E, s$ @# ^, G  ^4 a# q6 I/ W8 v& V- J9 y
3 |$ P% n# @4 i4 L( ]+ ~
$ W. ?+ t9 t( j+ x, q3 n

) ], G8 Q0 a! S8 }
% X2 [+ p& H  m& ]* }( K8 j+ T</li>: l* c; L5 G2 j/ i) F2 h- Q
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
9 ]- S! q# j: @, F<ul>
" ?7 i+ _5 F# G<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>' U" {3 }. M9 C7 T+ p6 T) q

7 O/ @. t2 @5 j; m# \
& g; R. s  H. D$ J" y+ r; |0 q$ @2 O! t! e9 P

+ [6 _1 q  H- O# f3 q: f, a
  O* W6 C7 @) P3 I+ g
* v; E7 v3 M& S" R& ^& L7 ]# i, s) Z</ul>; L6 `% s# d4 Y3 n- S2 h. O
" ^* I% t# P0 w8 s) y3 K) ?2 u
4 B% U3 d. A; i

% i' ^2 E/ _) ?0 Y; J9 l* n, F  i# Y/ Z: s7 B+ w7 ]) D2 Y

4 F7 j% i/ g: o3 m/ u5 Q/ W
5 k/ @) h* X6 I- \0 X. e7 x4 L</li>
! E7 G0 L/ r9 _; a  a  y, S. V" s1 c

: [  h3 A8 n+ ]; I  [& k4 M$ I& b5 {$ d) U

9 G- ~* l# G4 T' {' Y- U7 M5 V( m
, E; h$ y; c" w4 |3 d/ j  X
</ul>: d' M- L; d, S; p% [
<hr>
3 b& X4 A2 J, q9 T( ]<p>Leader宕机了如何进行选举?</p>& T0 k" e, q0 ]5 j9 B
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>2 w0 V8 G2 g$ h8 b0 a
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>
0 ~! b9 c- d3 e) ]3 S<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
* K  V5 R: @" {5 l  R/ U) p9 M<hr>
1 G* |% e$ r, [( L  V<p>选举后数据如何同步?</p>
8 Z. o. n6 e! P3 P* ]) l7 k<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>. O3 B5 @& F6 F1 c- x2 r; F  c
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
, E8 ~9 _5 \( r6 q5 `$ z+ I<ul>
& f1 t6 F" {1 f<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>2 P2 v  ~0 [/ ^6 c2 y! m$ T
<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
! E) D7 U$ z* m! s2 ^<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>0 X5 {6 W0 `& z; v* ~7 \0 F

' d5 ^# A4 Q' O) ~" H# B) [' P# X5 x, S% H6 V

4 z/ A& P- `/ T# l
/ ]/ _: R1 k. w6 y) l7 B3 {
" @% {+ ?% B* v3 W$ g9 T4 M5 l. j/ u, D6 n4 h* m. r$ J
</ul>' ^( A/ u8 i2 |: K, z+ Y
<p>同步策略:</p>
  z* k8 v2 y- a( `/ l<ul>
8 D6 g% d4 b, D6 P0 u<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
' W+ C8 \6 h  W7 M3 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">首先Leader向Learner发送DIFF指令,代表开始差异化同步,然后把差异数据(从PeerLastZxid到maxCommittedLog之间的数据)提议proposal发送给Learner</li>, V% I4 f: \" @1 `1 d4 U% D5 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">发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步</li>& U% f, c6 O) A5 Z  B$ j; \5 C
<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>
% z: R% f) @' w0 V( r; i
1 T2 g" O7 O8 J+ R, s3 o
8 K' F5 n' w8 y" F" t% D
% F& R+ }; r: t$ E
- u5 ]7 @) v3 I; f</ol></li>8 g( \  Y7 x  T/ ?7 e
<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>
. G' A0 C+ P: ~6 `8 h<ul>
, e- ~1 K6 A) K, g* 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! M: i) U3 y& J0 Z
<li style="text-align: justify">: V2 d7 ~, E! t% p' L# }( k
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>" J- Q' t9 F$ b' h( r6 L
3 A$ s1 B5 E( e* `

- e( S- ]) ~0 m' L* f! f8 K2 K: L4 t, S( B, r0 R
4 l2 ?/ l1 b' N% s, b7 n- o: r
</li>: Z  _! \; \# D

0 H. g8 o  t6 V" E0 z
+ G* |2 r0 [! [! `
0 O7 U2 k) @& \
0 \$ u# ~3 r% o1 k</ul>
" ^7 v9 ?6 q2 `2 i
9 {1 a7 k. ?! w" M4 S8 V
8 x3 L' Q; y0 z. ~+ i% m7 ^
9 _$ C; i% a( L. ?1 i( v
1 W7 J8 F2 B% @9 y2 e2 q</li>! B' Q8 W+ x% n$ F* H
<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
, L& w' C. g7 u8 a* G<ul>
1 ^5 n) h; B* g5 F( o( K% I# x<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>! w1 ^, \3 N) ?- t: x6 v( F
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>, K: F4 m4 `! c+ K. T. B+ t. C
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
( }+ v, ?! Y  b  `- [2 O
7 w7 O3 V- L: Q- i% |7 ^
1 N1 ?5 V  j" k. W- m( w; s0 J/ ?4 K; C  c' Z5 D
5 t3 e$ m9 C' g1 }4 U  Y7 i# ~
</ul>- m1 \% |) t1 a) F9 @

. ?( w2 Z6 w( f6 m4 K
* ?) n# A& y7 h5 C% ]% y7 ~  h) w6 s9 f
7 }" x( T. r3 U  {& l" a
</li>
6 a. J8 z7 ]: Q0 [- [8 H, @<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):
+ t: @/ d; z! ?: q<ul>& Y% ]3 J1 B; C& U1 X' g% B+ R
<li>. Q! q0 V: t. U1 Y7 z* _; d) P( [
<p data-tool="mdnice编辑器">适用于两个场景:</p>2 v' T8 L5 r) L! z$ V0 @, N: c
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
" k7 w9 _: _3 T5 u+ |( t1 T<li>PeerLastZxid小于minCommittedLog</li>6 d# Y5 Z# x$ p. v+ u8 l
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>3 H; C- j% u2 B) O' J, {8 {

+ Q! ~2 @( Z$ h# N; K( h' I0 A. a

- d, z! T7 I8 h. }
  P3 N; R5 m8 f3 l! k</ol></li>
0 i5 F) ?! o/ F+ \4 q( r<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
3 r. m5 h  [: R, v- S  C8 D! r* I+ x- x4 U  M

9 {* z* v. q# R, ~, ?  G, Z
2 B% Z6 g1 w) S8 }* |( t. L; }( v
. D, T7 O, ~7 D1 m7 t) g- H</ul>9 m# j. Q% G: N/ C9 N

8 f1 u  Q- m/ n7 C$ N* e8 Y9 i
4 d1 m7 K; o- J, t! n
) U; a2 f  n# l! Q% _  O2 Y/ ?- i% z) J' s0 |0 @' t$ n* Y! y
</li>$ b7 s+ n9 e( j! E
) Y; q! d1 z* G, `

% Y9 n3 s, _+ x" E3 g7 S- P- e) Z% D, J8 v: z4 b* U7 w  M% ~) G

: W% D' [; C- x</ul>
$ z* f% K1 N% F  a' G<hr>
( Q: x/ w2 F7 `. ~& q6 a<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
6 }) ?% j8 ], H/ p8 l& G<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>; z3 I4 o* ?4 E$ y1 h$ c
<ul>
- W$ ]$ F( Q! V" B* w2 |; Z5 {. s<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
  t' f5 n* v: `7 ~' n, M7 a<ul>! V2 l6 |  }7 T: V  z1 O
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
2 ]( T/ Y: c- a<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
( Z% [9 z- T* l8 B- j( M# \
6 m6 M$ X: s4 l/ s' c, a0 E5 L+ }' o3 {  k0 Y  E
</ul>
" x! R, B6 p! g; {+ R& N8 I& _6 A; c2 t- I: n
' z8 b! ^+ _% b  I( t9 b
</li>+ e8 V, J" k- e; B% V" m6 w' v! B
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>1 @9 A( e6 T" Z$ V8 v
<ul>) I9 H/ Y' q4 @2 G9 w
<li data-tool="mdnice编辑器">( q, b9 p# ~7 o1 `
<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>- t( D' y2 y( J# E! i6 L, [
4 c4 }- V% v7 R( }' q
4 K9 b. d. W4 M- n
</li>
3 P$ q7 L& R/ M) Q- m" o6 {<li data-tool="mdnice编辑器">; i# G9 w' V7 p( f
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>* `$ ^7 n( H5 n! `! |

) Q3 d$ I( l0 P) v# c) P2 `- u$ E* p! I. `" T6 s: M
</li>
3 T5 U( s9 E( D" t7 g; V2 P/ p# V: K1 J8 w+ h  X
* r  E# N# j- N" z
</ul>
$ ~  s; q6 V; v' Y5 Z' V+ n6 q2 x2 p; L. p% a

( \# u' P3 v, g</li>8 a7 l" |. l& ~  x
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>- e6 B1 P& A+ }; \
<ul>
- Z& ^; `( T0 |! p8 |9 B<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>3 E" f9 }& A9 l( G' ]7 i
6 s3 O1 \" r7 \( R/ X
) W! X( ]: ^3 ]' T2 Z
</ul>
6 f; X3 w' R. G, d) N
  i% E' d9 L: _6 D
4 K4 W$ f9 n* B6 C' ^</li>8 G) l5 u7 i9 v! j" m& G: B
: Y- a- a: |9 z, G
) q8 h8 {  v' P- K& w* @! c
</ul>7 i6 G0 ~& T: t- A: \  f9 a6 _5 l
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
1 {; \2 y* m7 G<p>zookeeper 是如何保证事务的顺序一致性的?</p>) ?- `# r" a+ A# {9 F7 q
<ul>* ?; C6 z  H: X" R& y. ~
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>6 g; [- P8 V) a7 ~1 q1 [/ q& y
# S1 g: R$ g8 F; c
1 G8 Y, [* {7 Y& Y- ^; {7 F4 `
</ul>
# j$ h4 x0 R0 a+ ^! N- c" X# U<hr>8 |! t! A0 P& Z9 {
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
0 I4 Y1 @! p* x& {. ]<ul>3 q: c+ A8 O( J! N; g$ s9 ?
<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>% p  |) G$ J! D$ X
% ]- C; `5 w# P; q9 Q/ F
4 ^7 B; R/ o! k& X' B, @
</ul>
* J8 P6 U$ b8 I/ P8 w4 |; p<hr>' K0 H' w* n* `* n, h: z
<p>说几个 zookeeper 常用的命令:</p>
$ Q2 d/ j! j6 L, W' ~1 S<ul>4 M/ F8 E# X4 I  g" A2 p% n
<li>ls path:查看当前 znode 的子节点</li>
7 F0 X; |9 r# o# M/ g7 c+ f5 j<li>get path:获取节点的值</li>
( s9 i/ T7 b0 k<li>set:设置节点的值</li>( m$ ?+ M+ g3 ?# o' K7 T/ W
<li> create,delete:创建/删除节点</li>
* E2 G* |6 S3 j( w( @" T+ ?. |, I+ }8 D3 t- @# x8 c

! g5 U  G) Q/ g0 U9 P' H  W* S</ul>
( B9 x6 c; h6 g* H8 l6 m<hr># S/ ^- O, {2 H+ o1 v" H- H- e9 s
<p>会话Session:</p>
0 p  M* u6 k  z<ul>" C" @5 B) F" I- _4 ]. q% j
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>3 {: x' h2 H& N5 ]
; Y% S5 J* y0 q6 L/ y, |8 R$ ]

# U" C0 R1 f+ _# d% X</ul>
6 R6 O5 V4 y: g% N; m<p>&nbsp;</p>2 ~. Q) c$ W( u. N/ O
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
  s- @( C& S. e
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2025-12-22 17:37 , Processed in 0.066425 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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