飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

9180

主题

9268

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
29870
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
5 @# b4 p# _/ l
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>+ E* |9 f3 v) D0 W+ @
<p>&nbsp;ZooKeeper 是什么?</p>; d3 D6 _- k3 u& K
<ul>, l* i2 N8 j6 B- |
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>" {& M- X6 Y' F  D( A- A/ L5 _) v
<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 q6 W$ \1 {+ q1 a
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>' H. S  Z; ]! T* ~! H/ U
</ul>
& b- a3 W9 o! h7 `6 C" F<p>Zookeeper的作用(应用场景)?</p>2 t; \& {3 O6 m! D5 _* B
<ul>
9 C- m3 U6 K/ Y8 G<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>6 f/ d# K( ^! z/ 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>
' [' f' Y) M* U1 B" o; r) K<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
6 k- x9 {$ D+ D- Z2 k<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调用。
2 X, {" o  U8 q2 n5 f: x" J% p<ul>! q' ~1 M& m" V! N! K) P
<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
$ [2 {1 j3 F; }& {" a<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
" I3 W7 D$ c2 X; _, ?1 T<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>
1 k  \* c2 D+ P+ m</ul>
0 T# Y) I' \: P$ k& h- K</li>
- t) J% @/ q9 d. F% b2 z<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
) U9 |. ^) }+ t- T2 K) c</ul>
- Y: y9 i  |- ]) |  V! x<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>
* T  Y2 V5 @1 S  W<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>4 u6 B: ~& n1 i) Q
<hr>
7 t; O8 _3 e/ L' a( m2 s. U<p>&nbsp;文件系统:</p>
* w4 _9 R7 g1 J: g1 k<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 H" d& o* f6 v) `, {2 a* d<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
0 E0 H- B6 R% o# ?3 W  x  H<p>&nbsp;Znode节点主要有4中类型:</p>% t* N9 Z7 P3 T  Q1 A7 k
<ul>
' j. q1 d8 ?9 u# H# X( T<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
9 m& N/ i) v/ T) ?<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li># ^1 t& D4 G! c) b( C+ c
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
% C. o" @* k9 L. S2 ]: K, k* c<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>' \- X* q* @3 A! b# z
</ul>
* B) d) Z* }% F0 g0 K<hr>
3 n3 ]$ {8 C2 k3 {. n# B<p>&nbsp;通知机制 (监听机制)</p>3 u$ d  w# S* k2 S- g
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>6 b7 G: n. }: ?8 h4 C' ^, C4 S. [
<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>. b! n& x, Q; v2 X: h8 L
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
" S, |( \$ v+ }) v<ol>! ]3 E+ t3 [4 {
<li>客户端向服务端注册Wather监听</li>3 I5 Q) U0 M- I$ w. Q
<li>保存Wather对象到客户端本地的WatherManager中</li># Q3 S7 l& b% @7 d3 G/ c7 T) ?
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>
. K- m+ s, Y3 z2 A+ i</ol>
4 N- i4 W8 k/ X4 W) r<p>&nbsp;主要监听2方面内容:</p>
, G# m+ m, }6 D- G- ?<ul class="list-paddingleft-2">2 x/ t6 Z/ Q2 ^7 ~5 k, M: K
<li>
6 N% z5 a, |/ y0 _: l( s% E7 J8 T<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>7 A" k% {/ k2 f
</li>
( \1 P( Q" H1 b" C<li>
- W4 I4 i" S- a0 o4 ?  k<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>) d1 e- U2 D8 V3 M6 [2 W
</li>
; a9 n  s2 S8 }4 ~1 T</ul>
0 t2 E7 Y  `+ R& L+ Q' l/ U( ?<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>1 s- R5 `9 e8 f: @% c2 s8 V. o
<ul>
/ Q+ W5 b2 e4 ^5 o<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>. _" q' i, E7 K7 D' B' Z
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
/ b: e1 p( }$ e5 a: F, k<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>/ L) Z) Y' ~1 x+ q3 W7 s
</ul>
% q! _6 F* N2 n% K: \<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>' k; s, [2 u  I2 {" i
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>+ x0 O" O! k* z: F9 h: N+ W
<ul>
- K; t4 _/ F- b, w$ r# `<li>Leader:负责写数据。(写数据都有事务)</li>/ y. x' U& o6 u% z
<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>
, T% ]/ a( G, E: x5 Q) Q4 l" |<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>4 t( @& g# O$ d, I1 K

* a8 a* t* S' [: h/ Y</ul>
% N/ z8 z+ j. g<hr>6 c: f4 R  n$ v9 Z( j! ^' j
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
6 w* C9 A* B+ E: r5 s# G<ul>
, B6 H) @+ L3 c# P  S. D<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>& q, Y6 F# t* f1 S! M2 B' p
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>/ w% ?! @4 }* {* m
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
5 }) D& G" b7 R( h2 X<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
, W/ _' _, `7 K: O8 c8 Z3 Q; O3 w4 ]; O: X, ?- c, ^4 [: M
</ul>
4 y) X! v) F* v$ A3 l<hr>3 q' g3 b2 s$ s6 j" H
<p>其他概念:</p>! F' f6 \' e. h
<ul>
# o% Q, ]: M* w" V<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:& e$ E; q, V  h9 S8 l) K) ^0 _9 R
<ul>& H% p+ i7 M8 ^7 l* f1 }  D
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>& q1 K& ^7 b  M* _# Z! o1 j3 S
<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>5 ]0 R3 _2 a8 ?. w7 c, I

+ o- i' S% R$ o  |; |
$ N' E4 x: ]  Y* Y4 L; W</ul>
. _; i  t- k6 c* X$ U" k) _. b9 s0 ?" T( f3 I2 x& V1 E, f
+ n3 @9 O& j: t7 k
</li>% d( T7 q. s7 |

* T, ]6 V7 l) k' u. |# \
2 |2 N% ?0 i6 _* R$ n& E2 }& o</ul>
4 B7 E0 f" G  X& M" s& d<hr>
' J- _2 \- h/ O<p>写数据原理:</p>
+ h7 A$ ]( ?6 d+ O: h<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>9 W9 W0 C  _4 N6 L
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>: \4 Y+ u! x4 t9 N' ?4 W9 T1 G6 l
<ul>7 z) l  W# x2 o1 n; b" _. J, J
<li>写给leader,leader再通知其他节点 </li>
( f& y, e$ r+ _4 n1 \1 V8 {) M+ B<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>
3 y; K6 I7 y# X2 u; k7 o<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>7 b9 j- B! x: d" V: k: {# s2 s

( F2 t, `" J9 e8 j0 K' e$ A0 z. K
</ul>8 d: Z$ g0 @, q( a, }
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
. {4 l1 j  L, Q/ d0 A, O, N5 m) O<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
4 o* a( g0 w9 x/ `$ P' j% C& A<hr>
8 K+ i# Y8 S9 t7 ~% _% ?8 t0 [% V<p>&nbsp;ZK怎么保证数据一致性?</p>* z) i3 H) ?- N; R: z
<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>9 s5 }/ F; _; f
<p>&nbsp;一般我们正常是消息广播:</p>
6 w" h! X0 b  k# {) ?<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
+ e0 R4 c6 N. w' ~, u" K<ul>5 \9 w1 S+ q  w& w8 M) D
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
3 W" k/ c  m; l5 E<ul>
/ z3 f% ^' d! e% }<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>' m6 e7 Z7 \! O$ J; O9 `* Y
<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>) u% q" t' I% h: l' c/ Q$ @

1 ]& m9 F8 D  v: @3 C- y5 p" [; f: f$ W' P' e6 I# {4 h' t
) A% f/ q+ X* k/ K6 Z! ?4 d
, f: D5 v: d* h8 D: d
- j  ~; m9 |  G
# ?% B& V% @/ j9 N7 q
</ul>" ]" x: \! @3 p. }# u0 [5 x3 |
: a& x# W1 Z: u: H- F6 ~! V
; v; d+ ?* ?9 i  u
  s1 h% X1 h, H4 z6 B; ^

: J9 {9 Z- I9 n- @9 }7 b" }5 y9 ~7 G( I: g

7 D8 ^; @/ J! x4 [6 z2 z</li>
) N1 h% S' O7 V0 k9 \<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3& p0 V) \6 O! m; P
<ul>
2 B4 w* @" l! s" F<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>
5 f! S$ r& n0 @* n6 n5 ^  m8 U0 T
+ ]" k, T* N/ x4 s: F4 E" d
$ @- A4 A2 z1 {! l
+ L: h0 {: q5 s  |; O  G7 K( J+ v* _1 `) W

- @; Y9 f- ~# Y: Z8 i2 d1 G
" v+ g2 U6 Q6 `& k</ul>( `' `# p$ l1 n1 R  Q

$ w2 F1 N( |: `- a) f8 D
/ t' y* a1 l$ a8 u9 U4 h7 ~9 v4 Y) }$ a5 q( g7 p2 }# j
; |' ?+ K. Q. }8 H8 u

6 V0 e  m4 E- r! B  S- v# z* i+ L( B$ r' g
</li>- [: t2 |; D# l' P3 V$ a( s

  n: p8 f6 a  H! p
( t  g8 H+ D$ S9 c9 }7 _; l3 B2 g/ u! e$ O4 d
2 A6 c! `6 l/ H" O6 H
- p; N9 B/ G6 k/ X% R7 l. N( C# x
/ j" o1 J; ^+ R" Q! R+ F
</ul>& Y" F- u( s; N/ c3 c* m
<hr>5 D' ^6 H( A: G- ^8 z
<p>Leader宕机了如何进行选举?</p>8 W3 e2 b4 g. D( v4 U5 g/ b
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>8 r3 L7 A2 X* a4 ?- |' a# q
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>
1 ~$ k8 w& Z( r& g/ R% m<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>0 ?9 e9 H1 r$ [- A5 `
<hr>7 c& u* s9 Y" e0 b  C
<p>选举后数据如何同步?</p>% k; W( \4 I3 P  q/ V
<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>1 h0 e0 s  b  P7 f2 B6 i
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
& D4 T% O- w* [; R2 ]5 }- I<ul>' Y+ q7 G, O: X6 l: L& _& `
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
4 K9 B4 h8 ~3 S' C<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
4 x# A: N" u/ `$ j<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>6 T+ A! a6 i7 I5 L
! @! B: L1 E5 b+ x3 q; Y

$ A$ t! N! ]& u4 K
! P0 i7 Y3 D9 @5 A* ]4 v9 P: ]% Z  S- t

: Y* ]( R! Y- b. I% ]% S2 d" ], `5 @% d# q
</ul>
# ]5 g& B5 Y. \+ X6 S9 ?. W9 m<p>同步策略:</p>
. C- W$ O) Q( K<ul>
, u& Q$ W4 L/ j0 v4 C! _# L% z<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
7 [* X% T6 V6 [0 K1 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">首先Leader向Learner发送DIFF指令,代表开始差异化同步,然后把差异数据(从PeerLastZxid到maxCommittedLog之间的数据)提议proposal发送给Learner</li>
& ~- N: J  |, v6 d9 t( C! H  x# D* 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">发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步</li>
4 F/ P' _; @/ }* g6 m4 J, I<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>
+ I1 u. a# x6 @0 k! b# M+ o* T- Z
6 ]  h9 M  i( ?$ Y4 I0 G
' g& k; y6 D0 x0 H$ E7 a; @: T
. O* }' R5 C4 f) H; v8 k: B( l- u: x! N6 }" s2 ]
</ol></li>
; ?3 d9 U/ p* O% _) ^$ t9 [1 o, C1 F<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>9 A' D9 T4 Z  e4 H
<ul>; f" c" j) c' G7 ~6 O
<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>) p. ~& G7 a" |2 F
<li style="text-align: justify">
* H' q8 x" D) y$ e8 Y6 T! C0 x<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
! }' \1 |5 F9 H$ j, w* h3 E6 J% S0 F0 f
8 M. ?8 t& J; m

- T" G9 O2 X' q5 a6 w
; x2 r0 A+ ]! g/ }* Z# U. O* u</li>7 u7 y. |5 T* B+ c6 Z) ?2 v  ~

2 h- s; b/ G: e- O. f& s2 k
- q* q" v; z4 F/ Q/ l8 L; q# H" f" M" E
! }0 p6 e2 x# M) F2 G3 {
</ul>5 X& S1 [8 W2 K4 ?0 Y  W3 ^
5 L# E7 J. Q, X8 O3 G7 Y1 u3 V7 C
, ~$ M0 r2 L: }0 ]3 p) z
% J8 _0 U* Z5 q7 L% j
6 s2 W: W0 n9 A, P: [0 b
</li>
0 }% s4 f* \6 q) ?<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
( H0 f  P1 A- ]# P& M* q) Y<ul>
5 c5 R: A- C9 R7 N  |<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>5 }2 A2 `! l+ m; N7 f% t) D
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>
3 J/ a1 T1 }/ y1 K( U% u; Z1 D<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
. D1 [9 P1 ^" }  p
: m8 }0 p# U" P. V# P: H( b; H# |0 b! Q3 a$ z: O

6 y( a! Z( Z+ Q0 H3 A2 \' V  _( I# l5 I2 Z7 i) v7 h# I/ X0 C+ u
</ul>2 n# `# G" o9 \) G! u
0 j% _0 O! F3 ~

9 u" P2 E2 l6 G! w$ g
/ n) `" G8 [# ?$ ~: P" @$ K4 s- y
4 A1 I9 Q- H: T8 z7 d</li>1 K. `" W; l- C
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):6 d+ q, ~3 `3 t
<ul>
" ~: f: q* j5 B0 ^' ?: U$ \<li>
6 O7 [' b, l( l1 M: T/ W: \$ P  @" Z<p data-tool="mdnice编辑器">适用于两个场景:</p>
% H  @  D+ s* |" T5 f<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
9 d- }3 s: b8 o<li>PeerLastZxid小于minCommittedLog</li>2 r+ ^2 U1 n9 E6 Q* E0 V9 X
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>% {) D( p0 M: w9 z, J8 d7 _6 c
1 U. C- ~' s. @9 g; ]7 |

2 g  Y  @" L% v* l4 x4 e1 _9 d
2 v2 b! D+ `: X& H4 ~2 D2 R/ P( H; [* K
</ol></li>
9 U9 y/ j/ t+ [5 Q  a( P; d! [<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>9 B# j# N& D  |. j: S# E6 V, [* m7 X
% w0 @3 z3 G" N5 w+ W7 U- C

: g& m* u2 ~" l# i% D. f: J- p: [, k& {8 L; O, F& j- g
* X0 j" c9 D) D: U8 O$ h
</ul>9 S1 }. E. r& v6 ?% @, s

3 G5 B. K) z- A* M# Z
/ d5 H3 b  H1 x
' }1 N3 Y9 }0 b7 Z2 a5 P" r" C) q
</li>, Y: G2 \/ A- f; r  X$ W. t& b
9 T7 I' N2 N; C
" }, t( {/ i) ~1 o9 Z5 P

7 w" b1 i$ E: R' _+ D  h5 {: ?5 e# `$ n5 ]
</ul>5 X; x2 V: m0 N% \
<hr>
- }/ H+ O9 j/ ~$ e5 P- f; W<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
; X  u6 H  c' X<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>8 k( V! \! r* m6 r* F6 S% x2 r1 o' y
<ul>9 w: `& C% Y! d& B# X
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>+ A& P* t$ f4 G; ]/ e5 O
<ul>
; m' {: k' T# J<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
5 m, W# Y* P' e% [7 p  ]9 s<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
% M  _, w# N: M: H! M
+ y! ?* X9 K% a* K/ G6 M) G4 b2 H2 [$ H
</ul>
  c9 `# ^% h( c% |* M1 O) A3 e& P& g

& o$ e/ `8 N2 B</li>  M0 Q  I) q2 I) \
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
; d& ^5 p2 X2 O: H& U<ul>
0 J) E; T( V- z" Q- F  W<li data-tool="mdnice编辑器">- Y4 [# n5 I& \" Y
<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>3 ]7 e6 l/ R0 f( W( ^( y) }2 V

3 M, N7 g# [5 W" p
3 V7 M. O6 E- K8 D# T</li>
3 b( K1 L8 G/ A6 G9 a# N: V0 B2 I<li data-tool="mdnice编辑器">* Z. A$ R/ ~+ ^+ ?1 V' Q2 d
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>3 _9 u# \: W" n! B) R- q) y0 J$ i
; {) F/ H7 y" [( P6 K

1 L) _2 s$ _: ?9 B</li>
4 a7 l) n8 c0 \( K! L( v- u/ V
5 D* w. A' p8 O! [
  `: G; Z3 j) m; ]! ~</ul>
  J- J1 o; ]$ i
+ |/ ?  d+ f. |0 V. B3 C/ h) q
& }  \  X8 j3 |, C</li>
; |# `' G& L3 X  ?% r+ n<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
7 V* v% V! t, L0 D5 O<ul>
$ R; m2 R0 Z3 z<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
5 Y7 q! z2 P0 J. ?, q: n+ w& J- u  M: Y6 |" S
! d! F4 l, N" f- y! ]
</ul>  U6 i8 s3 j! x, u+ x' w6 U
& ?- ~" w6 P8 U' l( O
$ ]  p; [# y& Z8 |1 o# Q3 R) b1 B
</li>
7 @7 _0 |" k" C! w8 j! J3 N. P5 |$ ^8 d. S$ _

0 h( \( P$ t' |0 g; f</ul>; m9 r+ z6 n6 M/ I* Q. s+ s5 j- S0 _
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
0 ?+ q1 T4 y$ v1 o1 V# A<p>zookeeper 是如何保证事务的顺序一致性的?</p>
" s$ n7 O$ E6 T" w: d% W& ~$ \<ul>
  O  ]. g# O  X' V3 L) M<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>6 ^7 w5 Y$ G0 u0 V) A
1 B, x6 c; }" R9 M! N' d4 ]1 o
% C$ A/ ?+ Z0 S6 L6 ]* b( g$ r. T
</ul>6 m+ t$ {# g9 T( F8 H4 ^  f+ ]7 u
<hr>2 I" F4 r  [- J* M3 @( \8 E
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
; C5 v0 m7 J- \2 D1 m# f, Y- t<ul>
4 i/ [. F% _& L2 i8 [8 ?1 }3 K0 z<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
& H# r5 M5 ?3 S! `7 H; X
. K: Z, \( {% a1 o) j
$ B8 j4 I* Y7 P, {# x: [</ul>
! ]- @3 {6 i. _, `2 x  ?9 h<hr>. ]3 r* n4 F$ }/ s( y
<p>说几个 zookeeper 常用的命令:</p>9 u/ ^5 G: u0 `4 \  Q; ^( q5 D4 j
<ul>
% }1 _/ O2 m& T" G' U% ^0 R6 e<li>ls path:查看当前 znode 的子节点</li>! O4 }9 ?: j- I8 C4 k5 D) I' ?
<li>get path:获取节点的值</li>! g% H$ E- M8 |" |$ f. @, s; B
<li>set:设置节点的值</li>
* C, C' M6 b5 Y& P<li> create,delete:创建/删除节点</li>
( z6 S  X6 O$ M5 |/ H* }5 \
1 S6 O+ }7 k5 L; Z" `. s
; t# q- U5 K) x& s# j( |' q; }</ul>
! S$ G" d5 c7 V# _, ?1 `- v2 I<hr>8 Y0 A+ j: y7 F) H- o) \
<p>会话Session:</p>
* F6 m( _; H; }3 N! k: W) B<ul>
; B  S8 I" T( J5 Q, |& Q7 D<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>  n; d: R- \6 v6 y* c

' Y* \. E0 H. d( k
, f$ S! L4 d: j' H- _4 H4 T  |</ul>
1 h$ ?& ^$ X0 L3 ?4 x1 u9 s; F6 b<p>&nbsp;</p>& K+ k2 ^) A. _9 W/ v
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>8 o, L3 D! u; m& `7 z, {* V
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-9-9 11:46 , Processed in 0.066734 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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