飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

9008

主题

9096

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
29354
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
. i) a5 x! T  F4 X
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>5 Y4 K9 O5 N+ k) C7 ?
<p>&nbsp;ZooKeeper 是什么?</p>
! j2 y" @3 o3 n  Y<ul>2 E- d( p* ~, Z7 g
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>
6 }3 y. ?+ I1 a* m- \) r% A<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>8 J" ]% I- I% D6 e
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>0 A! j  V" K( F
</ul>
* d3 d: A* h* J$ A/ w$ g5 Y<p>Zookeeper的作用(应用场景)?</p>
; M! |( c' F, K# Z) R; {# a. u<ul>2 j& P! Z1 g* }0 O7 a8 p2 m1 M- H
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
1 J, h" A$ |/ I2 g- C, l5 Y<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
, e( N" Q& Z6 |  ~4 }<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>. t: x' F* m3 ]; S& v! i' `
<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调用。
  I( }, e5 i2 L- b2 v1 b<ul>
6 ]3 e4 i- T- g) S0 `( m: B2 h<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
" ]- v4 i2 P0 n- O% c  N<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
) V( \. w# `* [9 [<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>
. }7 d# {$ l6 R3 M; u8 m5 `</ul>/ x* N3 h. p. i0 x
</li>
' v: R5 T8 x8 D<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
/ F2 e$ v, A5 e" W7 K. R: i</ul>0 x) D, b) s1 ]8 p
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>/ X. u, @7 d3 H; Q) g# P6 U; V/ {
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
6 q. @, ~, x2 k/ `% A8 w* u<hr>; K; J# n5 U. P" N$ `6 w  Y- E
<p>&nbsp;文件系统:</p>
) Q8 q7 z5 |3 `<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>
6 P! Q; Z) J: ^* w$ {. z* K<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
* B4 a. `" {3 h  N# R4 Y<p>&nbsp;Znode节点主要有4中类型:</p>( P* ~# ?' g2 P5 M4 l0 J! b
<ul>
/ Q5 F/ `: y! v, W0 W( C6 u% Q0 u<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
6 ?$ U5 V7 b7 S0 h! @9 r- v<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>3 b  Z# ?7 I+ s& `$ d
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>7 h. z4 f5 `2 i9 h
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>+ p5 y6 \! ~/ [- d! H! v
</ul>  |- @! N$ b! f1 U
<hr>3 M% V5 a7 }% |; d
<p>&nbsp;通知机制 (监听机制)</p>0 H1 f9 Y8 j6 ^' A. F2 j
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>7 h7 o2 E/ h/ P$ G
<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>
5 o* k( ]) C% B9 C$ F( y6 @<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>6 d6 v6 {8 j: P
<ol>
4 t' y! L4 n2 F  Y<li>客户端向服务端注册Wather监听</li>/ P, d2 p; z+ J0 `9 |+ N  v6 m
<li>保存Wather对象到客户端本地的WatherManager中</li>
6 N. _6 z# O0 J. U$ m. t<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>( O# V3 H; L6 C0 W5 N
</ol>
8 ~  t' c5 u- o3 N<p>&nbsp;主要监听2方面内容:</p>8 X$ ]5 R: n" \9 w
<ul class="list-paddingleft-2">
+ e' D1 u/ f- ~6 `<li>
6 j; h9 t( |' q0 U<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>4 R/ ], }) X; w3 ?- g' N: q2 u
</li>! c2 ?: s# M) t: K3 |3 {( P5 M
<li>
2 l: G! i. z1 i8 [<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>9 Z4 I: z( ^% L" K) J
</li>/ K- V8 A5 j: ]: j  M# I
</ul>9 H( E: F0 V8 u! m9 V8 C, w! ~4 `
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>
) c7 }! I5 x+ v& l8 s7 [" B) n! b<ul>
4 p) S5 ?$ R- J! j<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
6 P; S) \6 ~0 _$ \<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>, x+ W3 ]( L0 d
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
' |2 }2 S8 F, M0 J</ul>
1 I# [+ c) z* c- j# Z* p0 q<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
4 a  Y6 w# ~) d$ X' I<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>, q' i6 L6 l9 @8 ~% J# e$ F' ~
<ul># D9 z/ T, w* H# l( q# X' y
<li>Leader:负责写数据。(写数据都有事务)</li>  S7 u, j, \( g' p) A" `* Q. [
<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>
- d5 Q2 Y9 u$ G1 i<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>
# n. w" y/ S3 k: Q/ n0 h8 s4 _: q# c$ c' ?& ]1 ]
</ul>
( ^0 S1 Q7 I( ]+ c( Q& [6 W4 r" e<hr>  W$ {& C' P8 f5 s+ [+ @  v
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
" R+ g6 N9 w+ |2 u<ul>
9 O5 O) ~4 I% i& V0 \- w, X9 a' N<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
$ z7 U5 G! P4 {8 X8 O  u5 f<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>9 B- Q* z3 c( Y! p  X9 g3 v$ Q
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>2 N8 u0 y1 F2 d2 o/ G7 j) _
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
) q9 i7 h9 g+ l1 ]( g) [* X# |- t9 T' @7 Y9 I
</ul>
3 ^. O! d) @$ Z! W( R<hr>
/ B0 Y' y1 |1 S/ w4 \4 M- p* b3 y% k<p>其他概念:</p>
4 |6 E+ R( ~$ J+ d2 a$ H<ul>
; U, g+ b  l9 O0 H<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:/ Z; A7 C' R6 Y& ^  V
<ul>( o+ r, Z+ n" i' `
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>) b. j) n4 h; G6 u5 m! d
<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
4 j8 c/ |) o: G+ j1 j2 L8 z$ \# z  R3 Z9 x6 P9 J
$ t. v2 ?- Y+ d/ X- _
</ul>+ b! h# @, M, C3 h/ Q! g
0 Q9 i1 U+ x$ I! s6 \

) w# B3 |( i" U, [</li>
, Z1 R3 R& w! r% d
( m! e( z5 d% ^$ K, p& [/ T+ M( }# t- G
</ul>
7 M- o& U- m# f! B+ h6 Z" T9 b) ?<hr>1 r4 h3 J! C" g$ ^0 t8 C8 j$ U, I8 A
<p>写数据原理:</p>7 F# L$ ?$ l8 [  ~5 \; W! n# A. n
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
* |9 {0 }5 z/ n5 P6 u<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
2 e) |1 _2 T; @8 V<ul>* {4 Z" i$ W3 D' Z/ p& J9 f
<li>写给leader,leader再通知其他节点 </li>/ x) N% z0 t" C6 A
<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>
$ L  H* ~- h: f0 A<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>' J/ `: w! ~% ?- e

, ?* i7 P  G* L9 M. K6 C: |) ^
; s4 [7 \0 p5 X</ul>6 u3 S6 o+ w5 \/ \6 A6 f
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>7 F9 |0 I' o) ?6 z2 ?8 D9 n
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
* Q8 n, y+ z# I# L$ Q<hr>
8 m2 y0 s: e4 B1 ]7 }' z6 Q<p>&nbsp;ZK怎么保证数据一致性?</p>
. [4 C8 ^; G) I2 f) r- f. I<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>
" C# f  ~, p! K<p>&nbsp;一般我们正常是消息广播:</p>
/ B, Z3 c& j5 x' H# B) m<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>$ x' H& ^) g& P
<ul>2 a7 [) z3 a$ h2 j6 w) ^
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
# d- ^- t# M' L' m$ t<ul>
3 H! r1 Z1 n/ `- U<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
9 V4 D$ |2 i1 U+ ~<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>1 h1 [/ K, b9 E& @& h$ C
% f, e* ^4 x$ c6 S7 l$ c! R: |

0 W" {) z) ]# n: W& B" w& `3 T" S; V: s/ P

7 S0 P% E3 @9 \5 Q! A. a! E
/ _% E7 H% H- W4 G" o. i' _8 v! u: V* u
</ul>" A4 ~0 L- f  J  f) y' `- c3 f
. n/ A5 {- n) l7 u, k; ]  n$ T7 a

. L! p5 ]9 X4 k0 R- {
" J/ u" X4 v5 q7 T! U! N' @- M
" B! M& J# x8 V2 M8 B, G: r4 k# `' G/ Z
6 A: g2 k  l/ t' t" W1 |8 h
</li>
  ^" s$ z- p; B<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
5 Y& q- f, A9 z. ?8 t; E( C4 P9 u1 s<ul>) f  W1 U) u8 T% _% q
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>, s& P0 v0 N# ?9 w8 C- ^8 w; ]& _
, m( _; g3 O6 R8 w
% B# r5 @5 h8 q' ?9 N2 k
! C! l: e; n7 A- Y6 b& B
, \2 y) d% y/ {9 R; j

5 g( ]* _; d) p1 F( |3 q5 T" l: V8 Q2 y: \  e
</ul>) b4 [7 `7 g) H
8 _) J  }6 k. w8 g- a
; m1 u6 R2 r! b9 ^' v" a

2 z7 h( ^5 j1 N7 I9 ?
, j. R7 a) ?( Y8 a7 C! J1 H2 @- i
. c( ^+ x" b. a  X5 f! v3 r* d! Y7 |" R" I9 w
</li>5 m1 t) x4 o. o7 n* w# x
5 g* P# w: O. O% e* n

+ [  F  p. c; {5 w2 @, }
4 r. H8 S! p' [2 ^0 p7 C2 i1 h8 r3 K% M: M& e
( z, n8 `# u% N# c4 z& n2 `0 c) B7 W

. k! G2 b& H4 @0 L! v1 w3 H</ul>
9 M% T* W* O; p+ K9 m<hr>
. `& l, M5 x" g<p>Leader宕机了如何进行选举?</p>% s7 O7 Q3 D; q( M' |$ T+ S$ c
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
/ u9 A$ ?( t+ F: B$ \6 @<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>4 r* _" k) X) b- `
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>4 p3 H/ R6 s- G: f' G3 |/ E
<hr>
6 m7 A3 C# }3 Z7 R! ?/ j0 O<p>选举后数据如何同步?</p>, ]6 z" L& Y: k4 y7 t
<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>" A) t) @0 @, q9 }; a  {
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>2 @9 S, n5 D6 m+ c% [
<ul>' b4 i' {" Z, B2 i8 q: F
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
8 ?; w: n. G' l) w<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
0 \$ V% R, H/ ]5 p, W% Y/ Q) c" X<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>
/ O  _( j% ~5 K$ d- N8 B
/ }  s% ]1 u8 v" A. V
( i1 S. q# ], C' x0 }& ]
0 |% d% N1 I  P0 Q% `  l8 f" @. \; Y

8 k% P3 D: y, e/ t
4 I2 V6 \/ v: x, b. K7 d( N</ul>( I# }/ h  M; Y- @7 l2 a; s
<p>同步策略:</p>
8 Y2 |5 S4 s5 a1 I0 S2 F. V+ |<ul>* T4 I: I% Q' x0 _, i
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>* E3 @" p; F' Q# 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>
) m6 ?: u2 g" W; r* i' x) V) 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">发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步</li>
! o3 \( J  |& B- ?  `, O( 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">接着等待集群中过半的Learner响应了ACK之后,就发送一个UPTODATE命令,Learner返回ACK,同步流程结束</li>
' H! W# |9 k& [6 o0 C2 v3 l% }" b, v" r1 B

5 I. P6 C; t3 ]" O7 C/ L: D* |+ r! d9 C5 ]  e6 L
$ ~$ d3 H' M* e8 w4 w% Q+ j& K
</ol></li>
  U: V6 ~( W  }# ]( \<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>5 k; J/ V9 U' p) G. v; b. l- j' o
<ul>" U# \+ e6 h% J8 Y4 y, w% C0 ]
<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>
  ?( y  Y6 L* A( i% O9 `" D% k<li style="text-align: justify">
( y4 R. p2 y' X& ]% |5 F/ Z( ^: q<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>. [! R1 i( d8 Y9 k
1 m6 r6 ?+ \- [- ?0 U

% p4 w$ L9 `  W2 @9 m' O
% v5 t+ A: o$ X/ O- z$ J% X
' c6 M9 ]: [5 a0 I1 W. D: o* V</li>( N5 I3 {$ d3 r
$ Z! m8 d, C3 B0 F- U+ F

! [' X8 K# N. `) F' W8 P# v8 K9 P3 Z' t: _$ o

7 _5 L3 q' z9 ^5 ?1 _9 f# [</ul>9 @4 u9 `: r' B
% h% H# N# e. v% S
- F3 s9 H" ~  A) R) r- X" W7 t

! p  k; b4 m( r: y
& r- r& k% g' P4 ?</li>6 Y1 H1 ?# C3 @) K
<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):' e  [7 O9 b* t1 T: ^9 {
<ul>
/ V2 K6 A; C. n# j<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>: v1 H. a3 D+ g* r3 A
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>
/ U# d5 X& \2 ?<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>! r$ @/ O' F8 a+ A
' j! O( q" m6 b: u2 l

* T, a$ Z: F8 z/ ~2 }& |8 i1 [  N- v& f9 P6 y: G

; F* B- d9 E  [</ul>& Y, _$ p. b4 V! \% g/ U
# x7 f  T& H9 |( M, ]2 q, V

' a: j, x5 `+ Y2 b2 E) T
6 H# g7 Y3 L+ p. g( o3 A' [8 x/ q) I6 v/ z6 Z  @' T
</li>2 R3 k0 c" M  \3 c
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):% _& d9 ]4 E8 h
<ul>
0 b4 E5 Z/ ]2 S" p6 V<li>) G1 Y4 w! w; |. C3 Y
<p data-tool="mdnice编辑器">适用于两个场景:</p>/ I$ r  I8 K( s% v
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">& B* ?7 M9 U; b. m; |% a
<li>PeerLastZxid小于minCommittedLog</li>
4 ?" E& i( M: g& H! x<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>7 O# [, d6 P0 G6 O

3 [* {) D# ^$ p0 z* {' H
! T7 M# o. N+ D' F5 Z4 M) |
5 n9 n0 b, a/ o' e. w9 q/ i/ w& |
5 a. ^, O4 t) @7 [0 [% Z</ol></li>+ {! d( g5 ?# f, M% M
<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>/ r* _# u+ L! R7 N
! K& s) b5 a# |- d0 v# x  C: {
" L6 U' d7 T3 k, H; ?" t' I

# f/ |! r7 ~+ ?- L
1 y- J! @4 F, P3 R+ S</ul>
9 V# _# F6 Y1 _& n  M# B
) n$ Y9 ?1 }3 n: D% t' t
" H& E# |3 O, ^$ [2 \+ q9 U  K  h' V

! F3 q% l! q2 @8 j  b</li>; z* w, b( L$ M  f6 `7 u

/ [, V5 y% D+ {, [+ p5 K) k- \' w: o) P: R# M
2 J! x, }) M4 L$ V# m1 J( R

4 ?* l* I: E1 ]$ `" S0 `</ul>
/ J  a& \1 A- \* [+ n' F+ E<hr>
* h) ^' \! v. Q* U<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>% O! M# p+ O  M, x! m+ c
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
3 K$ Q5 j# z$ C; o+ k& J0 e<ul>
% t+ y+ F* V$ }% `( o, ^5 w4 t. B3 o<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>" J' F1 v5 G& B' }) u* [
<ul>9 P. v/ I& \  e& F2 S7 F. h
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
8 K: Q' B. m8 ^<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>% L& P6 }0 G! r3 P* f8 P/ K
+ \/ d% u) s2 P8 T: u8 x$ ]
2 W4 V4 |3 k4 C: P' j) z. V
</ul>! ^; G6 L7 u5 y# v6 \3 C' V
0 Q8 @% d8 Y6 x, B& N
% M4 h* l+ h. D/ j3 D# M4 G
</li>
8 O- M5 x& m' }# T<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
8 A! l* X5 A% J<ul>1 Z; ], ~  ~. Z7 W
<li data-tool="mdnice编辑器">
7 @2 A1 `$ O) e: d& Y; O<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
# `2 l/ I; H: Z9 B5 o$ I
  W9 E. H, q! q  U/ K! M
7 A! a2 P: d* v, U4 S" I8 ~2 i</li>
0 y* C2 U: q- N( q! |. n<li data-tool="mdnice编辑器">0 |. |+ x/ }* j0 O5 }& {. z( E
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>' M2 d  `1 E) X) ?- k1 [4 Q

' i! J9 e* Q) a. k5 n, ~8 Z
2 @. }; j8 l: o</li>$ `5 I' f4 V( o

. r/ t0 G% t/ t/ Y( T, `8 p! D# f7 _
</ul>
: ]) l# w% A9 j& M* K
/ z* c- m5 {/ U5 _" l* b' L
  B( |7 H2 C' O7 x. I! \8 D</li>& L  v4 g  J3 ?; T5 a7 ]
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
3 I; h' Y  t: V; F* d% E3 F, E<ul>
' t3 \( ~) v7 p; ^3 t1 _<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
8 v/ f& G) p! v" y3 N  W4 V8 F% K9 d* u$ r

5 G' R4 P) J0 M$ f* `4 J5 l</ul>- S" s% {% ?5 Z0 \% z# e4 M0 t

- Z5 r' C0 S) M' {, N7 T: H
' v# a' A' \1 U- @</li>
/ \+ z7 h  h4 s% H; t
: J& t- Z. t* A: N! w6 C) U5 ]7 u" q: h
</ul>
; [$ T" H3 _  I: P1 Z<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
) k0 j8 I5 j8 \<p>zookeeper 是如何保证事务的顺序一致性的?</p>
/ N' y. U5 _& l8 y# l7 }<ul>9 }% {0 U% b6 s4 D; g9 E
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>9 X& `5 F0 C/ N! J

" O; }. X; N9 j  L, B  y5 ]4 e3 e0 b3 U/ W' f% C9 M) \
</ul>
/ F" _$ v* k  t<hr>3 b9 x9 ?6 q0 z1 `0 ^) q" \: ~6 w
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>! P6 y0 l/ C% p& S& Q2 i
<ul>
9 f& \: Q  V; N+ L) P! c. E2 ^( |<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
6 p5 P" D) W3 k7 T/ f1 q
( r5 b* e' m. |2 W& W$ R) L) @3 q% C4 W
</ul>* d) C+ e; ?8 [3 z' l
<hr>
& W0 v7 ~* K* n8 X0 a; f<p>说几个 zookeeper 常用的命令:</p>
2 h- l7 ^% @( j5 P- o& b<ul>
; K, F$ |* s  x8 r& I$ O2 H/ ~2 k# \<li>ls path:查看当前 znode 的子节点</li>1 p0 ?' _8 M6 y, W0 u% Y; Q
<li>get path:获取节点的值</li>
$ }& G% V- J0 m. c) y. N4 H<li>set:设置节点的值</li>
  q9 H0 C' s/ J) l6 ^% s<li> create,delete:创建/删除节点</li>7 _. T. Z; B2 ~/ C+ e" R( `

9 \+ u2 F' B- e. N, d$ O/ H8 ^) l" L  H5 f; |6 |% j8 T. G, G
</ul>
& q7 n' O  `1 O  k: U* h0 E<hr># n  x8 J) f6 z& B8 z' \/ ?
<p>会话Session:</p>7 L; I2 N( r- N. U2 o- S
<ul>
. j$ N# r- B- J$ |/ O4 z<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
! T0 Q" c: e. Z: d/ S. y$ Z
& |5 a. B& Q9 V9 R9 n- E6 k
+ ~0 _9 i- E/ O</ul>) N& c; \# U+ w, C) ~; H
<p>&nbsp;</p>
7 \& V9 N. D1 \" e( v8 J& _<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
. n4 \" V+ M9 n6 a0 f) C  Y
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-7-22 20:29 , Processed in 0.071434 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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