飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

8117

主题

8205

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
26681
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
& q8 P8 m9 K2 {
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>, m5 U. v( W, ?- A: q4 l
<p>&nbsp;ZooKeeper 是什么?</p>8 c( J2 m+ ^7 J
<ul>6 N! Y0 D6 `8 }, L0 S: h
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>' h8 g' n0 A$ Z
<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>/ C  H3 t6 P& Z
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>
& x- g. b; U: w; v: p- {6 f</ul>
- V9 a3 \; G- a, Q& r; h3 r<p>Zookeeper的作用(应用场景)?</p>
9 z. ~. \+ t6 a, E<ul>  t. Q1 u4 W4 g) b! X
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>6 s0 a3 k; o: ?2 `/ @4 _
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
+ d" K3 Q7 z& \; h* O4 n- f<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
0 F9 \( I7 L& T3 @# h. 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>就可以通过ZK调用。7 ^* ^3 ]9 @0 ]+ ^; p
<ul>/ r5 ^- K9 f' Z; C0 p0 W" g) d
<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
# n( f! P) T5 {0 b5 f: N<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
/ g$ n: `" [) W0 }; O; m<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>
9 E  n: @  q) y8 c& o, L</ul>
- C, P4 x  x) A$ |1 X5 h</li>
9 \* b+ f, s0 a- H<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
; X, K) [( Q: t- M! S</ul>
) [* q' e+ c& \- t: g- }" h<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>- h  I/ ]9 ~" t. ]! K' J7 N- H
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
4 }1 o" i% x- l$ h<hr>& ~  ~% ^4 R2 _. e& P% r
<p>&nbsp;文件系统:</p>7 a+ n5 \6 d2 u4 b3 G4 U  b
<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>
2 N, E9 s9 U( H3 H' v! V<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
# C, w3 |' g2 I* a% m<p>&nbsp;Znode节点主要有4中类型:</p>
7 {3 ?8 G5 V. A& |% D  j<ul>. [: `. D4 z; W" ^- e" p7 Q
<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
& E. s, o' r/ I$ l<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
% F$ z4 K8 _# l* v# e) }<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>6 R6 P/ q+ o9 \& e( @! L# ~/ ]
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>7 E' s* m6 U8 m# v; K
</ul>
7 T' V1 ?7 {* C3 f) k/ J( X. x<hr>
- Q$ L2 A; P! C- p( J( ~<p>&nbsp;通知机制 (监听机制)</p>
/ t6 B* y# V- |* J  r<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
: L& o4 U+ V$ p  z2 E' 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>. T5 y2 _7 |$ Y& \2 S. U
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
* C/ ]. P1 J5 {/ w4 u) {8 @<ol>
& n7 U8 e# Y# @/ C4 K<li>客户端向服务端注册Wather监听</li>
3 U% v( ]' L. l1 d, b! `<li>保存Wather对象到客户端本地的WatherManager中</li>0 R- j& U7 o3 G2 ~
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>0 f, J0 x% c( P5 b
</ol>& B6 Z) V3 z% k9 p5 D% h! s
<p>&nbsp;主要监听2方面内容:</p>5 F+ b0 G% e; P, b/ ~# q
<ul class="list-paddingleft-2">- s; U3 B, `' V7 O/ @4 y
<li>6 ^$ n! z5 c! ~# S1 n, ]. W
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
9 K3 o0 D. g( d% @7 {' F/ g0 ]</li>
* f# T+ {3 x1 v* ?( }/ m<li>5 Z1 v- _& T  z9 Z
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>  i. q( L, v+ x& [
</li>
( m  \/ v2 \* R</ul>& U- g7 b9 v% G# e% W3 i
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>  H- s+ i' v$ |3 H! |
<ul>! y- O) |* s2 E1 Q
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
( d+ |) `& H. L9 O<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
" n3 v' B; E3 g' t4 a3 ^; I% c<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
! {9 ?( {7 p% T0 e6 K</ul>
( R& _  G4 k# F. G1 ~<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
- ~( z6 v  k  \/ s! ~7 k<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>& M8 L0 M+ E1 l/ k' i
<ul>% F! R: k1 B6 ~6 w! B1 S
<li>Leader:负责写数据。(写数据都有事务)</li>
" ~4 o; J* t% ~$ |& \<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>5 ]7 J# p8 u& f2 d# X) n% m
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>: r- n' Y/ ^& h, J
" R; m' K7 o) h. L& d* S( I* _6 i
</ul>
" a5 z7 K  y4 m; F6 R: x<hr>! |, i2 G+ t. t% ?$ v* e$ j! ?! v: G
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>' D5 H: I, q+ T( r% b
<ul>
8 ~; k6 X( e- j6 E; E<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
6 S& B8 u. y0 T& R<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>
3 o0 y; h4 {- K% T( Y! _) V$ n<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
2 b$ L! e* J! ^: o* b8 P<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
4 K8 E0 }& z9 E7 v; e0 P7 _* k1 r7 C0 C6 t: h7 C2 s
</ul>  j" q9 g/ g6 T) |. C, H3 x
<hr>( ?, m5 b' {  l$ I4 U4 d' `/ o
<p>其他概念:</p>! M: E% v7 p& I4 x/ J% L
<ul>
4 h3 U; I- B% ?7 o- `' ]<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
( V- f, ^5 C8 w' ]<ul>
$ [( \4 Q$ v! i: g) u1 N) L<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
$ l0 v8 x7 r0 O<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>' L1 T. H2 a. [* D  g8 m
2 s% h/ P. p" y% w0 _1 I
& t" p$ Y2 K5 d, A, C! b7 u
</ul>
. B$ Y* ^4 i0 p9 m) i" s" m
; j: |  L& @/ x1 \" c- W+ w3 x4 z/ v/ |/ T0 z
</li>
4 Q$ S+ X, o' C9 M4 e) B4 b2 a3 b5 o2 U! q& ~4 e+ A

! E: ^, Q! q5 A7 b( z- |- k% I; O) [</ul>
& P/ J4 |+ o4 z6 j<hr>
5 U/ M+ ?( [3 q! W/ }; l<p>写数据原理:</p>
( {% [! ~5 ^( U$ ]# I. v<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>- G' w" g# t$ G1 d- u" X
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
9 }8 O! \; a, H% L<ul>1 F& p0 c3 w: P% @, x) p) |
<li>写给leader,leader再通知其他节点 </li>1 Z. S6 z1 J& k. q4 P! D
<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>
, v; a+ m% ]: o2 R<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>! P- a0 Q4 d% z  [- {4 @
' k6 r' B5 v: W
# w) ~/ `. X- r- ?5 p
</ul>! u+ j7 S/ K9 }( f+ Q; j+ R
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>% _$ P6 d# ]$ V0 m- o3 O1 h
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
& g6 a# t* @& c+ d<hr>+ i1 @/ g7 s) R0 E9 f
<p>&nbsp;ZK怎么保证数据一致性?</p>
+ i* |. \+ C; _<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>+ S; N- f$ |% @2 Z
<p>&nbsp;一般我们正常是消息广播:</p>* A% s; N+ g0 }' {
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
, Q( s4 E" t8 K<ul>* Q# G! ?! O$ a+ P8 H
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,26 f1 @3 e6 ]# D- H0 J
<ul>) v: w* T# w8 b
<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
' ?4 R* G9 W# t% W$ C* K<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>
) K+ z& V; ~& h' H% s
, x2 y1 A- h; L7 `/ v' W$ L' y  S6 Z. d* L$ U! s
, ^. q5 y3 E" E1 [8 w8 j8 H

/ O& ~; F! z; K; {0 N
8 e6 c8 l4 V7 i3 x0 Z
/ Q3 Q0 d( S3 b! Q</ul>" [$ L/ L1 \5 l2 }0 ]+ g' X

* J5 i0 Q) R, L' ^. F; s/ O/ q0 H) b8 j; l% T

2 Q6 X0 S  Z; p4 ]: A% h8 u/ W: X, P; J7 K

4 d& H1 p5 ]& P! O, t1 V
! S1 N$ u. @! B, R; K; T6 `" ?  X6 @9 _7 c</li>
9 q3 `7 ~" h) Q" L% n5 L1 O; k# i! h<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
; E' @6 r2 x1 v9 d& [: o  ^4 v6 {<ul># {2 i2 [# n0 W+ w! m
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>2 u, \  [* `$ Z2 V4 I0 H
. U5 D0 z6 e. A& H- I) B" s0 r' Z
/ w/ L9 c# e5 M8 `' U% _6 E" V5 O

9 x4 \( f$ T9 ~- K/ B7 \, }
! d0 p, T8 N6 b& ?0 O
  O4 ~4 ]4 N# ^/ b
. p" e/ v0 h# J. E# W& k</ul>+ r  N4 g) c3 B) U) o
6 X; X$ M) v7 {9 @9 j

# q! t' E2 _  |3 I
( D% A; C! V* d& u" e( y" `' J
' B1 ~+ i2 `, R6 e2 R) d
& Q, t/ O) Y9 u* s2 c2 F+ F9 |7 y4 j% M  q
</li>( G  S* [* @* m

& x- K- ^2 J- T; G" ]% R2 V6 p8 r! g
% L' m! R, Y( S: v
) B% p( ^  ?! A

9 X, C7 {4 F5 r, g# Q" O/ ~9 B
/ i4 X8 q3 F! K  `4 r</ul>
7 R$ w6 A* r2 z, }# S5 i5 o$ V( B<hr>
4 z( I' ]- E- E9 d2 W9 p3 C  ^<p>Leader宕机了如何进行选举?</p>, r4 V0 J  U  D( c& S4 L
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>6 [; P' A& S: ?8 t
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>: F( j/ ~( ?1 i) r/ u
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>, C' }/ P; |. n- K  i
<hr>* R1 I7 Q0 a6 R! R. C4 X
<p>选举后数据如何同步?</p>
& u7 @' X2 ~+ N<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>' i, ~3 V" P: r2 g* E
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
( I# p# J& d! J8 D: B+ p. Q<ul>9 U* T; p* X* f* M3 _
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
" {$ o) i8 K5 k0 g4 M+ i<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>$ r  H2 ^  r, K
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>: o8 `3 l: Y6 e2 V" o2 E
. n1 S. i7 ^, H1 @. p5 z: n& E
4 @5 j8 |* e3 j, H
2 W. l  H9 U. Q+ e: q7 [

5 b2 k6 o) P' n4 b2 r: `9 r$ x! R3 p! t- u: n/ w6 Z
, Z8 X* o$ T% E4 E% w
</ul>
" R$ m/ L7 q9 v, U% F<p>同步策略:</p>  r/ U  X( B5 P, Q' J+ T+ Z) c
<ul>
8 N- {. j- C$ }% O<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
& s3 w; y3 d- v. h7 v<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>. H" w3 h5 D6 T/ m5 A4 i; 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">发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步</li>
4 L/ B0 Z- m$ ~, i$ |# K! R<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>5 f  x% ]! Q! l9 z* \% t
+ b3 O5 V; F4 f
& ]$ f$ z; x- u' i7 X, t& w: c0 N
( k0 @  Z* q  B+ ~$ B
2 v- H  J$ M  F$ l8 i' u8 G
</ol></li>
1 @7 }; d& p' P! a: V<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>
+ T9 u% w* Y' f2 x& d; A' o& A; t0 {<ul>
" ?0 W2 j; f8 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>
0 T3 C5 H7 l) M1 }) ?<li style="text-align: justify">9 w  u+ w: X* {* o7 w. Z
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>( x& G* z0 a0 W# Z. Z& l& Y

# |. ]: W+ h! n- v! {5 I
- L' U' o( n# u. a- _: Y6 o) p1 s# @) [; F
$ C$ c$ c- p& N$ d* k
</li>
6 R9 m; x+ Q! `& |+ F
3 E1 ?. V. k8 C: C% @
+ V& ]- D  O+ V( }+ s8 B
* N$ `- }6 b6 K/ d, e, Q
3 s% S& k$ I9 `% R$ ?</ul>! h" @* k  `. Q2 j2 q1 }5 N2 Z

- L3 ^5 w( b. _" H0 d+ \
3 |) {; b& M' b  S! _1 Z. |) C; [* X" v$ B6 i- L" t/ L& N5 F
7 i4 p8 U& o( a; M' P* n$ q  _! m
</li>
6 g2 a% D- Y9 W7 b6 [/ I( k3 \  @<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):+ M  j! N( E% \0 Y! w& r
<ul>
( D2 U6 x, W' B<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>9 T2 D, k2 a! q- U$ Q% D* Y! `
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>
5 o( u' _8 C4 ?1 R. B/ h+ H6 W! E<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
' Q/ x$ a  `& n& B, H: c, g0 G$ A6 i0 S* ]3 Y& V2 k# a
( }; \, C6 k6 }6 _

+ G7 Z" }! p9 N/ {+ M$ y' r/ j5 t+ X1 Z/ b( \! I* k$ p# ?! B
</ul>
3 t2 R/ o8 F0 ^( P! ?; L) p
$ ]$ T, I$ R' t6 V9 h& [  |& ]/ R- _4 i
! D0 V& g9 N6 w! x9 U' w8 r- O5 M
4 h8 P# q1 J/ {3 B2 g
</li>
* a2 c0 j" M) i6 P6 i5 ]  Q<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):7 S& i; F4 [( T- ?) i
<ul>
+ v, o  [, a- ~7 e7 |" `<li>; {. O+ r" e% {' I
<p data-tool="mdnice编辑器">适用于两个场景:</p>6 n4 ]( m7 U8 B+ u" P, q3 j2 A1 z8 V
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">" y. ]: |- T+ r& D
<li>PeerLastZxid小于minCommittedLog</li>4 N$ c1 l$ X( ~' ?4 _  I
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>/ ]  w# I  G/ K% {$ |2 Q
% `: E( |7 e9 z  a

  ?4 z  s8 |6 ^, U, G; G
. J" l" X7 r; L" H+ M0 Z( _8 N% \
% X9 o' d6 l; c8 j4 l( H  q' W</ol></li>
" W$ j6 W& [6 ?" K  O<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>( i# b6 b1 K3 b6 O

9 ]$ ?  Q2 Z7 Y, n$ j+ `% I- k' K" q, M* Z) `7 Y9 m+ G

1 t0 q0 ~. {( }% M, ?4 t/ M% s" v/ ^7 f. L/ R& B/ R
</ul>
( G/ V9 p8 p( t6 U, e
1 T! _/ g$ H& q# a: e& C* g- H2 o& L6 Y4 z( d
* p+ q3 n) x+ O' j3 ?

  |' A& J, ]: {* f</li>/ L1 ^4 l0 N  [1 z1 M
/ C7 Q2 G/ @- Q4 P, [

# r7 q$ ~- P$ f7 X4 Y; m
& _( t# I! S& O; S1 v3 z/ W/ l- m- }  n# G, l
</ul>
! p% y! B/ P9 R6 ]! o<hr>
& l2 V  N/ m' E$ p6 m& n4 i<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>/ q/ L' e' _" s3 _, `: x" m
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
0 w8 I. M- R. j% Y* l" s<ul>
$ r8 o, J1 M2 U/ O5 y' t) c<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>7 Z  P- l. ^/ E5 |) _
<ul># Q) m- E0 a5 M- a' ?; _* a
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
/ `& m: [$ e" m# j4 k<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>8 x( Z/ @$ N8 K; v
. e0 f& I( [  r

1 V$ O6 ]  p: D( U6 U</ul>: y% y- _' G" }) [5 B8 z! G) S$ ^+ w

+ b9 E: F# f6 x$ h
, Y& Y, d2 {# C' S</li>0 T: h( b- p! {# I- Z* g1 B
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>, J+ A  @: b% ~  n: N* {
<ul>8 ]- C2 s8 h( P# N. q( J8 O
<li data-tool="mdnice编辑器">
# W7 C) h! p8 ]/ v* V( H3 ?<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>; }2 T( V+ e1 c0 }5 |, y
1 A% R6 t' {7 o( \$ u$ ?7 Z1 X
5 J9 h# l5 E" N, V
</li>
; ~2 o8 ]& Y# B2 g8 o! |& o<li data-tool="mdnice编辑器">
. @$ ~, W- X3 a* t' ~7 M9 g. L<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>% |0 h5 a) I8 a3 g! ?  D
, b+ p2 C! [" @( T* W# K$ a

$ Z4 F6 O' i& |0 r2 D</li>- r6 X# g+ }) K* M% f4 f6 X8 m! K0 B

$ m) Y0 {4 V5 ]8 g, z! t9 x
7 l6 J* E/ B8 H" [1 N</ul>/ w" Y* f" w% `3 l, Q' ~. e

' k3 e5 A$ q9 t" H/ S2 x/ R  K7 L: L9 u
</li>
- M5 O" S  q" ~<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
0 j  f5 F, O! J" a<ul>, B5 g, u$ Q* @: r
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
$ [8 p- p; B& s, q+ z
" c2 s4 h  Q4 w* e
# d+ b! ]# s/ |9 R  y1 M# g/ e) ^</ul>
. J. r: [' Q$ D, U
6 L, ^4 y+ x, T& j
& b& }" F1 r, i9 u</li>* `8 k! p5 t  g

9 _; r0 K0 F4 p4 x8 X3 F' D5 {- |
</ul>
. i2 _$ G$ |. Z. h5 I& S9 S2 ~<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>. G$ `" x' D9 s  I- Y2 W3 q
<p>zookeeper 是如何保证事务的顺序一致性的?</p>
5 P4 c( E) X3 h& _<ul>$ ?( g, Q' v- u$ B  P2 j7 P
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
. w8 n4 X- w0 i; o
- `; R' U  I" O+ n! U! j' G: Q
4 ^) c- X8 S& N( r</ul>
& y4 P3 E' H" S<hr>
8 D; T2 t, ^+ x' b$ d" Q0 E3 H- `<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>" m) O5 g# B- V" [
<ul>
: t/ {) N" q# G& l: \# V<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>! k- S$ a9 f; `2 j

- Q0 n" \9 O, ]4 I- V) s- _4 b( B6 h# @6 i' H8 D2 \
</ul>
% C" Z1 \% U6 _; v" P! ~: T& \<hr>
( P7 ?! Q; o# r8 \<p>说几个 zookeeper 常用的命令:</p>
2 Y" }9 X% c7 F6 R# }<ul>( T4 h' N% p0 S( [& }
<li>ls path:查看当前 znode 的子节点</li>" q2 |+ i  p( b7 _1 p0 H+ ~. Z
<li>get path:获取节点的值</li>; {# Y- t, }3 y3 B
<li>set:设置节点的值</li>
. v7 w- F% ^6 J5 k<li> create,delete:创建/删除节点</li>
6 e+ o3 a( d7 V$ }9 M
( l$ ]( Q: d. ^  Q3 {- m: A3 c8 ]2 U6 T( a8 N1 {. F
</ul>9 q' \$ X- ~9 }: \3 ^
<hr>
( m3 k2 h4 _% E, ?# T<p>会话Session:</p>
/ h7 }/ B5 l4 C- q7 d<ul>
- M) Q" a9 Z# W3 X+ J, G/ q<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>* t1 L8 s# J( V% w/ Y/ I

/ ~* T% l, E% |; u$ s* j3 K4 p2 I4 H6 d
</ul>
1 q0 V# k7 f; a3 z9 n1 i/ I! j8 `<p>&nbsp;</p>
% X7 f' |+ N2 Y5 x9 ?- {<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>) h* R, [& _( d. G( [' y$ k
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2025-12-22 19:19 , Processed in 0.066264 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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