飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

4137

主题

4225

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
14711
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
: r- ?$ E/ b7 V
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>4 G' Q8 j, u% [2 C/ p2 u5 e
<p>&nbsp;ZooKeeper 是什么?</p>
5 F: Y9 H4 D  Z# x+ c+ E+ L' Q9 u9 f<ul>
, f- ?* i1 I) y4 W2 O" _5 H<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>+ I) {2 Z& j( s5 q; Y4 b
<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>
- f4 R6 e% P0 p% }<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>4 _% G( x8 b2 F/ R$ ^) T8 w
</ul>! Q$ S9 _( X: z* Q
<p>Zookeeper的作用(应用场景)?</p>4 J% D0 l" N& _: e# ]; G
<ul>
8 [! l1 ^( m& G6 Q<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
5 s. @, e5 h+ }  U! ]$ v<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>1 r. Q& h" x* @
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
0 v4 |6 x4 V4 x% X8 e<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调用。
5 w( `# C+ m0 G' S$ l6 f! n: j<ul>
/ r: Y. H. l3 h8 y" u+ [& o<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
, H+ M* Z; P/ O' @<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
/ C4 ~) t7 |7 ~# K<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>
, r, D* O/ w% R! q6 f2 V3 I* X- A2 ^</ul>2 \9 O5 _, @# N
</li>
+ h! c7 t3 r. w- _! |<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>8 U; a* [) c$ r
</ul>: z: T) G2 t9 c& D
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>0 O' ?, u# q$ L. }, ~
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
0 o& y( u- s, f5 G( O- C<hr>* P6 ^7 O1 T8 \- ]; a
<p>&nbsp;文件系统:</p>9 a1 h$ ]9 O- j3 A
<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>/ F8 v- L+ g: C% M
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>3 R8 ?4 c9 E- n, F, ^, M! o& i
<p>&nbsp;Znode节点主要有4中类型:</p>
4 o- t8 P! n( y) G+ {( Q2 Q<ul>$ ]) m- h2 f$ s4 }! Z9 y, m1 e
<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
+ w, Y) d+ n) ~4 I8 t  H<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>. t+ [, @% c8 w2 @3 l' a# P
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
' \' T* }( Z. n1 E<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
4 E, o$ j6 v! R/ [</ul>4 C& J6 `+ \! q; Q/ L- F
<hr>
; [0 {9 ?) L" C" b+ N, X0 D( L  D  h<p>&nbsp;通知机制 (监听机制)</p>- J! L) }) Q+ F$ @
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
1 q, m# C8 c, f  q- [& J<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>
( U3 Q; t0 k$ U<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
6 r9 V* M' Y- P# z+ A3 k4 w$ m<ol>0 M7 G2 r6 x4 J5 [2 O
<li>客户端向服务端注册Wather监听</li>, l9 _' h7 @0 f# ?, @
<li>保存Wather对象到客户端本地的WatherManager中</li>) }9 @1 @. r! P( P
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>
* t% Q% c1 ?2 _, A  ]</ol>& H/ M7 J" R% S! L' c6 A" c
<p>&nbsp;主要监听2方面内容:</p>
, @* P, i6 p9 r# i<ul class="list-paddingleft-2">) K4 j/ P2 a- [3 L
<li>1 D9 \+ K$ K" C1 a2 }  F
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
; J' k! Z# f0 X" f- ~$ |</li>! a1 z- `9 X5 [" c
<li>
% o) W9 V% f% p2 k0 u4 l6 X6 f$ l<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>7 ]0 |  a7 @' l, S# U" M3 M
</li>+ F1 _' m$ ^! t* m" }
</ul>5 \# }" h& P4 e& x$ y* H
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>
7 ~# t  e& @/ ^' l$ Z: |& y, O<ul>
7 |, p+ h: s+ }5 `<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
+ K  U8 w# l4 d8 r# _<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
% s& U% D" J, F( n0 u6 |<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
2 F6 K; C  t! s</ul>
7 ~: h9 P; g2 p" B9 M, L# k' N2 M<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
, W7 D3 x: \( \) [- A- S<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>
" }1 o1 w& W# C7 d$ t# R<ul>+ E5 e% n, k/ P, F$ }/ ^
<li>Leader:负责写数据。(写数据都有事务)</li>! U. Q. f) M2 b/ m7 r6 N: d$ ?1 S
<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>* M3 N( B( Y' s4 Q5 `
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>  E( q% C. X; c

" D, O3 L# j7 _# @3 m6 e4 q/ T# j8 x</ul>
- ]! n0 C, c7 V+ o<hr>
# X$ Y: ~/ m; c# j<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
; @6 j! }& n# ~) ]  @: m3 n<ul>3 ?$ T8 N1 A: b5 S9 B
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>. N7 J/ x1 h; _: d
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>8 |8 @8 e! n9 b' Z- G8 _2 v  T
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
, a0 Q- Z2 C% L9 i, @, _<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
5 w3 N5 @& e! i2 f- Y8 }
6 z0 H1 h3 R& }! w) }! W- L, L</ul>
; ~; |% |: N- S: `- l<hr>) J: I5 y- V: w3 M- K- R% q
<p>其他概念:</p>! l$ o# g% h$ c; @- s
<ul>; Y5 m; l) P* ~- k0 @/ w. K
<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
2 ?7 w" @: b2 U- x) q<ul>
! T( ?/ ?" l; M; V2 h5 F<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
. M, m, j$ W: m( H% n$ w" U<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>. \# p8 |2 H9 \2 {8 h* X

. C+ V3 V8 K2 S
0 @% v; Z- A8 L* W* V" f% ]</ul>, {( Q/ E8 Z8 z+ o
% M- s' q' s/ k, l1 d: h# p1 Y

) q# m, L( C8 ~  u( b$ A' g</li>' d* _) h6 Z* x0 R# H

/ A1 U0 U' k4 _- Q4 Z: q& T
5 P7 U3 N6 u; H8 i# b</ul>
8 N0 ~3 O  ~9 n( k3 N! M<hr>
) y8 _# x9 y+ W1 x- T) Y* \<p>写数据原理:</p>: U1 p' L' H' T& _$ m6 y! i
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
. ^' `# x: k5 ?6 @6 n<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
# D; {5 [, `( s# h6 z<ul>
* |" T3 o* |8 u. T. _# `" I<li>写给leader,leader再通知其他节点 </li>
/ V6 I: d, U0 M' f! ~1 S8 y; U<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>9 Z5 B8 O- V7 o* k
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>5 o+ z  C" Y, T

* k) v  T8 ~1 H% g% l5 J) u& }0 m0 u/ l4 _
</ul>. h7 K+ x7 ]2 u( c
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>4 L( M( g) y' _5 f9 W5 ~
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
5 `3 p+ j# c! H<hr>& a8 N, x9 j/ w2 C
<p>&nbsp;ZK怎么保证数据一致性?</p>; ?6 O, ~4 ^6 O; e
<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>
% j# |1 C) p1 t) W' k( ~<p>&nbsp;一般我们正常是消息广播:</p>$ B$ j' l+ X4 t: p+ {: r! i8 `
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>* G; z8 u7 Y( M( O- }% e6 q
<ul>
' A' H( z  ~& L  D, L- X' Z<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2* w: o& k5 Q( _, C; g
<ul>
, j! C0 N6 e, `! }<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
, F# D. T" v8 K: ]* R<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>1 G$ F7 L7 j. b  [' [/ j5 `. t
. }8 C6 N% ]* d8 ~7 I

9 W& E8 L9 b* l7 }
0 ]. K( a* L3 T' X3 G! V' q
" v+ ?5 y/ w$ K/ z1 M0 ^# K. X5 k% ~/ T" o1 H

2 ^# r$ }4 D9 u7 M' Z( W</ul>1 F; ?! ~. N' f2 y  T; m3 c

0 ]" M/ a! h8 U0 j6 W0 S# S& G9 b

/ W( Y  G; d) x- H& f. T8 T) F  R; K) `4 E
% j1 Y( Q9 `) R% z3 R& S
! F, E4 ]% R, ^) w9 y6 K
</li>
1 O( K9 Z1 x& q% O3 |. S<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
7 Z9 Y, \0 S9 v2 k* M  s<ul>- }! M3 @8 p, b, B. @  Y
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>* O, e4 }; t/ S: @1 j0 T* I3 v9 d
" o# K5 I' e/ N
- ?: l( U  S' c7 n2 C3 L
8 [: `6 n' z9 |% e

* m$ i+ u% j* r6 Q
( w) L, M: A, e) z% \3 P0 x9 N1 e7 ^5 g2 ?$ t4 A, x- g" ]9 b5 W; b
</ul>8 Z3 N2 e8 e1 y# p) B
- @' i- B- ?$ D. o
1 A0 \" \, w9 ^
) Y" F. k; S/ q7 h- [' Z! l0 D/ s9 A! r
* A$ ^8 n6 ^6 z
- x! U! \7 P- O- G3 a8 }4 L7 T

: p( l1 W4 a3 y3 P$ y9 d, Z& ^</li>& \/ k( C- `: g
/ P* G# Y2 s" P4 b2 B4 T+ n
0 v* F- G4 z+ W% _' G

% F8 k( T  r5 d0 w2 d7 o
2 p* V7 |; o; m  F: ?" Z8 R
  T* ~) p; }3 x* ~7 b/ r6 X" m; _4 T/ t8 k8 V
</ul>
" p5 _/ c7 ]2 N6 N/ J<hr>
/ y" O: J5 P- {: N7 B<p>Leader宕机了如何进行选举?</p>
4 |8 x2 s* e/ C  n! n<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
* N4 j4 t- t- }2 x3 ]1 {: K/ o. V<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>
+ \' _- ^- w( R+ e' D0 p7 e<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>8 q7 R* q- s5 d
<hr>
! E# X8 _% |* D<p>选举后数据如何同步?</p>7 |+ |3 S+ F9 r$ d
<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>) z0 M/ g  s9 S9 }! D: N
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>3 B! y9 C; C2 r
<ul>
* M  E. q5 U5 t' g% o% m; E: z  K<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
/ k* G% y+ w( {<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>- E/ z  c: h8 t* n; u9 F5 Y
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>
- i  ~1 x0 B3 X; p% I$ G# |- n3 S5 |/ m1 F4 \

0 l% B! @1 i( h4 ]" @) q, Q# {4 K+ L* w  M) ?9 l8 m+ m

' H) a. y  t" _; g# u
2 R9 W$ z  b/ ~' g6 V" P  ]
2 x& w; W& k. F# n2 o: a! Z5 n</ul>; _! G, H8 t) M* E# I* e
<p>同步策略:</p>
$ Y3 v' L' K  O2 X* [<ul>- M2 {+ l; K# Z! O
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
; ~% F5 m* V7 m# X: 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">首先Leader向Learner发送DIFF指令,代表开始差异化同步,然后把差异数据(从PeerLastZxid到maxCommittedLog之间的数据)提议proposal发送给Learner</li>
' v0 _0 {3 G& m4 ]$ a! P+ Y5 E<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>( }5 C  J6 m9 Q( i$ U
<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>
$ b4 i( B! S6 d, t4 u6 x7 m$ Z9 W3 d2 C: d" W

8 B4 M5 W4 q+ F$ }# |+ M* _& G/ T! m* _: n  g
' [! C; h3 F& I: S. O
</ol></li>
3 m/ |+ |3 O1 S9 _" 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>6 k" O; j- K) ^! [$ F
<ul>5 x2 W! s  a# P$ I! H9 U9 x* P+ T' L
<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>: a1 `/ Q0 w7 @
<li style="text-align: justify">
3 x# u8 Y! t( e$ D2 S$ k( F" K<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
$ k; V4 }. p' h$ ?
/ z/ D1 H6 V$ U% u7 C& m/ t' ?8 ~7 X0 c
' z4 d$ k( Q/ w! \* r

4 ?: Y. t7 Y+ {$ J# E</li>* E  e) V  ?' H; I" H5 t
- m+ l2 t) i+ R( v+ f4 X+ @4 y+ f
  ]* y/ z1 h9 ~. ]
0 C( U- y+ @, j) f' }# }  V9 V
% p6 R, y0 t# q9 j
</ul>
! ~6 e( E, N& O0 {' T- I: P  F; E/ t/ A( a3 K

7 j0 W8 P. w- y" }0 Z) l6 y" r" t. A. ~, ^  i

4 M  o' D0 r& ~4 c1 d</li>
1 @! h1 [; j7 u<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
; w8 I7 ?$ p3 v4 B4 g- |. t<ul>  J) |# y3 }' m& {9 u
<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>7 J! b/ v/ P& \. ~; H
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>
, ?- E  g/ m; i' _, _<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
  N. |& ]) h. F$ k9 }  a' P1 G; R
) S8 z3 p: W) Q* e  b, J# w3 y4 N7 W" K/ ^9 C

6 w% j8 @4 @* Y* M7 M" j" e+ ^7 C; }  C8 n
</ul>
+ F. ~2 Y7 H) O2 }' l! M9 C' j9 ]4 b3 ?$ a% C% g+ Z

+ R$ N$ x0 f% V% u8 [) H8 u( ]6 Y3 d% ^
" t& S: ]3 c. z. u; M: I2 H# R) ?
</li>4 I1 I6 O( C# S3 ^' w2 I# V. h  _9 m/ E
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):
( q- R( M% B# R9 X* h<ul>
' J- H; q. _" [$ i" t<li>
2 L/ k$ o3 w1 }2 O% m8 v& c* n<p data-tool="mdnice编辑器">适用于两个场景:</p>
* w0 c3 \/ o7 n( e; o/ @<ol class="list-paddingleft-2" data-tool="mdnice编辑器">1 g% c& y2 L% t6 y4 R
<li>PeerLastZxid小于minCommittedLog</li>
! P. W* @: u( w/ Z- p<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>0 F+ n5 a" r/ E
; Q! J) w: K4 K( `" _! I

+ \- i( {" u5 L, Y: j2 i" B& {7 V7 E- s* A0 K4 r7 q

0 ?, \+ n. t- t6 L</ol></li>
" v' T; c. a7 a. Y- S2 v4 G' c<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
1 s& z0 P% {; o+ r+ v! y& R7 y
6 ~) M: J. s+ J0 @+ `
! p8 G4 @5 N/ s- ?# T# M. T  y% c

  S/ N+ C& |" ~, b% j. `& U$ N</ul>( z9 H. U6 ~, e3 J& o8 ~; ^/ e/ c

/ F' y6 X1 i' v8 D8 D; `* Y
  D  l* _' ^1 [2 S  S
* b' p( p  n' b; M7 W- H7 N. Q$ \5 s- t4 s
</li>
2 I8 e, w( y/ v5 u" l$ ?# X' s( f2 D+ k5 L( V& x; f
+ Y6 f; G% V; O( E/ W4 `
2 c0 g+ |% |/ D& B: j6 M3 ^
- Q1 I( e+ G! i0 P) C
</ul>3 ~  y& _5 ^2 ^3 E* U# f
<hr>) g6 f2 G) Z- Z5 S/ u+ G, M
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>+ d$ m" U$ L& R- `: Q9 w
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>- S+ s/ v* Y/ ?& k6 J
<ul>, ?& a" x( @. h: D
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>- x- b4 n! S2 O
<ul>  a7 V/ V6 R& U: h
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
2 w" W7 v7 `5 ^! k, I<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
% g8 Q5 o$ I* F# {2 ]0 z7 |4 S  G; H

# g7 x$ c& w4 E0 \) n/ z7 ^</ul>
* n4 ~5 M" h" q, E) }. w+ U5 r/ |" S7 k! \6 P
& W% g. L' O: J( p
</li>( ]0 P" c2 \5 e1 p
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>  |  b5 A% @3 x- \% F# a! E
<ul>$ @+ L8 z5 ~1 [. I5 n2 j6 H
<li data-tool="mdnice编辑器">
) Z! _9 w  ]6 I& b<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
8 s6 h0 r9 \0 C# b# L& q* Y2 E  I! D; Q. [# z5 f' l. B& a
$ X- d# r9 a- {6 M! ?
</li>
& r9 K7 ?" C) f% R% F, J3 Y7 S<li data-tool="mdnice编辑器">
6 p3 ~3 s( _' z) t<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
; W' _! F% t' j6 ^( ~
5 g* `' S* n5 K+ Q! t. Y1 `! U/ a) v9 W2 c  O. n
</li>
/ ^4 M# R! @' v5 f1 t
& |  J0 }4 t" |1 M; [3 W( E) X, N; J' T! h+ G3 _2 o9 d
</ul>
" {: G) S* H, J3 w! N, a
5 H( i& [' b* |9 J. I. O0 b& y+ C$ n( Z% D, B3 l
</li>0 w* j5 _) {3 O7 ^. h5 V/ K. T8 F& T
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>2 n$ D: \/ q  {6 n2 v
<ul>
. X4 _+ u7 V4 F$ g<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>/ C8 W8 @" u4 w4 G+ D2 a
. q* i( ]) G+ x: J8 o
1 R8 d$ P  D0 m4 ]
</ul>
" b9 r' D1 ?) F! I4 M; m$ I' v
) z  E  ]8 H$ z; P
' R+ H9 l; ?8 M. t+ y! R</li>  o) [# e4 u& O  N4 y- ]
' _# U8 \0 g; w$ g

  {  y* K  d$ k' X3 h  \( V  e</ul>: l; _# b6 V! D- j8 ~# i1 z
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
9 [1 ~* ]1 Z# G9 P<p>zookeeper 是如何保证事务的顺序一致性的?</p>, |6 _  \8 a6 l1 G) l8 o
<ul>
8 a7 d4 ~' I3 o4 [( F  O<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>7 G5 c' \# l& ~' k  X% }+ |

" H) s7 O% A. W! ]" q1 v! Z. k) ~% Q- w
</ul>
$ ?9 n" U) I0 u8 g3 q1 y! J<hr>+ P/ Q+ e" a7 s8 G( ^8 y
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>! f* c+ N& s! g- ?: j7 J9 t% f
<ul>
. y2 ~5 @! j% u9 s<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
' t4 Z' [1 y# b5 `' {7 G
8 e  L" y% y' P2 h7 G  C6 P8 D2 q9 ~8 E' j4 T( U
</ul>- q, z8 d# Q6 L  n" w1 c
<hr># W) D% e& O, d+ }# y
<p>说几个 zookeeper 常用的命令:</p>/ ?$ H1 i5 l) ?0 Q3 T8 j  o1 {
<ul>2 W9 R$ V  {6 P8 X* g/ `5 Z1 [
<li>ls path:查看当前 znode 的子节点</li>, |7 L0 d5 O( {1 c# {( I" e' b
<li>get path:获取节点的值</li>
# R( l4 w) j6 `- u( o; w<li>set:设置节点的值</li>2 I* ?% ?' y- b- v# K+ O
<li> create,delete:创建/删除节点</li>/ f; _" b8 s- s. W: t
2 W& m# g3 ]* r& X

. r+ G# Z6 J. d, @" Q) n( B</ul>
' O3 M( U! W; \' e<hr>
. r4 E, \% S: ?' h( y( Q<p>会话Session:</p>
$ m. S; g! W: ^, v, L3 o<ul>
" |: f* n! d8 N3 a<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
. t0 p6 ^1 O2 ~& a& C8 k: r2 C! j  W0 z' K* r" t

* i" v5 v: w$ K</ul>
8 g& ^* s, d2 X- |+ |<p>&nbsp;</p>
; j, c' t5 D! z& r: m8 k; M- {<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
' _8 l( z! p6 q3 M! c
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2024-4-29 03:49 , Processed in 0.066686 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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