飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

6855

主题

6943

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
22895
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
2 t, U0 ?0 N. Q" u0 t- |$ w( }
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>
, n3 V% u# y  h- q. B<p>&nbsp;ZooKeeper 是什么?</p>, N) l9 k1 D, ?2 X% U/ @6 v
<ul>" p- C6 x  J, @% K2 `
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>
& e/ I, G5 j$ D+ w<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>- f" o) A0 b6 Z% R. ]1 @$ D% _4 }
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>4 l7 T* O8 Z6 U! R
</ul>1 m) l3 m' {! A( T9 |
<p>Zookeeper的作用(应用场景)?</p>
- Q; u; t8 e3 ]' D% M<ul>1 D) k7 F( l2 A2 w
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
+ }8 I' a" \) W& K! q<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>7 d; T) Q; O" \5 o( l* z8 D+ v7 S
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>2 ?3 y# l+ V6 }8 i2 t8 [9 n1 L
<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调用。
7 g3 Z- R6 a: H& F" B<ul>
( l! G+ n# M% L<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
( Y% p9 |' M' g7 o- V4 ?<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
. H& b) H- H! K6 {<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>
8 H" g, N* M7 M4 l0 Y</ul>4 G3 j9 R4 n* B& u6 ?( V
</li>
# J, k4 ]  d% z" M. J6 C<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>( M# E; c" {$ Z3 o8 L
</ul>% P* ]' u  e6 n' D; U
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>, ^( f, i+ S! d* e$ ]# W) j/ A( F$ J
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
& c0 k. x7 b6 S# \8 r<hr>
( S. p1 {* q1 {+ d3 L$ C<p>&nbsp;文件系统:</p>/ E5 d) D* ~" v" x; p
<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>
) Z. ?' X% [, @<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
* X5 `. i) N9 l# }<p>&nbsp;Znode节点主要有4中类型:</p>3 L, p; W5 A* Y2 r/ O( C1 b* U' t
<ul>
  p$ ~0 y5 K( V$ k: ^' K6 G<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
7 e9 ^& g& N8 j8 J) W<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>  q2 M9 f) h5 M& J! ^2 Q1 M! Z: W
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
/ s/ q) T: A) C& n<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
2 B' g/ `: Y0 K$ w, c6 M) o2 n</ul>
" N4 Z7 ]  d. ]; d0 i' |" V<hr>: P) Z) Z3 u6 X$ X# C
<p>&nbsp;通知机制 (监听机制)</p>
+ M# u% L; h$ j% V<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
6 ]- o; p- M, ?4 k" u9 Z<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>
$ r7 }0 K) `& E/ s& p) \<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
% z. F6 l! b7 @9 u7 ~<ol>
& {1 |' ~, l3 l4 _' |' H& J<li>客户端向服务端注册Wather监听</li>, b! L* ~0 M9 P: ^! Q
<li>保存Wather对象到客户端本地的WatherManager中</li>0 d$ y  k5 I5 K. g% k
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>  z; ^/ [( T. @7 O
</ol>$ `* |* z9 ~. [( T3 ]
<p>&nbsp;主要监听2方面内容:</p>8 R% @- |  g/ w; ]5 s5 E
<ul class="list-paddingleft-2">. {: z& F( C! V8 b# h+ O
<li>! P, `0 I4 d2 J  f* r
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>8 J0 ~6 b0 V8 P" i
</li>3 k) P3 S1 X' ~" e2 t
<li>; p  q1 R) N  t1 O" p' X$ R8 L
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
( d5 B* s! Z6 Z/ T</li>5 x% x" d2 ]  l' S
</ul>
( ^9 @( u; R7 N1 w<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>3 ?3 `: W; q" Y6 Z% m
<ul>, b+ F* z/ [+ S. b
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>, h9 X; s8 Z4 Y) U/ F
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>5 y7 ?' Y& m1 G1 \! }3 r4 @
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>( a6 o  ]0 K" f
</ul>
( i  {0 z4 S, t& F( D# [<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>) r# T/ [1 X0 e: F4 n
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>
: |7 c' {& M* X: O: |# g<ul>
& u0 t& ^8 Y2 o6 ]. c& s<li>Leader:负责写数据。(写数据都有事务)</li>; N" r5 i0 y+ O+ w8 Q9 }
<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>
5 q! f2 l# U3 L; g0 P- X! V<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>) W0 R2 K" d+ o/ i- _
( T- D) W4 p7 r+ S9 j
</ul>/ l8 q" Z+ J8 V
<hr>% o( X/ [0 Y5 }5 o# P7 v! c
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
+ D2 r( `( g; H, u, p<ul>, i6 B9 p2 n2 c: v8 |) L" K
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
6 y) {' c7 d7 t, G. l/ I, }<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>" b$ Z0 O' w$ W0 k
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>1 }3 Z& ?6 C, }$ b" g( y! z9 _
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>1 G5 |4 H* R5 u! O7 V4 T

$ `6 |: e# I8 X6 H0 h/ j</ul>6 ]& w+ x' I% D9 O
<hr>
3 b% |0 r* e9 F% I$ \% F& J9 D<p>其他概念:</p>4 X$ O$ \8 q* x5 v2 }- L% {
<ul>
9 @1 J5 O$ @9 p) _<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
6 `4 Q& w' l( W$ @2 Z<ul>2 I: Q- V5 l9 j' d) Z6 {
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>$ {* L7 Y3 _+ P
<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
' e) I0 A1 _( e, ]9 a- ~
  y1 |7 R  a+ Z. z1 ^! I9 x- P7 m
4 r& Z) h) ^% Y. D% a2 a8 W</ul>! g# D9 b/ N% Y+ q, L

( N5 M$ j3 C5 u5 e/ P( S& e( ]6 r
  J! ]. q7 x# h" Q+ K</li>
3 e+ s$ ]5 D9 j# p3 i- c  a- B/ R( g6 I4 i$ o

/ z1 @& K1 i- K4 @7 @* \$ \</ul>
* f+ P  S0 X3 M<hr>
, o# `2 j! X. p6 j8 o7 A<p>写数据原理:</p>. ~4 F2 M6 x! v+ K8 {! o- M
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
  j. l% D; F& o# ~<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
; {$ \5 Y2 [  B6 R2 Z  {<ul>
2 q5 }7 s; L& e* d, l<li>写给leader,leader再通知其他节点 </li>
) r# Y0 L0 j8 C<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>/ R) Z4 S! U/ @
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>! M4 P! E2 ~. {8 c$ E' f* F6 w
# p' b9 O- ^! J& E2 }8 f6 u

' I+ Y4 q3 X  c" g2 @7 @  A9 [</ul>
7 q7 n0 K( K& m. ?9 f4 O<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
2 }+ H) G" L  P' R2 _- W2 ~<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>2 Z  d  e' E$ I
<hr>5 S0 M) L+ Q# `
<p>&nbsp;ZK怎么保证数据一致性?</p>
( V- l9 k/ |4 {0 ^<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>% v' T- }# O6 q3 w3 v' H
<p>&nbsp;一般我们正常是消息广播:</p>2 H, D3 w" u; Q# A) t3 Q. l. J6 e
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>7 y' m! M7 A5 E4 p, P8 I' Z
<ul>% e2 A1 z" i9 J$ X# q
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,21 S! \5 L  L3 D/ \" f. M" H
<ul>' F5 ~8 u( H+ H# _" R) D7 L5 u6 {, y
<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>+ m4 t! b% M4 t7 c
<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>
) K2 a7 Z9 ]6 G7 u# S, Z! x7 O( H2 ]9 {1 D- I

. l9 F' G% K1 d8 s, J2 h
% R6 y/ S) n! o% x7 j: p; }
$ H& w) |3 P$ x
" F$ Z$ B8 E; j; j. D, @+ z0 o) Y$ K' l: v4 l6 x% u
</ul>
% V  O& H& F2 `* Q8 w/ I# d& k$ e2 P  ?" w  m& x  Q
. ^2 Q+ S4 ~! J

  S. C+ M2 K- a# {+ G" ~" i& s6 N$ G! p" ~
6 ~( D! y+ F: b, z+ j) u

9 o" G( t6 G! ~. G: U4 V</li>
% I; Y) F- O4 O5 H1 U2 N<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的37 O" w3 B3 d9 a8 e7 J& T
<ul>
7 s% y  B5 X! T8 ^& ]* M<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>. A7 b3 L) W  y7 S9 }, F1 b

% ~! W1 _4 |( s- s' M- d( h) A; q/ Q1 h; D, k& p

0 M, A# E: u. P" F8 \
1 N2 h; E$ F$ ^" O; T
( s/ m' ^1 ]- d, u
, {7 d( e+ F- @0 l- B$ ]</ul>
& z5 \4 Z/ Z! L6 b5 s# E" w# r, e& C* R* ?4 M# Q* W6 J
$ z$ d% `0 D+ b! O

/ K; V- B0 r5 P9 E; E# N9 ^  a, M7 {$ s8 K
- ]5 K+ R2 l& O! ^: ]
/ r, O. w: ?* w% S% _4 X
</li>3 F: N) ?5 C: K: q7 B

$ d; K; I( l: M6 j' k4 l/ ?4 n) y6 G- i. ^
- T4 e) `3 q% n) |% V5 \

! C1 z- a& n- Y$ D4 c% N" H- _! O/ M# p4 ^; N0 m

0 F- j6 U) r$ X/ c  J8 w% W</ul>
' q7 r9 r5 i( y0 _) ~* L5 o+ u4 C<hr>
7 L/ Q) Q, n/ i<p>Leader宕机了如何进行选举?</p>6 d3 Y3 i6 ~9 c0 n' {! L( X# S( E
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
/ d% P: d0 d) T8 A& [/ T<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>) Z3 s. B0 `* g4 X
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
- }6 J; o- v0 }* ~* ?! x7 M7 _3 X<hr>& O! A, \- g7 n3 Q; V& W
<p>选举后数据如何同步?</p>
) f& N9 ^, X8 J% Q& w0 R+ M4 A3 Q% V<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>: H: W+ Q6 }: Y! L7 E# U
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>  W3 D4 p' [3 T4 u- A! T
<ul>
! p. j! l! ^0 B$ e0 D<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
: P) H% N. I/ m/ \<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>6 T! B* f% C4 s" p5 N
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>
: L# X9 B( @, |4 ~# q5 ~5 P
/ @- i# U, L$ o* N. j% o
# `& v  M! f* ~6 K+ P3 ~; Q4 M5 F( I7 ?7 K* R
* K; f' Q" E0 e) C
; z- s7 Z: F# n  n5 F: T3 K' n
) H: w* I* T6 A' c, D
</ul>9 L; l+ ~9 ?2 u; V
<p>同步策略:</p>( a0 ^# R* G. ?! l
<ul>
) t0 G- Y6 M0 P: B7 T+ c2 ?0 }0 A<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
/ g2 j9 ^+ B# Z* V0 a  e# 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">首先Leader向Learner发送DIFF指令,代表开始差异化同步,然后把差异数据(从PeerLastZxid到maxCommittedLog之间的数据)提议proposal发送给Learner</li>. t$ f: d9 F6 Y
<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>
" N8 a9 j2 E: h* ^. J% n8 `<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>
# i+ w! X7 f5 |: y; K2 T! y6 W6 G) p5 f) z+ e
7 Q% G  [' c% m0 S$ f( L/ @

# S( B6 O7 Y6 d
* f- Z6 W# L% `4 n' S</ol></li>9 ^* d' Q. F2 p+ ~
<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>
; U  w* _! Q$ `! E2 N2 D$ T/ {8 s/ A<ul>* {# z3 l' _, i3 o8 Y
<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>
$ A6 U* v! o2 v& V% G<li style="text-align: justify">: k0 f7 W2 {* E/ f* Q
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
3 L( X9 \; C! S! i, B+ ~. n+ h# ~+ Z8 f, Z2 n+ F9 l) S

3 C* J' q$ E8 i4 j+ s4 }) d) x: ~
9 o9 i! q7 [( s+ Q% o7 ^2 T* P# A( g
</li>+ C) ]9 ?' i0 M- {( ?( d" P+ _% m

& M. K0 m9 e/ G- G& s2 o3 z
; @/ d. I3 D8 P
# _. W' H( R; K  D+ @; i) P5 D3 i$ \! T9 H* C7 O0 V- t* H+ S
</ul>
# j" O: ~! s: k: ?, S$ ~. r, x, k' ?% Z+ t5 T9 r2 i: }% ~- k: G
. |2 x+ G! N( H' l
7 P+ w1 r' A! I3 a

3 S' u" f# y+ [. }; a' h( k$ j2 H</li>
; C8 C' ?8 `; \: Y" `! w! o<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):3 L+ e4 z" l! Q5 v
<ul>
4 ?% C+ u1 P8 m% k( D. r<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
. i  I0 M# b& f<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>
- r+ W$ i$ S+ i<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
1 \# Z3 Y  g, |: H. |; ]. K* v) ~
+ I; T4 Y% O( X" k: b  |) ?5 v

0 |$ w1 a) V( Q- C3 Q! l  p* s' {: W+ T) C" l6 F
</ul>
& p1 x8 h$ h6 |* @; n( M" g0 j4 h9 O# Q* _

3 H0 b$ c' v$ t* D- w: p- n8 I& P' X. ]. c

0 D& U1 B7 X2 j& t) k</li>/ B$ n; Q' R$ x7 m% Y; Q! v
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):  \$ R2 ^; [- P- [
<ul>
, F- D' F! I4 K7 S4 |/ m<li>3 g4 f  f/ b" I, H
<p data-tool="mdnice编辑器">适用于两个场景:</p>" Y6 |& W% {& _. F+ r# N" ?
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">' D  z; |$ z* z7 n2 d* U8 ~
<li>PeerLastZxid小于minCommittedLog</li>  H4 B7 z0 O# o% e8 }
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>7 r& G2 K, D( g* x. y9 y6 R4 z$ m. x6 h
: ~! }. q0 |* O6 r! u! A" S; v
8 V: K7 A+ e3 W% w2 k+ t
) z, a- k$ }% Z6 l( I' o+ z! f
" P3 P: P# ]' v7 U9 Q0 `
</ol></li>* l# W+ r7 k! F* t1 [  E" S0 P
<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>" q1 J" A4 L$ W# x6 N
% q1 `+ c& k& q; ?
. o, \+ Y; y+ v* a" n( K

4 z! B  T( b6 a- A, N- p
8 ~7 e7 A4 l* E( H2 Y2 j</ul>( M7 \  u2 N$ n& R, Y) X
1 c( L, _/ ~- G: \
$ _$ h0 b; i! N/ U5 f8 ^" \0 U. x

! {' s- P, e9 k1 b% x, |
7 |! s4 a8 _' ~. j+ ^( }</li>7 D2 l7 x6 z1 }3 `( S' L

) [% D+ M; l1 ?# p; R5 }" q) U
: D5 J, `$ K; n& a, m2 C) Z2 Y7 O
3 v) A( k4 _: k+ l- h: I( i2 N4 N, H. D" _1 B6 M
</ul>) g) ^  H4 n3 `
<hr>' x& l. k2 s5 F. u# ~6 _! Y
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>! R$ d, Y. X+ I$ h9 b4 C7 y% V" i
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
; ^0 o- B; V. Z9 \/ o1 l<ul>; g' r* ^, M, {* F2 P' C# P
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
# t, I! e  M/ f2 y" o<ul>+ @' z& [, T3 h, }
<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>/ U. C8 S. `& ~
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>% d7 u: V3 l( P6 G

: b) H# q- y0 N% F( w% v- u7 i0 ~$ c  F. Y( ]
</ul>
$ e0 Z+ R$ u& c2 M; S2 U3 n
7 t# J4 r. u! s  p3 I# T
7 L+ X0 Z8 `$ L2 L8 ~' X</li>
! s, H8 C1 t4 ?0 c# c2 g9 o7 W- e<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>, F5 c* o) b5 C: s
<ul>$ \# g8 z( `+ v! d7 o# H
<li data-tool="mdnice编辑器">
/ t9 O, F% w$ X! B  E<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
( Z9 w2 r! E! g7 P
. `+ ~& X( s9 ^% X8 ~* F* m6 X) t1 A' r. ~( r' A# ^# I! g
</li>
% n  l  E9 b  r( _" A: H! x<li data-tool="mdnice编辑器">6 i; K( m' q* o3 v. ?, u) m
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>3 R# ^  ?$ s$ m2 N( ~' r

5 R% D" }5 Y* ?$ x$ j4 @% e8 K) O5 U6 K, e' z5 ?0 `
</li>
3 k& ~% k6 c& G& G2 Q3 i
3 Z" ~" B( |6 r( V% `
+ j. T' l/ E: Z& h( M6 a) T0 p</ul>
6 z& W) p. B* P1 V7 F  Q$ x3 h1 U1 D' N7 x1 P  O! J6 I2 C

" `% j3 @( t7 Y+ t$ V# G& o! ^</li>  Z/ E5 l& l. i9 O9 {  d# z. G
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
  H( [2 G5 F) @/ X' c5 ^<ul>6 ~& m3 s2 X9 l
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>" N  C5 s) _3 e+ }$ r
; L8 M) T  M3 c/ F* i
9 a) [. t' M9 x- d
</ul>4 U3 [6 i1 j! w2 u3 y
( B* A! ?' t) \8 j; p
8 m1 d; Z4 [0 U8 D. z9 c3 ?5 L
</li>6 T+ ?  x: l, u# `5 Z

, a- E* A/ p3 v3 K9 ~
) K) k+ G( c! P  q/ h</ul>
/ R- H; s( t- `# {' ^<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>1 x& B- N6 g  E" p& a# Y0 D. w- R
<p>zookeeper 是如何保证事务的顺序一致性的?</p>
; N. }# ~( E. J, d<ul>
' M3 f- o5 N& c% W& M% Q<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>1 E3 T0 c: B0 K7 V9 r4 i
6 C2 n* K/ W! W" |& F6 i
/ c. L% Q6 |6 p5 Z  A3 v" r
</ul>
$ x& k/ R- {6 [4 l8 n& m, p. S<hr>: c! w  x) F8 {% M' Z9 |: T$ k
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
3 e+ g; e2 s" {/ b<ul>
, Y, Z* C7 Z" D. j<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
! o+ C9 M' l0 h+ |! s2 f! ?& _2 f9 |# S, a& |1 Q

, p% E3 J# t* u9 m( ~; {</ul>
* n% V$ L7 g* @1 y5 P+ n<hr>; Y. Z- Q2 w# d% g, p
<p>说几个 zookeeper 常用的命令:</p>* `3 _. r. o) Q# L4 N7 J
<ul>( n0 s. e# P$ `& I3 Q$ y
<li>ls path:查看当前 znode 的子节点</li>  S6 M$ F" p# S1 P6 M9 X
<li>get path:获取节点的值</li>3 T/ P+ f2 I* `0 _, V- x7 {
<li>set:设置节点的值</li>
  n" P+ N  h- a/ c. s  v<li> create,delete:创建/删除节点</li>
) G# S# L  m" {, E1 K* u; l
% K7 x* s- T7 \9 k* m# D; p! l3 f( a$ L- G* A, t+ e/ j" l2 z
</ul>
* [0 y; C$ S6 u7 t; R/ q<hr>
' O2 _0 y3 z7 A- _" Z<p>会话Session:</p>0 s- @  N2 ~( _+ @# y: Z
<ul>+ T/ {% @4 L  m
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
% r4 g. t( [4 S$ k) Y5 c9 b6 a" i" }) p7 i9 [' p. B/ \! K
9 _. G" s, t9 x! \+ v. I
</ul>1 |8 c8 x9 m' H7 {3 g; E0 }2 W
<p>&nbsp;</p>
, _9 _! k0 B" Y* \5 @( r# r8 A<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
, x( ~/ Y3 B$ o+ b1 y( @, K2 I
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2025-7-2 07:55 , Processed in 0.065559 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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