飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

8993

主题

9081

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
29309
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式

+ }; ]% h; q( Z& p! l' ~$ P<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>
4 ^9 l# z0 s2 k5 }( ?5 |9 w<p>&nbsp;ZooKeeper 是什么?</p>
- g3 Y9 A& X, O9 {<ul>; M5 ]+ l5 i. ^4 n" L) e
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>
7 O; {2 A5 M9 ~; 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>1 X  l! R6 i0 Q3 v) x
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>
' y- y' z- h( X  Y</ul>/ i; c; t% s( E7 m; j% Y+ V/ H/ {
<p>Zookeeper的作用(应用场景)?</p>. H; x. K) j8 F: d% \. l: X
<ul>) Q2 ]7 x  R# A7 ]1 y- f7 D
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
) N) |7 L/ r/ x0 k<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>  c6 D1 j0 w3 F! R/ U  W7 O0 M- k
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>5 b" [) X4 y3 d' G% l1 e7 F4 ?5 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>就可以通过ZK调用。
, z2 g7 a& w, H/ p: B; Z1 K2 m6 r) Z<ul>
  l+ H* J0 e+ k! [3 A<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>0 q7 j; [  t( B$ X0 N
<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
& L8 t) A2 `: s: |6 V4 _<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>  [2 ~$ }( }, d* V! S& Z
</ul>
7 u( a. d9 \0 r) ?  O</li>* j6 Y4 U" @! B4 \; `% h; Q$ O, Q
<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
6 ]' b7 v' x1 U</ul>
5 S" p9 Z1 Q0 r5 b5 ?  h<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>
+ f% H5 N0 Z3 ]& v9 Q1 x<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
% B  q' A) J% L8 V8 ^+ v<hr>
0 |3 P" [- s1 P- {0 I) C<p>&nbsp;文件系统:</p>, H) O: X: Y+ I* ?7 K5 i9 C
<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>
1 t6 e$ S# O8 B4 Z, ]<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
2 A/ A$ R: G) H2 B7 F9 S<p>&nbsp;Znode节点主要有4中类型:</p>
7 e: S& f9 Y, \- p4 k<ul>
" z: U3 S- j8 y7 j' V. @<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>. V9 r* ~7 M( |3 M- u* f4 K5 M
<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
( i4 `0 \* j9 O5 `<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>6 L5 g& r3 L, [7 @; }
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>! `/ u  |! w4 Z
</ul>
+ b$ M( i! _0 Y8 y<hr>
  u* s. S  U" G' L  k  ?<p>&nbsp;通知机制 (监听机制)</p>  q. ^/ ?; k& \/ x- |
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>1 y( `! W/ l' d) 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>' H* i4 D2 ]6 b& j
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>0 F* z  @6 i) C- t2 t1 C
<ol>
; h" c0 \4 O- X9 z; W) F<li>客户端向服务端注册Wather监听</li>
* Z0 o2 {) I) H# p$ C$ r<li>保存Wather对象到客户端本地的WatherManager中</li>
; a9 B9 X* r& `" Y) h<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>
, }% t6 Z) s8 H$ g</ol>8 t5 n5 O( \4 e' C6 [. K
<p>&nbsp;主要监听2方面内容:</p>1 N# W# n# Z3 ]" \* g: R5 ^# v1 `  a
<ul class="list-paddingleft-2">4 n! u7 p3 q. J/ d. |. L% s) X
<li>4 `' t  q0 }9 \: B0 o, T! U7 V
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>! u( S6 W, H% a* C
</li>
* R) }) H2 g) X) F& L9 R<li>4 M* P. X, J/ V, P
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
1 h& V. X" D7 w5 T& w</li>
; Z$ ^% ]) A+ y! U: e5 o+ a</ul>& s# R# v! D$ Q/ @1 k
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>7 [: V! X! C9 a4 \9 i
<ul>2 \# Q8 Z/ Q' j2 F6 }9 G0 k# [
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
$ K$ v* e# r6 [& ]; C. L& S0 D<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
0 T# P& o, A2 I, {<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>$ q% ]4 }" ^/ X- s4 z
</ul>
0 m4 ~8 R9 }9 V( I3 J7 L+ |3 F: u<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
5 S& ]" N( s2 |6 f3 v' H2 Y<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>
5 m" l0 l* Y8 U8 p<ul>: g" r4 e! q( Y" R
<li>Leader:负责写数据。(写数据都有事务)</li>
& J" T/ K( z) }! B- P4 L3 A<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>  \% V1 N3 e5 Q3 ?7 Z5 u
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>
5 ~4 @6 ~; p+ v  I; x  w) ]3 J# r; S+ y& G  t8 l5 l8 l% K# {& n, |
</ul>$ J1 d$ y1 W6 H! O
<hr>
! U/ a- |, u6 q: ]<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>. @6 M9 k6 A9 O3 f" E: V
<ul>
# ^6 U2 e2 d! U5 q$ T<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
" P. Y9 E% R! x2 {- c: Z% x, J<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>7 y6 d1 ?; s6 x* q9 G0 ]* A- d$ h
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
! x  Q% q  y$ @% N<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
. c- h. f: L: |/ p4 Y$ |
- c/ [  ~: r2 B* ?" N" b" x* q</ul>
: M" B2 G1 z7 _  {4 Z; o% N' r$ B<hr>
9 H8 d3 m) z: v0 g) x6 z4 n<p>其他概念:</p>
' v3 k  [1 j; ?( C<ul>7 \$ q4 L1 O3 f: @' N+ o: G: G
<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:$ d* v! M) J+ d1 Q3 [
<ul>6 w/ ?6 l& N8 {# D" u6 b
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
* ^9 A8 v: u4 ~8 W<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>. H4 u; v9 e. `- I, K' \
& J- z. T* r4 I+ W! D

& n5 a% ?6 D1 n1 P6 G6 _</ul>1 @  w$ k" T% o3 ?2 L

# P* g7 X% z$ J: R! {; i# ?# k6 n& q# Q4 S( U6 }
</li>
) W1 J1 Y* N! C# U; q1 u; j: L3 G1 x1 l/ s, z6 w  k8 c: b

, W3 B2 @8 X6 Z4 x- c( y# L0 R</ul>
% Z! ?) b2 O0 K5 o: @2 S- q) m<hr>
4 g$ S- i/ y1 `/ G<p>写数据原理:</p>2 ?# {- v+ T$ d  P0 p$ J9 N
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
1 Y6 v3 I" T; R8 F4 q<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>  l2 }: d' ?) b; ~' l
<ul>
7 O/ _, h7 b5 z; h# a<li>写给leader,leader再通知其他节点 </li># W" ^- c2 g8 F
<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>
. Z. I7 W5 l' ?5 |# `" T$ [<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>0 `# E: `% F* c9 ^6 X( n

) T/ P7 g  L7 l: [2 H; T8 d) u. a5 ~; }! N  a$ B5 [
</ul>
; h( S7 B! p8 z. T& ]9 D- U<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
( O8 c4 }3 w6 h<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
5 P( u0 N3 _3 Z# t! t<hr># c; h7 x& g/ b- Q+ P
<p>&nbsp;ZK怎么保证数据一致性?</p>' H8 q. j+ P: S& F! 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>+ t: V, f+ n: h2 `+ n) C/ Y" I
<p>&nbsp;一般我们正常是消息广播:</p># ?6 ]: \0 V% ?  t2 P: V
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
$ q% b$ ^# D& `; M: T0 c, ?<ul>
% P' B" a; O# R% Y<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
6 @: X0 C) p5 i- `1 {6 C<ul>' d0 A9 H8 r* r& G$ v9 [
<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
: E2 l, m( C$ }+ ^4 n7 M: _- D8 n4 V9 S<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>0 q2 L$ b0 H! r

4 U* ?( _: l6 I( U0 a  _# o! v5 O3 e; _6 [
) Y/ y" D: P) Z0 j& G8 N: r4 C6 X) i

" g" R% v# b. V$ `$ l
! w7 ]0 F4 `7 k0 j- g- ?" R
8 O% U/ ^4 y/ [: E* y</ul>6 y2 m/ r8 p5 }2 c* y) E/ n" v& ~+ N* O
# S- v5 E! T8 L  n

, c( [5 o2 K% \7 H' Q- A# ^
/ r$ ~4 a7 y8 Z- M& r1 c: T6 Z/ ^) \0 R2 {" M) M
- k# }3 k$ @* o) O0 x0 X

. z% \+ ]# e5 D( n. p* H. p- N</li>& \4 K# `/ ^8 f4 v
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
' ^/ s9 f# X* J  @. ]- Y- q5 D<ul>
5 w5 p# I; A6 m. E<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>3 x: m4 K( f5 P6 `9 e  q6 z
" p, X  W) J! y* f+ [7 w3 d: G, p
) G( v& n4 ~0 H1 e3 i2 B* R
) o$ t4 x: s+ R2 \1 }9 k: H

! o* N. S, q; }) o; n
8 i0 f2 M0 h: x4 B/ E% @* M
' n4 ]# \0 U6 o5 r</ul>
2 v- u8 N( Y+ x  `- y3 U, i3 p9 U9 I0 ^$ j. W) G( H9 y9 e8 E5 A
4 |2 I  J2 i' i; q/ X% o

% `9 @' Q9 T. P8 S" d$ n# _5 L
# m9 @9 h4 t( Q# z- s& `
& Z* m0 `, {$ b7 R/ M
  _7 O' Y! B2 D- `: w</li>  J7 B: l" q. Z
8 o, W0 W" e" ^9 P( q5 o# l

! t$ o( k% m; g, z; q
6 p3 |. r( G) I2 l' h$ p1 I6 p: b9 r1 u7 X5 K. c# J+ \3 B

; E( S& T" \" e# \: x1 Z# A1 E5 A5 L4 w# u3 H1 g. v$ e
</ul>
. f( p! F$ y* J3 {<hr>
; w$ l) }( R/ a* }<p>Leader宕机了如何进行选举?</p>
, x  u+ y! ]; f( ^: T<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
4 h2 h5 q- f( t$ U& w$ P1 m3 `<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>+ }; H6 I& f1 I" Q, n& T& s
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
( I$ \$ X& u* Z, V<hr>
6 N& |/ m2 p) `: W$ n  Y/ N<p>选举后数据如何同步?</p>7 _1 v) L  ~; B. e0 ?
<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>
+ j5 r+ x- e2 P/ U- b9 z! d3 E<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
! D& B2 `. T. d<ul>
* H) Q- k5 h% q<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
9 y' i, c$ ~" C3 l: S<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
0 v9 Y  c- m# a<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>7 w# c6 T' r" p8 x4 f0 W

) `$ C) e! t2 D9 l* C5 A
8 ?3 U, C, T% x" n" ~* g; U* Y, X& o, b3 S, W. I6 G

: e# U! a/ @2 `+ R, l4 V! A) Q; [1 H+ R

- p! u& M  [5 F. [: ~9 H</ul>
# G" K3 i4 x+ M6 i$ ]# L" X; C2 L<p>同步策略:</p>" f5 c; P, i, ]. G
<ul>
4 q: G  i- p0 h, Z<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
% b& M+ ?7 Z( K" c% ]<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>
5 l: O% o' o" W: W0 C8 t8 h<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>- g" A2 _  K$ G, 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>
$ L; X% R9 ~5 {/ D) E
' ?9 C+ A# y- g2 Q4 a+ V
4 |: Y1 T0 P7 {) |6 _/ q% x% Y+ f# m: p- j9 Y

9 F7 d/ w: e9 i8 D</ol></li>/ m1 m% M, _7 H! D( s
<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>4 G' i& T: F. c
<ul>5 S8 S6 {2 x$ k  u
<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>
% R3 a( M, Q  _: U* p<li style="text-align: justify">
; n% Y# |' ?) t9 x& e2 ?<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
+ d- w0 _3 o7 E$ X% a3 i: c
! V' N2 w: Z9 W1 ?+ @3 I9 y! P# `% L2 S5 U! b
4 q$ y* C& \" `& ?0 a; G: }

# A6 X0 `# x. ]/ `& y5 n</li>
4 O! Y6 X3 u+ U. O" ~* R' _
. o+ h* d1 U1 B# e5 w( j+ O
0 |- X' `! y7 y+ G
3 V3 Y8 g  T6 Q  {6 ?* p5 [
2 D  i4 ]# G- F; i; v9 @2 Y</ul>5 Z' w/ m- j3 ^. F3 j& x* S; q
+ b9 T/ E7 v( G4 C$ k

) Q2 T. N9 G$ b: h+ V
- Z- h' j. f- P' C4 N' V# V
6 o3 S- c/ H# H9 I4 p6 z9 ~) k</li>
& J: O: m. O0 R4 J! i<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):0 ?+ K4 O3 X& m9 E/ K
<ul>
# ^$ g8 [# ^8 ~" e<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>' ]0 b. L. u; [$ l
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>8 c* U  H6 a: K7 z& P' g( M7 `, Q
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
) I5 h# H" h: ~( f: r/ G3 _4 l5 h6 L9 l, h; Y& W7 j, p3 i

. D" v' Z0 k6 \; q! i/ u9 u0 q2 P6 q/ x2 M9 D: ^( h6 V& X
; d4 h% I" c8 D0 j1 l1 h
</ul>
2 D6 f" C# d" y2 }& t  \6 r( T8 K" G7 N" v- ^
4 V! U7 T- {) \* g  E4 ]$ c

: J, U- w2 G& ~% C: J$ V( N7 G4 q1 M9 f* u" V' C& o
</li>
  P0 B, t# @( L* T+ M0 s# b<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):
4 c- i1 v$ p9 ^' a2 t+ q3 ?" G<ul>( G% B' q* w4 M* |: q* C6 ~! k* U
<li>
3 z" c% m: p1 i1 F; S1 }<p data-tool="mdnice编辑器">适用于两个场景:</p>. x& n" O- s6 L4 T4 Z6 Z9 O8 y
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">% o# x5 a8 }2 G6 V
<li>PeerLastZxid小于minCommittedLog</li>) s( K# R2 D5 R" G  f
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>
0 U  T( I5 y$ N( I/ }5 J6 u  A+ r5 e* P. {! R0 h

( {1 T7 P& I# p) a- _- x9 ?! R8 q2 S

# m! p5 l: G! C</ol></li>
( h, P1 l! j& B1 X<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
2 ~0 ~( M4 p* h, ?7 P. T8 Z0 a, r  O. s

0 G  w5 q8 x: F3 @' h0 N3 J( ]+ O( [5 D* h9 ~! _

& V7 S8 c, g/ A2 p) F$ r</ul>
, [6 U/ _+ Q  q( {9 l: l+ L( L( k0 X* I( H0 ?# |8 S

; S; M0 a! u- F; p
4 U" f& g- Q- V5 `& R; x. S) W% y' e( I2 w& b; z1 O, _
</li>
& E7 W+ l7 \" V3 l- W" |/ t7 x  ^: o6 }/ M5 a
' @4 C2 G. p" s
' c* A% T8 G1 u0 g6 S+ @" _7 d' O
! S* C+ K2 ?* Y8 }5 c
</ul>3 |: o  u/ A) b5 @
<hr>8 d9 G% \( ?+ t& F& k0 v* T
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
. j: _0 d% h( B& [9 L6 t4 V7 V8 B$ ^<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>- K- b5 b0 c! N6 T* {* I  K
<ul>; _4 E/ e! Z( j1 J4 E
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
, i+ N1 p# _! Z6 \' I* p<ul>- P" r9 G* n2 l1 z* R
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>  `6 |( `% q3 [4 n( L
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>9 z5 B# E7 W8 N+ n
# b, h/ D% ~# b9 o
, I( _) u0 W# [# r- l" Q
</ul>- K% {' i/ q4 \/ _
, @3 F# H( L1 i" z3 D2 W

5 u! s) H- }# t( Z  y7 X7 v$ N5 K</li>
2 _, I* S0 L- M<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>+ C& c; f4 e) e
<ul>, M" @% M0 O6 q( I% [& b
<li data-tool="mdnice编辑器">
- p) Y1 V9 w& ~3 Q0 T<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
( R) z! ]8 j$ @! @8 ~1 N4 K, i; W
, y1 Y- b5 v; ^6 l) ~. G  R4 Z
& L6 z2 Y# I" b- s$ e3 C1 e/ u+ d</li>+ h3 |$ v! u9 T7 R4 F0 e$ Q9 H& E
<li data-tool="mdnice编辑器">. w4 n, G# x3 z% c0 _- B$ Y7 H
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>& r3 @) K8 o# E- |5 O: i* H

# t2 A( N  R! ]/ m6 g( s! \9 {# k5 N- {4 s+ i4 V
</li>
2 P$ [, w) u- Q) T2 t& g: T: ~7 D5 k) G! t

" f( q, _6 A3 p/ i+ s</ul>
, [% G* s" Z9 K3 E4 \- p' Q: ?) a+ y$ ~0 w# Q' M' E" C
; B8 e* x0 Y; O# }% X! U. j
</li>
. G( q/ H, O, [: |) ]5 q( c<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>/ F  o$ d3 V" ]. i# q: v4 ?
<ul>* Q, Q  y' v7 z9 H7 U
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>. j+ \  k7 z1 f% R. ~
6 K, Z/ g2 {9 D* Z% w1 S
2 B6 ^3 {+ y# O
</ul>
1 M+ H+ x& [+ p2 C, ^/ ]) c% \+ Q1 b0 B1 l: {3 W
  @6 i+ u- Z; c
</li>) V" v& g8 k7 y9 B7 \0 V

2 `0 X8 m; L. q6 i9 ^9 v& i$ I4 {3 s( K# Y- w/ f" X) I
</ul>2 O8 `" E% ?( N' t$ p
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
5 H; h4 r/ w5 X3 Q<p>zookeeper 是如何保证事务的顺序一致性的?</p>
8 n+ C6 e7 Z3 X) w1 L, X1 F<ul>5 l- i( g" [5 f7 @
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
  @9 i3 ^: ]/ v4 v
/ u/ S: l2 G: A: l; V! Z! \% ]
7 m4 f; |" }2 ~$ _# k- y</ul># O% n8 z) }  ^/ l& e7 X
<hr>
- d: C4 h/ W3 A& L$ h/ Q<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>& b4 n# j9 H/ m. G4 }' I
<ul>
  k' R% P' D/ k* d! C; T<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
. w6 r- |- n( Z. ]
0 ]9 `1 {# f" v2 \6 D# R( k8 O: r/ g* N
</ul>
% J/ K. _: M2 h( w- J<hr>
2 |3 ]( u7 Q; Q* J8 u' T7 r<p>说几个 zookeeper 常用的命令:</p># i0 a  d* o4 O
<ul>* X/ ~5 _0 G6 q. F% D1 f) O  W4 ]) x% E
<li>ls path:查看当前 znode 的子节点</li>6 T" G7 H6 h; L
<li>get path:获取节点的值</li>
1 w/ \8 b) |7 s4 w<li>set:设置节点的值</li>7 _+ t+ w% S/ v% K# O
<li> create,delete:创建/删除节点</li>
8 P# D: }7 }3 O) V! E4 i# g& g  u5 a4 x+ ]1 d* K  q) B' @

2 t+ J: Y# l; ^7 [5 E9 R</ul>
4 D2 V. D$ ]* {4 @<hr>
) R' x" c7 _4 x8 P4 y1 y2 k8 W  z6 \<p>会话Session:</p>) a( x  S( B4 Q* b* }
<ul>
! Y8 f& L5 p; q! K; T8 U<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
/ J/ ]# Q0 `/ |. h; S9 f
4 t7 E  j6 _$ K5 F+ W- p  f- L6 e8 _+ O2 p
</ul>
" o, B" ^- w% o/ h! D<p>&nbsp;</p>
% R7 y, M7 Y  e8 _/ c4 [: G& R7 K<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
! ^+ w3 ^& V+ U. `$ n& W1 Q- @
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-7-12 06:31 , Processed in 0.068642 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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