飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

9208

主题

9296

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
29954
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
: T& b+ Q" J: S! e
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>
7 `7 U* C& G" o; l2 T/ K+ G2 a<p>&nbsp;ZooKeeper 是什么?</p>" I+ t8 j! V7 |3 j( y
<ul>
  U6 q# G( A: `& D4 D<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>  X" n) m! W% A4 O
<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>6 j( W! `6 v1 X6 {! P
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>9 X8 l- G. t* n# `3 D5 E$ q/ f
</ul>- A3 [, ~% a! r& U7 X* R/ p
<p>Zookeeper的作用(应用场景)?</p>  W3 v  _$ Z; n5 v0 L
<ul>
0 T9 X) t8 `4 b1 O( E# ~2 e; x: S<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>$ B3 j/ J( ?+ f9 n1 ?
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>* g# y7 o% u+ T- v' }* y  |* W
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>- B% v4 g$ I- 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>就可以通过ZK调用。0 a% Q; ?7 Q' H6 u- m+ a
<ul>
' P8 l3 d, m8 S: A% z<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
7 s* z  k& h. g+ N6 {4 t* J<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
* M" R1 g2 j4 P<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>
% u9 X+ a3 V/ l2 m( b. P3 `$ K</ul>9 d3 ^' @. V7 p
</li>- M2 D+ p* j, ~$ J' G; P) ^
<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
% C; z& I* F6 u, V  m1 x, N- B3 _0 L</ul>
# o7 T$ ~; }. g! m<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>+ o1 m  {0 X8 Z4 `( @- q' ?
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>) e! B; ^- w" w9 h# _. T  t; n/ L
<hr>
% m  J+ k* @3 h& r<p>&nbsp;文件系统:</p>
  Q% e! C- a$ s9 p0 Z1 `% L<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>
3 n9 {/ q; P6 O& J<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
' F4 N) i) W# Z* }  N<p>&nbsp;Znode节点主要有4中类型:</p>! B1 R( W- M2 e+ f& K# |$ b
<ul>
  @1 i0 B9 \& f3 a4 r5 M) [# i<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>9 A* L( ~- j. C6 m: u) I% p
<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>6 A, F! Z, N# V: h( I. b/ R7 d" d) l
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
+ S2 M& E9 d% ~! c" L/ Y' A4 A; g) Z<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
7 V) P& c- S# |- z8 U</ul>
# ~, g1 Z1 n+ I6 @$ D$ A+ u<hr>7 ]; {5 m4 z0 `  O
<p>&nbsp;通知机制 (监听机制)</p>1 d) A: ?. c2 g5 @. U# M
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
; @# [' B8 U; e2 _% v& e<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>
( ?7 K7 H& M3 K" r1 a1 D<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
' S% B- b; b* d% Z# Y+ [, j7 M* H<ol>+ b* t5 I% I, ?6 |# D% r
<li>客户端向服务端注册Wather监听</li>: ]7 N. a; H4 d! B+ o
<li>保存Wather对象到客户端本地的WatherManager中</li>3 ]/ }8 @3 \' }# M# A6 n% T0 F5 l
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>% d/ k2 a, m& _# f
</ol>- a9 q/ ^* H) G" `+ v. a- K$ F4 O
<p>&nbsp;主要监听2方面内容:</p>! B' `9 W1 N; `) N, g
<ul class="list-paddingleft-2">
6 M4 ?; t; q! U# Z* ?- S9 V<li>1 x* w% T- y& q1 R
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>3 u$ X7 w5 C2 `
</li>" u: I! T. }  p$ `
<li>
0 I4 E7 s+ L! }6 q. k" Q<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
* z, O9 Q7 E$ \</li>/ E7 v5 W% y5 O$ b8 W
</ul>
% S5 [; w. u6 l3 K0 a<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>
3 u1 \' e/ D! F, c5 k6 Q<ul>
; V7 k& G; I  W" `<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>1 I( r, Z8 r0 X6 z# p1 c# z) I
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>2 D' w, j; x: F# }0 U( W2 d
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>. e+ R/ F5 y4 [% \2 t: B6 y6 J8 s
</ul>
. V2 o3 r% f% h<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>( V! z/ S5 J4 x# y( M9 V9 ]8 T
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>
7 E- H- p/ D% b; C' e<ul>% c* \4 L7 {! x- W# {! k
<li>Leader:负责写数据。(写数据都有事务)</li>* H3 R  T: k  Y
<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# X; @( _4 |<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>0 z* _( x8 O( g* N8 |4 t3 e9 K5 ~
; v9 C( q% s% V! z" n
</ul>0 S, y" M3 {, e) W2 [5 A* d" I
<hr>* d9 B" C- M% J' w( s1 F' @
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
+ F6 X+ @' T' o- k6 u+ v<ul>
$ \6 ?# L+ |5 y* F! `<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>: T) F3 P3 w9 ^+ R9 E; X
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>( w4 y4 a, B$ g
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
4 G2 A; {6 b" N) D9 S- ^9 Z<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
, @) S/ n: h0 H, E* n3 j
9 ?- ~# f' L: `8 B& i</ul>, P0 C+ V: i7 N: s
<hr>
  K2 f7 T# k+ m+ e# X/ b<p>其他概念:</p>
' k( e' V' ^* c' K# ~: V; D<ul>
( j$ C6 }# ?4 O6 t<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
2 ~9 L7 D! h! U" v<ul>
0 @' y( a# Q" D$ \* t4 y<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>0 p2 j8 b2 j! j9 V& h5 Q
<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>7 h7 O- [1 a( H$ u" N7 S$ N: w

0 R  v) @/ r! L7 N! m7 p8 A
3 w' B0 L# Q, X" o, D</ul>' \' B* A* i" n/ i  S1 V, m% L

% n$ @) ]5 q- c: t8 [$ Y$ L
3 ^/ j# j6 o* G4 R# X</li>' I- O% @$ [! {& [& B, m

$ c  X* K) x' Z, q/ |3 ~6 f' O2 u
</ul>
0 f" k1 S8 J8 x$ x6 T) U8 ?% |8 J<hr>
6 Y3 T# s2 M  J  [  ^; q  g- \<p>写数据原理:</p>4 o# P8 P5 Z) x& |; I0 h/ P1 s. ?
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
6 o6 Z% \& N+ r- }' o7 Z, T<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>' K4 y: r5 q7 t0 G6 `& B: M2 i# i
<ul>
) o, x% H& S+ F; o+ @- Q<li>写给leader,leader再通知其他节点 </li>, ?- O9 Z' X0 [  M  x' r  K2 F6 x1 O7 W
<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>7 P* z2 B8 ]8 {3 p$ I) K. l
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>  u) Z  \- x1 l+ k
9 A- W1 ]. s6 @: q8 C, b

3 @+ P/ Z1 h% t3 c7 a3 q. X$ S</ul>8 y/ T) s- h: Y* P' k
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>; [3 x1 G. c  Y
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
$ b/ T. L( I- J1 \; I<hr>% z  z5 h+ _! m$ J$ q& t
<p>&nbsp;ZK怎么保证数据一致性?</p>0 z% V; e' }8 |! w
<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>4 {/ ?: {, w! r* p/ w" n
<p>&nbsp;一般我们正常是消息广播:</p>) A; O, S; h" ^& ^  R
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
/ z" C7 @# {8 b) I<ul>
- K! q  A1 r; B1 r# S<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2$ f; H3 a8 T( x% S) o7 ~
<ul>
- S) N9 D( z! i<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
/ t( p: `7 ?2 h1 g' O# h6 i<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>- I7 ]. ^6 x' U: A; S: U% `

$ D7 P; v$ Y$ ]+ |' b$ n+ g: n# I
- Q7 z9 B. p6 h! r7 M) V4 ^  |
) ^- G+ `" L0 v% E/ c4 s# F2 Y6 [, R, f- z, w. b: _; I4 A+ W
: g4 \9 j) t7 |, E
% U1 b! Y4 ]# u9 O, m, Y9 c
</ul>
( d) i# R. R& ]% Q% U5 F1 @- R& P8 R# y9 i# f
. h: v. J1 @. Z& V8 ~- F
9 Z. b7 ?2 g  l: M& I
* M2 D6 Z/ o5 ~0 ]/ f

. P6 X( P/ ]; u0 x5 e9 W9 m5 }. ~6 o  Q
</li>6 k* _( U4 X, y# {* F
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3- d4 p6 E1 S0 v) j  X% S: b# ^5 ?# b
<ul>
4 `% M' i$ \$ \3 `3 {1 L3 U<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>5 j# ^8 f7 e8 e4 h: V

$ a. h% x& d4 w5 q3 q6 K
  X6 f- ^- \- y8 p- ~
+ [3 z. {+ o9 p6 {2 p% [4 O; N1 G3 Q& a% t
. t3 {& u" f2 H8 \3 q) U* O" E

, ~1 Y! q% S/ C" Z2 w- A9 P</ul>
9 @% R/ a6 q' X' {) m7 y
# T; g( l' K% @
! `/ o4 k* K- p) i( X) i& ~  X1 X
  c! p3 |7 s8 F2 \6 {
' h3 s1 Q; D1 |; w% a9 F

2 g; C7 R. x* b' A! ~" ^9 |' |7 j/ n" q, G</li>
9 Q9 J+ D% \* t# m4 ^. f4 d$ K6 X8 g* _: M0 P

+ ]4 ?, Z3 p) Q* I* u* r- k% @4 }9 O% w7 p8 B
8 E' Y; `3 f0 P

/ w+ a0 I/ I6 Y4 ?4 M, ^* o
/ v: d; X% L' L3 c; [/ P, U</ul>. U  V. Z. U, h0 J# a
<hr>
. B3 l9 r4 Q* k9 ^" i% B' B  x8 f4 [<p>Leader宕机了如何进行选举?</p>6 w2 q3 `3 t: X5 E. ^/ {
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>) Y$ W- t3 Y. J. s6 S
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>
* c. t2 I% Z- {, y/ r<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
1 J3 v: s9 Z) s" l- y# s4 c<hr>0 l4 P4 |. Y( n2 p% [' j
<p>选举后数据如何同步?</p>
/ k1 o9 f) C1 c0 `<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>) o, U+ v2 d, U" s2 s
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
5 w1 h) L% O& N) T<ul>
. s. N( H1 V3 i7 r3 X1 T8 `2 T<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>2 y0 P  X. P0 R) y- M8 ?3 T
<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>4 ~$ n# G# c6 h, n  K+ S
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>" Y0 {  P& e  ~

% W; F% N; }. p+ L0 Y" T- g, L  G* Q# J+ m
9 y2 T' K3 ]! X8 k

2 u3 D5 \, Y0 f7 [3 u. f! [% m9 v$ `. x
& Q7 O- C: m/ o
</ul>
) w5 A- m6 v6 s, Q+ a: ^<p>同步策略:</p>
& [3 [, ]5 l2 Q$ `8 I<ul>9 v: e4 E1 s; v" z' g7 q
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
0 [  m- G( X8 G5 t3 J0 u8 M<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>
3 @. J1 ?6 h; a: j<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>7 m/ t- h2 M4 @8 Z5 ~. n6 B. n  F
<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 X+ K2 w- J# ~

0 w/ S5 L! u1 ^, s4 V" n5 R- _8 T! N4 @  o0 t5 @

5 D; O0 f/ S! u
0 P0 g. R5 X0 l, H$ M" M7 a</ol></li>
9 j% a& w  C6 f' d) ?' R<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>" c. f: a4 L* d) r, L4 z" b
<ul>/ S. T0 ]# J  A9 @
<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>- f& N9 R4 H$ ]( @" E- X5 O
<li style="text-align: justify">( O% u7 ]4 J# _6 I# w4 H: m, E/ _
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>+ Y1 V5 P. w3 {/ Y5 p

- D7 F2 a7 \6 |) v* i1 J; l3 |% d( s! \: g

' o' [  X1 l) J- C! _1 W9 w( D  }
</li>0 B/ G9 L' s7 P6 ^
. @, A' E+ M2 D3 p' Y- ~

9 |9 W$ I8 b  v4 W9 d' R- E) B8 T* i# |

$ F9 V4 m. |/ X( o0 i* \% X</ul>
0 E# x2 A( U' X( h* G& }8 i: s3 J6 C8 u
* O1 R; X; G" a7 X+ F
+ h$ D+ N( M# b/ l% l
1 |; |( h  a/ q6 q) G
- s5 d6 K5 \5 o9 P+ Y# k# k</li>
" {  ]; W. p8 c: B<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):# U- j* v" e6 V% R" t& F
<ul>0 }4 v8 V+ t* S
<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
0 L% M+ _1 Z8 N$ ]  H<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>5 k: }7 |& \( ~2 W+ u3 M
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>, K7 U: g9 `# W3 K; S! g# Q6 ~

( U7 `- l0 a# C7 c" B
" J. ^3 o5 d, Q. [& W  j# [0 w( x, l' G
4 o  `- S/ ^7 s4 n7 j
</ul>
* I" z- _0 c8 W8 A) w4 `$ ^! _0 I) I* ^1 A4 {& {! W4 P: W2 Q# R

# h0 w7 B0 O4 X% H
5 ]" {) l3 O0 ?+ x4 L/ q
+ I4 ?- O+ m$ A' e% J: n1 g</li>
% V& ^2 F! @+ j# V' }<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):2 m0 D: }6 n9 h5 J$ L: Z. \
<ul>1 p" N# P, J$ r% A6 F8 M% n
<li>. s- ]  y8 z2 z0 \. U' S% {
<p data-tool="mdnice编辑器">适用于两个场景:</p>/ A4 c  p/ v( ^& l
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
1 s. s" i, ~  S! s5 H! j<li>PeerLastZxid小于minCommittedLog</li>
$ M# U$ q# Z8 S5 T) M<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>) o+ K% G2 U2 {2 n
, Q" X- y: f4 P* r& Z9 J& ?
; G( O; T* I0 u5 I1 U; X) o
2 u3 c  y9 x: i

& L( f3 a; n8 {( g  I( R</ol></li>- A7 R8 O# o+ N+ }* C9 U( R
<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
  E- A2 ^. A: G4 O
1 W. m4 O" L( b, k$ w1 K; j
% K" L: j" t7 w/ Z6 y6 ~7 V
, g1 i2 g* P& g6 r4 E5 q$ s9 Q! }4 U# X9 B* K4 S: |
</ul>
  d& V( N& y" w, J6 n; V3 ~% \/ a
, ?0 F5 G8 z: Y! H: `
# K7 X8 m0 |( [4 m" {2 l. F7 \6 m+ _' t6 S* s. c4 R4 V1 i. I) k

  f3 @& p( N' a& i$ [</li>
. L9 G2 V! F1 h2 k3 ^7 x9 `8 G( ^) h. q/ j6 A

3 C3 f: V" p) u
& a+ x$ I4 a- G8 @4 G7 G
0 k/ Y* l. z( e3 Q</ul>+ `" r5 U8 t# {, c  @. \
<hr>; i  w5 v1 |$ ?: D) @
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>' K- \( M# A* P* x2 s
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>, `/ @' y- {9 P$ ]5 w. H. p
<ul>  P* R" \8 D! f1 _4 u) Q
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
% l9 Q! {/ L5 N<ul>
( _8 w+ V; n+ f7 d2 {1 Y+ S<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>( W) t+ ~7 }: |4 s) l! r
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
* r0 M6 w- E: Q
" T  C% Z2 ]$ M& {# J5 C$ p0 k5 E9 e% L
</ul>: S( \2 H4 F0 J6 o9 v
/ e9 P- \; }: k* f5 r

* y2 M' G& g( K8 o) E. {</li>
8 {/ s3 f5 e7 a6 l% z; ^<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
" i4 P/ o# B, ], _: J* N<ul>
) Q4 T: N, K# F" R<li data-tool="mdnice编辑器">3 Y5 X3 B- ]8 L1 ]* f/ R. H
<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
/ `% L( O' c8 B
+ c: _# S( K9 L! _8 p* K4 g9 X* z* @1 X
</li>
8 v* Z/ N, G( e8 v2 S! b2 l2 r0 t<li data-tool="mdnice编辑器">
' `* e+ i9 u' r+ E% ?) |<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
# U6 r9 b! I' o, z& p3 T4 S
7 B/ ^# \6 e" h- a# S
: T; S) j4 J7 P9 W, e  Y+ p4 m</li>8 b  S8 j% J* P8 Q3 j; A5 K

( X: I" Q, W/ F0 n, `) T9 i! I
0 W  ?5 y) I) w% Y2 ^' |* S- \! [8 v</ul>
) ^+ Z1 s( j: f& Y7 `9 K2 Q, g' K) M

- F8 A! n1 d$ J, y4 R</li>1 n1 S: \. X* [9 ~) ^
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
" ?, t" f% y9 u. a8 F<ul>
  a& `5 b" e8 n( r5 ]* b<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>( l" Z! p) X# c: G( h) S
* f1 p; e9 u. P& @) V# R5 \

0 r+ H9 l4 E/ |</ul>  Q5 [) V4 _7 n' N( @
0 ~' y/ d7 r* o1 `. Y7 ], t

: s& l- G! t9 A; ^/ u) O3 _</li>
( J' i' d! ]& F$ Q! k9 `9 o& e& y( D, l' C
; W" ?+ Z: x- y0 x5 l; A+ b! M' C
</ul>; F# \6 I, _* M' G+ z1 U" G
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
* l6 ~5 N  J3 @; l<p>zookeeper 是如何保证事务的顺序一致性的?</p>7 W# K% b) w4 X/ @
<ul>
7 p" ^* G6 P, Y0 k" a: G( L<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
* K7 @& v! W& ^$ A
5 }! H: f4 b; U. I' @! B0 B% o/ C2 B, z0 o, X- h
</ul>
- U7 k, G  k( j$ X% ~- A0 c5 m" W8 g<hr>
3 C2 {1 O0 Q5 R& d<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>6 m5 U' S2 [* q/ f9 E2 G! T5 M
<ul>
+ Y, ~8 E9 [. I! w" U2 l<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
% p$ k) i" R- y# N
/ y0 Q( _9 p9 c4 o3 `9 K, A7 ^& x% e' N3 h# M7 M9 p& ^
</ul>
1 j" d# f: O! h  u5 @' ~2 y6 U<hr>
& y- |3 W% d( I: f" y: Q( v6 @. ^<p>说几个 zookeeper 常用的命令:</p>
2 U& o* ~3 t& Y4 b' b; ^+ s: b<ul>
. O% I  n3 s0 H; J( U<li>ls path:查看当前 znode 的子节点</li>2 O, ~  A+ A6 g  a4 K' w; t
<li>get path:获取节点的值</li>- o1 S2 Y: m! z& K+ c+ t
<li>set:设置节点的值</li>
4 ]$ h: h& ^: U6 o! v<li> create,delete:创建/删除节点</li>
3 v; _, C8 {7 `! V4 u
, k# I1 g& `% K4 m
& w) o  |: T3 Z</ul>9 J+ d# Y: r: a7 v0 n
<hr>* `! |" G( ^0 X, q3 F
<p>会话Session:</p>
$ N+ r+ w; a  o+ _8 R<ul>9 L, d+ \) e6 V$ x; }# ~4 @
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>. W2 e3 t0 G3 s5 x
. L1 u* L& O: j& A3 h8 W

* S8 a2 e- b  [' N! }</ul>
) ~$ @+ ]: _  r: D# |) H! g<p>&nbsp;</p>% r& [0 V. _# }4 V; @9 t
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>* ]; A8 h3 x' G0 w
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

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

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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