飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

7577

主题

7665

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
25061
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
& D+ K$ }2 T9 Y0 ?. u- l5 I
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote># I- F- g0 `. a+ O2 H- U$ A5 Z, c- Q
<p>&nbsp;ZooKeeper 是什么?</p>
8 {& `+ W/ t7 j/ q<ul>, o% ^  x; F& C) M% ^! U9 w4 q  I
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>6 Z$ `* `( U4 c
<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>0 |3 M5 p1 t9 T  A
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>
) }7 H& v0 I" g1 @9 n% L6 i</ul>
. n& v0 m1 a; G: {' J! Y" Z<p>Zookeeper的作用(应用场景)?</p>  n% S; w: `; K* ?2 \. T) X) O6 z- e- l
<ul>' f2 k7 H0 N& o
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
8 ~0 k/ r( O# f+ W' y9 W" g# v" P<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
% P7 X1 ~2 n6 B. T- O1 a0 J; o<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>, o$ S0 |# x4 s0 c6 ]0 i$ s! 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>就可以通过ZK调用。
# S* F% E3 l5 a% s<ul>
: M9 g. l4 l$ h' |<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
9 T2 v* j6 m% q9 x<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>" o8 Q- D# w; H  l: |' W$ S! |4 ^
<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>  h) Z1 C# P& `' ]1 b+ U0 t
</ul>4 [% L$ F  q: v+ l
</li>
# V! J' [. V: {) X3 D4 W1 C<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
* N' k! ]6 J% B</ul>, M$ \3 h+ L  l2 C0 C
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>4 r5 E3 B# o% A! z
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
/ b9 q+ t/ v1 W<hr>
* i! q6 @# m" t( E<p>&nbsp;文件系统:</p>' v) s, P2 D% V! ~1 |3 M; x
<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>, R' Z+ U2 M8 j! X
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
; e6 F0 w! ~6 p0 ~3 E! P+ O& z<p>&nbsp;Znode节点主要有4中类型:</p>
3 M. N3 A- n' h. S" n<ul>1 L# I3 f: Q7 B, j) [4 V, X
<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
. b2 \7 L& `" ]<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
. ?" w( |- h% X0 m4 Q5 C# X<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
; A  U( M4 Y  ?' |/ Y( r<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>& e4 U& J/ ~% [0 ]
</ul>: b0 B/ e( P+ o+ Y. _3 H; Q
<hr>' n  @1 Z; C  V
<p>&nbsp;通知机制 (监听机制)</p>2 c" @% N4 h1 Q
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>" i- Q& }( E. Z
<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>' q7 C# O+ [4 G5 B2 X% G* Q+ E6 ]
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>: A+ l0 L  R- w1 V2 ?8 n/ u
<ol>
: d' C1 e+ h/ @& ]# H$ |$ a<li>客户端向服务端注册Wather监听</li>( |; E# s& `! c1 @3 v
<li>保存Wather对象到客户端本地的WatherManager中</li>/ n9 \: g; \7 d8 j' e% ^
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>
5 d) j8 H6 C# ~& r- z7 Q: @</ol>8 I% b. c4 v1 \8 Y+ W# i5 x
<p>&nbsp;主要监听2方面内容:</p>
9 d( C8 D* L. J+ e3 O<ul class="list-paddingleft-2">( n0 D5 h! J! q; Q
<li>9 I5 i. k, u2 Q; L; s
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>) R. |( s" P( u: B8 y$ i
</li>5 B% |. c$ Z) Z$ E% m
<li>
; p$ U2 b2 f" K<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
8 N2 y5 z) c  y4 d" ~</li>! v. h$ ]# k: k* L) i
</ul>( }+ L1 d, Y! I: Q. r# u0 @6 k
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>
" R9 |9 n$ c/ L<ul>( W+ h- B- w# O0 N) h
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
" M( }7 E9 E, f0 c! B+ U<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
1 l' M* ?7 F% a6 n: h) @<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
% {8 z6 Q6 i6 v& N# Q+ E</ul>/ u5 x8 r: q9 q  h, c
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>; [3 u& n: G( w# r9 r: z5 q
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>1 b1 j+ n" f- {! n0 }( p! ?" }5 b
<ul>
) i# D$ F1 V/ A5 `<li>Leader:负责写数据。(写数据都有事务)</li>. M3 P2 j" M3 s. 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>
" f2 S- ~5 m0 h) z7 ~* o<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>" |! b" `/ q0 }/ U( M
5 T5 T- w7 i, f+ l' \3 E1 ]( O
</ul>1 l  g, q+ k/ y' }
<hr>/ W. i9 F* W4 c
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
  m  v6 {+ n1 j* }( Y: s; G$ R4 ?<ul>
7 [8 k. {+ h, l; X1 j<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li># K6 d* I6 U6 S6 \- W
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>
5 |1 w+ t) [; ^& P$ S/ D! Y$ r<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
+ a+ i6 J$ {/ z9 w. a<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
# }8 I5 f* l3 v  M9 N/ z6 {! i7 H/ d4 K- }# `
</ul>
: @! T: t6 u  s9 W' F  D+ v<hr>
( Q% V1 a% v% }<p>其他概念:</p>
2 F. X/ |' m* x/ |* `3 b) G<ul>
; j- ?: X4 U4 V! m) O# b<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:! N. |7 A. H+ J4 h, ?/ \, A$ _
<ul>
. p; U$ A! n! v/ p3 L<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>0 }: H4 e3 n7 P/ E- c/ V( l
<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
1 W- }% [! b2 ^) g( S- W8 J# H$ q/ c1 a$ b0 {8 r
' V4 q5 K+ Q6 |  }( }" }2 r* L& O
</ul>- n! _$ V5 W. U1 d6 ?

7 _" k) T3 P# J8 g: j/ }1 h! V( y+ x/ C
</li>
7 B. U- `3 O6 V3 p$ \$ Z6 d: l, p1 f6 G9 g

: z1 J9 ]8 }4 [7 A6 p$ i</ul>& S5 V1 E' Z0 s  c
<hr>. T9 @) }9 N  ~; i
<p>写数据原理:</p>. b) l+ R# K0 L6 h' t/ }
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>" B# ^8 n. H9 _3 q& i
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>  Y9 H7 |9 Y9 @; p6 P$ N# q
<ul>! d& u/ u$ \& a: p0 ]8 h# p' c) R4 m" u
<li>写给leader,leader再通知其他节点 </li>
) |" |" l/ D) ]7 k<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>" l: _( {: L! ^
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>  x  k; n% Y1 q. ?, b$ |% g

. s. F% ]9 i/ S) O
, R7 A( k* s# v: m: W3 s/ W</ul>
  e; {+ h0 P0 d+ M2 U- I& \4 B<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>) f: _4 W' ~; N
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
; \  y+ s% O% o. @& Y$ c0 ^, m: A& F<hr>" _, k/ A3 f, }  r9 ?& {$ G! d. y8 V
<p>&nbsp;ZK怎么保证数据一致性?</p>* i( X, E. I9 z" T: \
<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>6 |  F, Z6 E5 k0 T9 X' m% M2 U
<p>&nbsp;一般我们正常是消息广播:</p>6 B! R. I/ K9 C. J! L
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
# ]$ Z( ^1 L  m& M<ul>
$ C, s6 E6 N: g9 L<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
6 `$ N1 @5 g% N+ v8 B; D<ul>
1 ]7 |# `0 f* F% N$ V" V<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>* t6 E% Q( ?: M2 U
<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>
& D: ^5 ~8 N% F/ t7 F* p! ^
0 H3 Z8 }* _, {6 w+ K# m) r" H5 z  q$ n5 A) U0 ]' m3 |- L
. ~5 j! l: T& [$ @% l2 W5 m

# U" E  Y% j( r7 y5 i$ ]
1 M$ x6 X7 W7 u9 ~- Y5 m. U( Y0 \0 q* m
</ul>
1 k3 g" Q4 X% r9 q. H. t# L$ u* ~- ?: T& ]% K4 T8 x; v

1 B% H+ n9 y; x. q. x+ {7 N6 L6 Y3 V2 t8 E

! Y2 D$ [$ W/ Y/ i+ _
* E8 \  B% m" ]* K: }6 f0 I
% ]7 z8 }3 I* [</li>3 L) @6 F1 I0 B2 V4 U
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3( k- F! z& w) L4 o5 I5 `
<ul>' ]. N6 v3 g" `: [4 M. V. K7 e
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li># }0 [; x( h& q' ]
0 n2 _6 r9 q. E( m3 W! K

, D8 p1 T- x2 c+ a/ J: A8 \# C; e: }; f9 _5 ?& Q

$ {, U1 H$ {: e7 D+ G( O* l) c; G8 _

, T1 t2 [6 l& x- K: \) q5 K+ t</ul>0 `$ w* F$ s  @

4 [# A7 m, p2 M. x* e! \; ]1 W' t7 Y  {
) I) U) d# o* N; o: O4 e- ~% H

# ~% W0 R0 I: O7 B. S/ V$ v: n7 O, b$ F6 W# }
. s- X3 v, d+ f& k! T, A9 O- w
</li>
2 D% L6 {4 l- a5 W% y& t. P
$ S7 y. H" d4 V5 Y2 b* e- _5 j$ }! s0 ^, ~# r  B! b% m/ M9 k0 E
. o, A3 [; S& G3 r# S

- n8 `. h5 _6 k5 W( t% L5 f; E" m0 O9 l0 T* F% {
  E6 P/ F# h" B* H% _) P8 P$ t
</ul>
, O' h3 v- x( I' u/ c, v) }<hr>0 v" ~8 A+ g# R/ Q2 h4 q8 S: U
<p>Leader宕机了如何进行选举?</p>* I1 J! F8 l7 s$ K( _
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
$ `( P6 ?# b* J4 B+ n( I( C<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>1 k/ {7 k" }4 R
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>) J8 m4 S% ?2 U( D. r5 ]5 d, |; }
<hr>* \3 g6 w' R( m5 s* J7 ?
<p>选举后数据如何同步?</p>3 d6 j, P! H5 f# s: a
<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>2 V3 R+ F& [. S% y) F
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>5 N; r$ e! K4 L$ E
<ul>% Q4 Q* t- T  m$ V+ t3 Y8 B
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
& X8 ?2 T9 d, T( `0 I<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>! S  V* Z& o' f1 _! X6 T
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>
7 y' g$ Q( d9 T6 d! X0 [$ U' K2 R
' i2 t9 s1 {  s8 `8 F) V" z4 l
) U/ h; C& z. A: q/ e& Z
# }( b6 ?3 o4 \- s

& g0 Q( L. o  @. \2 ?- [7 R! a; E0 g
</ul>$ D" J) l- K# D6 s" ?- ]
<p>同步策略:</p>' u/ e# @( A9 q; G- P5 k/ Z
<ul>0 g& X9 Y! r( x( ^+ y# H7 s0 p' O
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
( M+ H- B. G! t5 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">首先Leader向Learner发送DIFF指令,代表开始差异化同步,然后把差异数据(从PeerLastZxid到maxCommittedLog之间的数据)提议proposal发送给Learner</li>0 T0 l5 |+ Z* J" c9 u2 o6 n
<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>
/ S2 X; o- e5 _( \) h2 T<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>4 \' a/ Q0 Y4 N+ l: P8 K8 G
% t& K8 _; \% c2 @% e

% u" s4 `" P+ n4 r! U
  O; f9 T& [. ?) }- k
6 d9 e, _1 Y. ?* v</ol></li>: j- H  P0 `  E% B' K4 ]
<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>
& Z9 ]# |' R% Y. k<ul># M( o6 x8 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>' o0 H0 r* M7 R# p7 u* r' G) y
<li style="text-align: justify">) }8 X/ f9 y  B
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
# h8 X: @& f, O4 p! j1 T& S8 w6 }/ G# u/ H9 |0 U5 s2 i7 ~
+ O9 [3 M: F; }. c9 w% a
8 h* c3 v- b7 i  ^/ B5 Q( t5 F
7 P' U6 [$ b( p% k
</li>
/ c7 G& ~% T+ q) ^; D5 q% Q8 A8 j9 X  ?
! C! e1 k4 G8 Q6 J( |
/ F9 M/ D5 ^! c

4 C" ]$ K+ E! m+ c- e7 d</ul>) u! G  v! G* z/ ~0 V9 {/ N
8 O3 x0 z0 ?' s* ^5 {. p
  k! v% t: O  z9 j9 H$ N
: D. E, p, s% B9 O: p7 Q1 V8 X
  Z5 s8 q% z+ N- @; ~! r9 i" f
</li>  s1 N% v3 Q1 `: T: D' x
<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
1 |9 V& l' v+ X; I7 o4 m" J: C<ul>
$ F3 q# ~$ l, T5 M1 o- z<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
5 \/ ^1 x4 w  V$ u<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>. K2 e  O7 G& K- O. c  D7 p1 j
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
9 f$ w% R  C; h
# r  F  e& S1 B* m9 @( O  Z5 d) M- [
) j8 b/ v& j# ^5 H  i" s
: ?3 \) S( s( j) E, o
</ul>
+ S: W  X( B/ q8 W6 |+ }  N
# k; z/ l! s; g- d2 {# |6 m# q* J) U- Y5 a! r% U4 c

' {0 k; T1 T& S6 o& [0 _% u1 i2 b; L  J( ^+ s
</li>0 A  y3 ^& q  _
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):, Y$ c! h$ `: G4 r
<ul>
; w% l( A  \4 H) W$ j( w0 \$ w$ m<li>
; f; N# G% _; B/ S9 O: x$ \5 f<p data-tool="mdnice编辑器">适用于两个场景:</p>& H- j  U/ R' R1 A/ M0 M. @
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
7 U* ?. M4 _# H% M<li>PeerLastZxid小于minCommittedLog</li>0 u+ N' @. J! p- o" j: q2 c6 F- s
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>- j4 g9 l5 m, |6 g' S/ m' U

+ l8 P- L) d+ W! K- |2 x" W6 J2 B" k* ?& I- D2 L# `; z- X1 h
# D4 M, x3 r6 J% d: s0 ~% e5 Q

/ H2 ^- I' Y, N, l; i* P</ol></li>$ Q( E- ?- c0 y: |9 H# q6 b" N2 q
<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
, l$ X1 f4 G" Q/ C8 H0 B' ~7 ~& t7 h+ J" u8 I' J

+ U: h; ?! r* k1 u% ~4 ?% }+ N6 n; t3 l
4 L# U2 }+ K9 b& |0 E
</ul># i4 }# n- ?- m

# B8 _  @0 D$ ^  Z
2 O9 v* U3 n. K0 }& B; G
/ P0 Q: |' S3 X! Y- E! B& a5 }# C9 [  {) {' s! m6 N
</li>
; {6 L$ S- I, P2 W1 P1 l: C( d
5 M/ Y7 K( r( X4 T( _# E) H+ M  N* b* r+ C7 i. `& f2 n. }/ ~0 B8 }2 N% s
7 N% q7 R' n. S- \9 B
" U/ r( q1 a+ k: C5 `6 t) D8 ^
</ul>0 e. F% c$ W# X% ]2 m$ x, E2 A9 D* q
<hr>
' u( h& Q; W, K7 W" p' ^<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
1 ?5 @: b$ A" \4 b<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
  ?9 K7 z2 z& Q$ N- z! w6 I<ul>
0 u, F$ S6 W8 G; N( g<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
2 p( s) U+ l! w. r  K' N, E<ul>
0 K: P2 M  T6 b& Y0 n<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>; ~* a7 ^* [1 B+ I' I- x
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
% f1 G9 @6 G! ]; h+ m1 W% G# g+ ]8 k' u

# B5 _" z* d4 }8 v</ul>
; T4 P; Z6 H2 I+ r- _
" l* g/ g8 I9 M2 u! }  R
+ f' A3 ~  H1 L, Q</li>
: x5 G6 q+ C% H' `! C# r: n4 _/ v<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>0 |% c1 e- e% e: o" |9 Z; C: J
<ul>
' `% S+ D2 ]: D/ O2 {; x; Z4 s3 K<li data-tool="mdnice编辑器">
% y; `% a# u% {$ h4 N) X6 R6 L<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
1 c/ f5 w% G/ A7 S. ^; P7 R
; B8 S9 C, O) P( x$ I1 P0 X7 F7 d+ j1 E# S) G2 o5 y2 c
</li>
7 l# K$ C& f. V3 @" ^& ^<li data-tool="mdnice编辑器">8 G* E% t3 B8 B( C, k, Z% Y+ t
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>6 i5 u# @, H; R! v7 b

9 w) r1 T* \' Q: Y# j: [
7 p" b0 B, ~- l8 v& V</li>
: [0 E0 v3 I, R* O. b* [! [6 B: M
2 f$ `: }4 U+ m; u: W7 Q9 g5 M8 |' v9 F2 S& f
</ul>! \; ~& `4 m$ E2 [* z* W/ I9 g1 A5 N
/ l9 j; Y( [3 b! V7 ~9 t
. z/ [) z/ k+ K! j2 v- S' M
</li>/ N5 j) {. C9 ]' ]5 F; X
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>' |1 C! t1 m6 d& n5 _) O' u/ p
<ul>8 X1 x* U4 ]% T  T; I
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
5 C! b( {/ P; \" x$ l' l6 d1 p- Y+ |( C- B

( ]" L0 H& I" k. m- i* A</ul>
6 w$ ~9 t9 s- n# H+ x
# @9 m& h: b( k' M* R
7 D+ j8 J$ @. u) a</li>. L, X: o6 C9 Z, g

2 y! ?4 X! [% l* @
( U6 {4 \5 X, A</ul>, b% d  G  i! y$ z
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>8 q  P: l# d* \5 {# w
<p>zookeeper 是如何保证事务的顺序一致性的?</p>
* \4 C/ ?, ?% {  I<ul>& U0 {2 F3 M2 W3 _1 a
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
' r+ i% c( S- S9 }  R2 P8 W
4 u1 Z" l$ Z9 V" D1 ]
. O' T9 A" s% |& z% h, q! u</ul>
; [% \1 M5 }7 F<hr>2 d& P( {8 X  k: o/ T/ n8 V
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>' F2 z: S$ Y7 S9 i) \# G0 J, {
<ul>
# p) G$ k* T3 E2 M# p  i<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>. @9 p6 v  N, n  w3 r  n1 ^& P
: W9 i1 @% S# J4 c
+ K7 s% b/ Y* d
</ul>9 g% G" j$ P  s( m
<hr>
% h0 N$ N" @; U* O" N<p>说几个 zookeeper 常用的命令:</p>
6 m+ U" r* o; ~<ul>
! {' R! d% L# ?0 t7 y<li>ls path:查看当前 znode 的子节点</li>. i, C' ^" `  |7 @9 I- t" k  z/ a: g
<li>get path:获取节点的值</li>
3 s8 U6 H) f, y6 s% m- j" F<li>set:设置节点的值</li>9 k& L; X# g& C+ B$ Q2 C
<li> create,delete:创建/删除节点</li>
. u+ O4 p1 h0 t: X: G" F# R
/ s9 v  y# _+ }2 C
+ T  G+ h  D  [% c% a  |* M</ul>
- u: A# Y; X# p9 m( \9 P<hr>, A+ s+ u4 f% Y
<p>会话Session:</p>: l' O/ l+ J  U  U6 W+ ^! e
<ul>
. s8 ?$ ^# C5 Q% O0 t% D- w<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
0 o% W* c, K" I5 Q6 d) `2 i- c4 Q- a1 l7 c& s% x: ~& N

! M# Q% j1 m; n6 [' O</ul>' k* L: H& E" r' e3 Q
<p>&nbsp;</p>
7 \* E! v* v: v" s+ q3 O<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
: W' B2 h* ]4 k2 J. W% K9 J" |6 l
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2025-9-16 22:02 , Processed in 0.077953 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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