飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

6855

主题

6943

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

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

8 G4 Z  {+ d0 f5 g9 F$ _1 @0 D<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>$ K' p4 j$ C+ \" L0 N- b
<p>&nbsp;ZooKeeper 是什么?</p>
/ r6 e7 _5 s6 o<ul>
, F. e' M: U' k+ r2 X1 {8 }% C7 L<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>$ t* p$ P; c3 \) a3 F9 m# ?
<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>
  a" \) [9 u& M( p$ I<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>& G  A& E% h3 J
</ul>3 j1 g6 @6 p5 h$ [; r/ M4 q4 F7 f& U
<p>Zookeeper的作用(应用场景)?</p>, l' @: O" N1 \5 T# i- S8 `0 L
<ul>
0 ?! k( W, H  Q* X<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>: ?* C! m7 Z: h; J. O
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>- R& T4 H, X+ t  H% A% \* F
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
4 F" O# T: W; r' P; M. O4 }<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调用。
0 |! C& ^3 i) g) o<ul>
& j% i: S0 _& E% u3 Z0 p<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
( I, u. ]1 ~: @: C4 b<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>. k% }$ p" u. ~0 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>
; F4 q$ C+ G9 S7 ~</ul>3 {& v- n2 ]% e) C
</li>) h9 R8 D1 e) s' o7 Z2 Z
<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
2 R  s/ P# v0 D9 c5 ?</ul>
/ l- O2 g' {! s! I2 R8 Z; j<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>; ], _0 P# _# F& p( f
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
+ S3 R" Z1 u1 D<hr>: }; g" O& ]! t* p5 k/ D
<p>&nbsp;文件系统:</p>
* J. A! t# E! h: m9 L<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>, X$ ]  y0 K3 Z- n5 ?
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
# u, `" F3 k. v& c' o- W<p>&nbsp;Znode节点主要有4中类型:</p>
8 ~+ G( D+ B+ B9 F& _+ N! D<ul>
$ Z! S9 ?: L, N4 ?4 b3 T) I<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
# ?3 ^! k8 Y/ m* J& X<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
) Y+ `8 }! e6 ^' \, s% b<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>; x5 I  w, V$ N3 X
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>1 n$ o: b* Y- @4 Y# O
</ul># ]# `$ T0 ]9 b4 a* P5 o) e+ q
<hr>
/ ~* }3 \. D- v; L0 C<p>&nbsp;通知机制 (监听机制)</p>
+ @, d0 P* W* ?3 C; C0 i<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
3 g9 o! O* b' K; p% w) y<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>" `0 t, T; `2 i* r0 }. v
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
) z* s, q% z1 Y) i) e<ol>
  K5 F7 r+ \& C/ U<li>客户端向服务端注册Wather监听</li>
3 y2 T1 J+ g; }" i# F+ C" r" n8 `<li>保存Wather对象到客户端本地的WatherManager中</li>
/ M1 j$ `( _! h* G$ i9 G  d<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>
5 F# P( Y- z6 }% ^+ g& t</ol>
5 T9 y+ y5 v& f2 H3 K4 ~6 E, g7 g<p>&nbsp;主要监听2方面内容:</p>9 ?1 C$ B  d, _( f* f! n
<ul class="list-paddingleft-2">$ p5 R7 s) J. v0 P
<li>
5 A) {, [7 F8 O. b& d<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
5 D+ A8 y6 V9 |</li>8 p% i( v7 h, O
<li>0 B& `; F: y. W* y  u# r" Y
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>' B) J; T8 s/ [, b9 i
</li>& |, e8 Y8 }1 e9 D" o
</ul>
$ f, \7 I. g+ U2 `6 v3 |2 K<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>
* c8 \% c1 {2 ?/ ?- z$ E  a<ul>* ~8 R* @4 {: R( y& Q
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>2 B* [' V) f$ y( T5 c4 w
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>9 [  v! Y/ V1 S6 i- |
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>. D! {( d6 v! c" M8 B8 x
</ul>, }9 J% s$ _( d& H, n
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
2 y7 p& K9 I# s<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>  R3 m. h& I4 I5 ?& Z) Y+ K
<ul>
4 Q7 t2 j& e$ K; b, R( E* r<li>Leader:负责写数据。(写数据都有事务)</li>
( N7 {4 k4 g/ e' x<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>: S2 A( W+ g( W5 @( s4 f/ S  _; p
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li># H" \1 s" q' ~3 E: v
8 R. ^2 y2 ~5 `2 X4 n9 Q) C  A
</ul>
0 P( d! K- S3 t% C! K<hr>8 E& \* W5 v' {, _4 ?
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
, M  `# v, }* a' F<ul>; h- s" o' g7 o, f5 Y3 `0 C6 W& h" R! r
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>* w4 d3 p" v! F" w) u5 j( M: }
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>5 M6 ], x% t/ E# P
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
. T, H8 H( J) b0 c- Y) N<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
# Y; J! q9 o; `! x
; n, ^5 _( O6 S</ul>" ?5 h) K/ ^6 i. u% G6 `( K
<hr>$ Y9 y6 D  u$ o! ^
<p>其他概念:</p>
/ W2 I) _/ y; t# v- d<ul>( {3 `  L0 c* E9 J
<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:& Q+ n7 y1 A  ?( }. k! Y
<ul>
7 N* C' |6 L: O6 j+ ^2 M$ u<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
* c* Y) h( |; r# s% V$ ?0 |; u) O& \<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
1 P5 [. ~3 Z* _7 F2 m  `8 ~
; W( U) L( o+ w! g* f' I' @- z
0 [) G0 v, Y- i3 `/ F</ul>- t7 I7 F; k2 \9 l  K; c6 s
' W4 l& F& G  Q& R% r

% R+ P$ g9 h8 C* k</li>, q8 c" u& E9 S, p0 y$ z3 L9 ^

+ u; b4 g) z  w' e( @0 v: `9 X& h! j7 l
</ul>1 h# C8 `* @! j- b$ }% s3 r4 |3 Q# }
<hr>
  ]& B! t5 w' D2 t( l<p>写数据原理:</p>5 y3 s3 X+ v8 f  h3 w: [
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
0 z. q5 A- O' N<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
# }1 X  E0 [# g5 }: \' `<ul>+ R, B: w0 w8 G9 a0 h" }2 J
<li>写给leader,leader再通知其他节点 </li>) Z( F+ O2 @4 v
<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>
7 b7 W& l5 K2 G% z+ k  p<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>
$ s0 N  Q  n5 u8 e4 s# W
) m: {2 v4 [2 n+ c( k1 M( K: P" j) J* ?  T" w
</ul>
' a/ C" B8 y3 d) V, C<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
0 n$ A2 P8 ~; c  }. ^<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>1 q7 X7 m' F1 g2 G3 N' D  m0 R
<hr>
! Z1 i, T6 r1 W<p>&nbsp;ZK怎么保证数据一致性?</p>
  H& J9 k1 i5 L0 ~3 ~/ }7 ?: e5 U<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>
" M3 c$ u! U0 J( z# j; ^<p>&nbsp;一般我们正常是消息广播:</p>
9 b! f' q7 h: K! p2 W  ]<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
9 H& v3 n, G, C# N$ s! J<ul>
2 g/ F8 f$ g- h$ Y0 O% }<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
3 p5 c! O+ x* w  x0 b2 G<ul>
) i8 U: G. \" i. z7 w! X5 D' ]7 K& p  F<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li># {# @1 e. v" w
<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>
, ~, \, W0 I1 L) Q: r' m8 i- Z- z' ?& C6 Y& ~5 m7 e

1 A4 `! C; c4 c! X- V) I' T- V  h1 u6 R7 ]# X7 ^1 C

! o2 k! ?* D+ P3 Y  v9 q0 f
- t5 [* x4 G( B* m# ^* k8 e! o8 ^2 q4 X) r7 D! |8 M
</ul>6 e7 t, W7 V4 r( Z$ x

) Q4 H$ w) h9 `, h, m
% [1 p5 i4 S: ]6 u: o% h  q5 I- F6 m# u! A! H5 @% C

$ B/ P6 q" j: {6 q5 M" t3 a! z% a2 m3 h7 L+ Q: i/ Q$ F4 Q' q6 y

7 v/ T7 |9 H! t7 J5 ~</li>
& x* E  t' m3 G! B( W; m  u<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
& d2 F) X% g3 v$ U" L<ul>" d# {" l% a( C* N) ^/ t+ u
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>: k! N& o* q) S- j) i' T' B6 ?( S

8 l! ~5 T0 k! R/ ~1 _
- s1 Z4 s0 c: W
! P% V' R/ M, S; H" C9 s5 r5 k2 N6 g; O6 |' c6 w2 E4 `5 N' S

) w8 ]7 A( }: N& p- R4 c3 s7 e4 a
; c2 g4 U" b8 t; e</ul>
+ }1 y+ O- [7 Q# F% b/ c; q; \$ K% b9 I% Q$ N) S( \; |# e8 [

. v- S* @4 _6 r' _
* G5 h- _3 |0 @$ }: j/ N- q% @" a  s" Y5 V, ^0 }

8 \) c8 K8 X2 q
2 J' c. H' @. Z7 F/ ]</li>
. w' K4 X" t9 h5 Y- |$ r! Y2 o* }- n8 R0 ?6 W+ y# g9 Q+ P3 ~

) G3 G3 C2 r. c2 l. C
3 r7 J" Y7 N6 D: L2 _# m) X/ {1 D4 r
- V9 M( ?2 M% [; V
* w' u+ M1 E2 L+ }! p% Q6 m% I
" ]) b$ k+ G* R- g2 z</ul>8 {) U' q; v6 S( p9 Z
<hr>
) V1 a; w! \2 w' Y5 z( n' s<p>Leader宕机了如何进行选举?</p>+ ?3 s, E. l2 u1 ]
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>' N- X) W. V1 r+ c9 N, Z0 N" o
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>6 C8 R( u( V# a
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
. T! d6 ?* W) I" I( ~1 }<hr>$ y$ o" H# n( C" _
<p>选举后数据如何同步?</p>
4 q0 i3 M: \  t<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>
6 Z3 K$ |4 v  Z# M2 R$ W<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
( ]5 [7 ]* R+ L  h+ ~<ul>
5 g, k3 b2 ~3 Z7 ~. r. s  ~<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
3 d/ ^% k/ u9 U# t<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>* z  D% T5 h7 w7 y
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>
% K8 ^4 e" u9 k6 Y1 i$ d4 a. E( @
, d7 c+ t1 U) L1 J9 D) x
. q# G- }& M: P: N0 R- X2 y2 f! Q

% {+ u% M  p& r# s
! D& Z5 D) t5 x! f& D$ s# L
+ J& \8 c# r0 D7 E+ C  H" B7 }. x</ul>
$ v- e7 Z- _5 ?% c$ [4 f$ z<p>同步策略:</p>
0 E( z5 F7 Y' |# h<ul>
0 Y/ m) P4 J9 x' D7 a1 l2 h<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
2 \9 k3 s  N! N4 o& B- `<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>) S. J  g9 B( G  c  [- ?3 m1 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">发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步</li>
1 G' G# X/ j0 G( J5 ?5 w- N<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>  c( N% `( \" _/ f( E7 H- l

" r& M4 j  T8 G. X$ ]+ H9 \, C9 _- s

; p  C$ d# W6 Z* w" {% v: r* V: x6 k, K; x
</ol></li>
) X4 s) x! ]/ o<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 m# I7 g8 A  o<ul># h: ]* q; z" ?. \/ X% O; O: p& R
<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>
! ?5 U7 d9 q& f6 V1 A% i4 Z<li style="text-align: justify">
5 M  q" n. t4 Z! x- U- F<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
4 \; a& z. q- e- H
) _: S6 D& l( p  q
9 j$ V' @0 o- r* A% `. [2 Q; z) t: ^" K0 T0 p
" v$ A1 {" K5 ?6 W$ Y
</li>5 t( x. v# q! _6 W5 m
4 j/ f" r& u5 j$ \) _/ X4 v

0 X2 w2 K5 D# A3 n! T7 w  F7 a3 T- S% [

9 d& O# i* x/ ]3 N3 t2 r( k* L) s</ul>
+ t: Z4 f: p- L: ?
7 D" E) J1 ^4 M# i, u# k
; v1 u8 |& J+ {. m( s% G2 I: i5 R1 X; x: F" m: J; J8 a% [
2 n0 {; I8 p1 Q; `( r
</li>
! C! B+ @$ [9 j7 H<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
0 \' P/ x8 H& t4 A) j* y<ul>5 d  s3 y+ i+ J- g% g
<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
1 U9 i$ P# w% K3 B. W<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>
0 j1 ?  e* Y& l  V! d% X<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>+ l' F/ D) T, t6 Q
% z! n" A2 x' A# ~2 L/ h7 p
8 h0 n. j; Q! [5 ?3 s+ H
9 \) R1 d. R/ |: f- B1 v' q

3 d  u% v# X/ G</ul>
% o% [* I* b) o: \
, Z8 K) m9 X7 r1 d; [& h) y. s0 Q7 y' H; U% W( ?2 ?
  m* Y, U9 `3 H; ?# q8 p$ n/ v7 j
8 b$ D" l# V" m9 ]8 c6 q4 I8 _
</li>9 j. |  p( T! e/ u
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):
/ U) t3 G3 z* c7 g5 O5 i4 ]<ul>
% [% R- j) e: p2 ]<li>( p7 W  z) t2 s$ K
<p data-tool="mdnice编辑器">适用于两个场景:</p>
" n7 k: Y3 t: }- x$ m2 h, z! d0 p<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
7 ^+ r# Z4 r  V/ a' N9 @<li>PeerLastZxid小于minCommittedLog</li>  ~; N2 I" e  A9 g% q, U
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>4 [+ i% O9 y0 T( {5 c% t7 m

6 \. n, j( @# j. |$ c1 U! r- B) o! \% h$ C  _

# I+ i& r4 B& k1 g
9 P$ s; O/ Q4 h1 Q6 c- [  }; i4 E' H</ol></li>
! |! o0 F6 Y+ m; L- [& R<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
2 o, A4 n8 O& D2 E! J3 X& f4 `/ M# T4 w# X  A
  t9 b0 j/ R( n# Z$ Z( g
  z, H1 g; E; |1 l' f( ~: D% Z
6 {/ X3 q8 @* d# d$ q
</ul>; l& ^: ^" n1 L- I7 Z, \% J
& L6 }+ B% o" v# @/ j. V
+ V+ {3 u& h) ^+ n: {

3 b, k" }9 k+ `: }) }. J$ X! @7 E' t$ v
</li>
5 v' ?7 \, T% |* E5 |4 B- @; p) H& \0 p, D! M! U. U5 ~
' G+ d6 j* |2 f/ i8 ?6 ~5 z

; B4 c5 B8 {( Z4 J& X. b" \! x: Q' S" E7 U' m$ Q0 k
</ul>. ^/ R( R+ Z. t1 P/ k5 b6 g
<hr>6 a! o& T& y6 \- A( T1 A8 S, H
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>/ n8 M; G' n* p( W9 A. c
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
1 a( U, R2 x: P& r<ul>
% {7 E- A9 M* a3 U<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>+ S  x: |$ Y) a& o& k
<ul>5 p0 f, Z1 w0 l% P7 @
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>9 a5 [  |: }; q2 t; h
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>) |' l6 h1 }  x
  f; T+ ^, T! ~( Z, X* f1 S8 x, {

# F# C/ x1 B7 [$ ]; j+ U$ f</ul>, [2 C) Q" A7 n0 ^& D
# k9 `) d3 k% n) ?" q

! S/ N7 x0 u, @! t+ b: S9 S5 p</li>
3 f3 E% z" l+ @% R! p, K<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
1 w5 o7 B- i/ g- V( n: V<ul>
* O( ~0 q# C" J$ i( J1 u" f5 Q<li data-tool="mdnice编辑器">+ H+ w# W* M5 F; E: {  i
<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
" Y1 [2 ~% n7 G2 `9 m0 q6 }+ c' D5 T/ Q  r: }8 L8 u4 ^7 X% j

: b) V6 c. i. Z4 C</li>
& M. i$ d' u% Y! w<li data-tool="mdnice编辑器">( v$ o0 A8 y0 C& H, w
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
* v+ m9 g5 g. r0 _! r" I7 C. ?6 c- V% i
4 v% t  ^) S& t# T+ q7 n0 U
</li>
3 K) C: j' J" I: h& g
' W# [$ b' e7 W  w2 z( m3 O/ }5 ^$ Z" \; \; S% s5 ]
</ul>9 T7 c* M; o+ X. F7 `  f* w

% F* f0 I5 {$ s, J/ u
1 t( r& E& N0 R& K; b</li>& B9 Y" \: f, j) [
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
, x& {& g+ R3 y( ^2 E3 A. S4 m<ul>
, B' K& }+ A* B6 e: o<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>
/ J3 C7 ~. @4 Y8 S, `- {8 r% G# s, f' _9 A# L

! h: c( A1 u" t: e2 @: P8 _! P</ul>; `  L) J5 h! A+ A

) @7 R  E' O' G. D' ~3 a- I5 j9 O
</li>
& G- \- g( S/ P1 o, Q
5 k. P: }4 b0 c. J$ |' H' c0 F5 W# N9 n3 r7 S3 c
</ul>, t9 A6 `4 ?0 S
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
4 j0 ^; M9 x( m1 b<p>zookeeper 是如何保证事务的顺序一致性的?</p>
  O& `4 C: N2 }0 b# P3 O5 ?<ul>8 }( A0 }1 K  a! H0 `- |2 U3 m
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>1 ?" S7 ^: q) Z4 _/ H/ V# g
! ~. R5 [7 u9 l5 N5 C6 J& z
3 R3 U1 u' T7 ~8 R0 s) |& S
</ul>  Q4 z7 [4 ~- s6 f+ f" x' ^
<hr>
* X* w8 l& @7 n) _5 o2 o# g+ ?' f<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>! W- R  x& C- G' A0 H5 F( |: I
<ul>+ s7 A2 y* W- B+ g0 L
<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
- c$ ]& G% I  i6 V. E* f4 X9 k5 t9 A( f, r

* D& T1 E# ~& f, Z- i</ul>
) _* I: m! I% x; Q& w! U<hr>
: d, c" }4 z( j<p>说几个 zookeeper 常用的命令:</p>
' Q$ A+ P* y# c- j( ~+ R<ul>) k: ?4 u8 D) k; f
<li>ls path:查看当前 znode 的子节点</li>  U6 ?4 w/ b) ?3 ^, [# E
<li>get path:获取节点的值</li>& t3 X: W' ~: ~2 Y! h7 |
<li>set:设置节点的值</li>
4 M2 i1 y. {9 ^( p; v) d<li> create,delete:创建/删除节点</li>
0 E% z" K' p! Z& w+ @! r3 o& x$ H; ^3 o: {0 R: D* {
% w9 |# n4 K8 K* o0 Z, W3 V# w6 I, U
</ul>
0 l- [) H4 o  L" u& t<hr>
( A5 H8 ?1 ?, w9 T5 c0 _<p>会话Session:</p>
$ a% W; p: B% K$ ?<ul>
; b- p5 R. w% t" d<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>+ j/ l% `$ P: y  p5 I  Y$ ]# u0 V
8 W& A6 a% p8 T; Y% ?) U

7 o6 Z+ X$ b& P1 x( [9 B1 f4 _: u8 \</ul>: b) e9 X% A( a, s0 q
<p>&nbsp;</p>( c7 f0 l! [3 |9 ~+ \
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>  j9 r% y9 y5 c/ x  ^0 q! ~  P% B
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2025-7-2 08:04 , Processed in 0.064380 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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