飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

6478

主题

6566

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
21758
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
! l2 F2 d9 T) ]' U' A0 _8 [1 D
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>
! |5 j/ ?& u& Y, G' d! i<p>&nbsp;ZooKeeper 是什么?</p>  _+ \+ H- {0 _% t$ Y3 u
<ul>" p  @# p1 m1 q9 o6 j
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>+ X; b! t( T2 v2 I8 p# s4 i& H' 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>大家都关心的数据,一旦这些数据的状态发生变化,Zookeeper 就 将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。</li>
8 Q! y% h. E0 ^- H* Z9 j<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>- S* H# t+ O  G, }3 }0 O
</ul># w- f9 Z. ]) g
<p>Zookeeper的作用(应用场景)?</p>
1 z- K6 b( t/ s5 \" y: X4 g0 ?+ H<ul>
; }1 z; C4 `& @4 W& G<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>( T1 [6 g: g1 b' |) y6 G3 M
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>; J, H1 v6 \* T
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>4 n% g! q0 _" q: I0 n
<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调用。
! Y. A$ \4 N3 m, W9 _1 ]9 F<ul>9 \: }' T& K9 [. U" T/ Z/ `
<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
3 ^6 ]$ P' L  D; F, h( _2 A<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>% w( e& \' X* U6 `
<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>
, c( N/ k( G5 G</ul>* o7 }% `; c- `, H: g0 [% G
</li>
3 f- L: L! y8 c5 Z" R<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>% P7 q: p1 i: o" M; I( M
</ul>
; m& U* D: g! ]8 f% {<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>  y; y$ T% a+ P& g* d6 j0 F) N; T
<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
6 U2 [; A3 W8 C2 _<hr>
' R8 {; i, m7 K7 ?6 H8 M0 s# ]7 g<p>&nbsp;文件系统:</p>; t0 K! o0 H2 s. ~/ p3 \6 V0 N
<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>) \9 o7 }4 s+ ?5 m; b  e% H
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>3 I& J0 X- M6 J* N1 g
<p>&nbsp;Znode节点主要有4中类型:</p>
) s4 x6 t5 c! g" E9 G) P' U' D, o<ul>. n% f- u9 k) j; v* [1 e
<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>6 F& N" m( i- c% G# g3 D
<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>/ J: J0 O" K) x  j$ Y
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>6 a3 C* m4 n3 I4 |
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>2 w; e- z. d: S+ \( U
</ul>
; C$ j* z! f8 J<hr>
* ~) g7 a& F$ a, C7 z  D<p>&nbsp;通知机制 (监听机制)</p>
4 L& L6 s( \/ w3 [1 P6 s, {<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>; U. B* S3 O$ o
<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>
3 ]; O: r* `& V* h<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
( N3 T, W& O8 Q& ]1 j& Z  n4 c8 ?<ol>
) y" F3 y7 D" x* p$ `<li>客户端向服务端注册Wather监听</li>
" F+ ~8 s1 x2 ?3 q4 D<li>保存Wather对象到客户端本地的WatherManager中</li>
7 \8 ]' K0 v9 Z5 o; @* P9 I<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>( t. p8 R  Y$ F: H; L
</ol>/ Q, w% f$ M( J4 R4 J6 a- X
<p>&nbsp;主要监听2方面内容:</p>  ?8 I' p' }. X5 V9 s
<ul class="list-paddingleft-2">
, T8 J/ B7 o: A) U% w2 _<li>, W3 Q" n9 F) X% w2 Y2 k
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
- @" J& _6 I: D; m( z# j</li>
5 e! r5 K" B- }9 z<li>1 z0 T- d2 G, I* U! y
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
+ t- s1 r/ `- B2 J  Q  _5 a% w/ ~: U</li>
8 Z5 I9 x- T1 R</ul>
* K5 y  X5 x  D! i<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>* f1 ?5 R) J0 c$ s/ ]  E
<ul>% F( ?# q/ u7 `6 ^
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
$ }% b# |* S) l: R; F" {$ @/ j<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>' o- H( ?- B& Y& {
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>, ]0 v8 w- r6 Z1 y% y6 t7 @
</ul>) z; H, x, d' Z7 t5 g' z
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>  n9 ~# V: ?+ K9 H2 }$ _, y
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>
# A: U9 x9 I3 H<ul>, _' v9 B- E9 l! k# Q# C+ h/ V5 ]
<li>Leader:负责写数据。(写数据都有事务)</li>9 ^% ^  R+ J# n. _* m
<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>
  [; h* K/ n: m4 H<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>
5 v9 b$ i% g+ R- y! I5 X
9 X9 N  p. ?" K- E</ul>
' @2 T0 I- Y5 N- q0 G; F( u6 p& G/ v$ K<hr>
, d# y) K) q/ g& w6 N<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
. L: p4 ^2 F* C% u2 s  {  l<ul>
1 P; _& I8 l# M3 x  G<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
  s; g4 L9 p6 k' z5 P<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>6 [/ p  J$ u  D) Y! Y
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
6 o, e$ Q" v" y4 n% T<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>& ^0 Z0 h1 A( E+ f/ f# W- ?5 j

8 Z2 H. Q! U. {  H3 ^* E</ul>  X+ S) R4 K# J7 X# C6 _
<hr>
8 k; B0 u# ^9 b  W<p>其他概念:</p>" c! O5 w  n. G* q  S  {
<ul>% S# [2 D# x6 ]3 S2 _  G% @! }  _
<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:) F- d4 K/ Z; [- V  a
<ul>
* t5 Y- r; h$ O! Q<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
  O- s2 n$ [+ e$ n- f$ x<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
* J& {: `' V; N7 [' ?: {  l3 w. q& k) h" u+ F
$ x& A, l" N1 s; K$ a  X
</ul>. B: j$ h3 c( f9 [+ U/ C  ]

% q3 P3 R9 B& }% O& S
( ~! k: q+ k7 m3 g! a! ^</li>5 i( A( r/ V4 _: L8 b5 _
: S/ L) d+ W( F

& P* x$ h& y# ^, c% {6 {  @" I</ul># H( `2 M. j- k0 X9 T" b( j
<hr>! b5 |% N) I) d8 s9 X$ x
<p>写数据原理:</p>, i6 H& X3 r2 J7 X7 y$ _! V  j
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
- i: e7 X3 c# [4 i" X2 `. H<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>+ [" g3 \5 u4 ?
<ul>
( q6 b" P- {! T# f6 J8 w<li>写给leader,leader再通知其他节点 </li>
/ S! T% ^- n9 h* W<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>2 o  Y, h9 W3 m- M0 g3 Q+ K" i
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>
) n- O; y8 R, R9 i; q  R) M+ W  E0 b
8 ?; A& n' v% }% p& x
5 P6 A8 g# U1 K' E7 R( H+ Z</ul>
0 t# N" U) r' v) G" e' J/ g! d# n1 _. q<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>0 ^; _* v9 L3 y" B! G6 }" q
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
$ s8 g7 i( }) H<hr>4 T, R. W9 u% b
<p>&nbsp;ZK怎么保证数据一致性?</p>6 F) L# g: A8 @2 a$ h
<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>
1 h. `! o5 N, Z  D. w$ R* }<p>&nbsp;一般我们正常是消息广播:</p>
& I. {- F$ O" I( K<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
& V# x0 j' p' r  U<ul>$ [8 z( `4 o% |" t$ E2 X% |
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
% h" a0 U* p& M0 s+ S. C<ul>0 g, j) A- U2 Q$ ?# [* q" V
<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
9 R9 X8 ?% b' R8 d' {: q<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>
$ z2 _  t4 R0 Y" ^$ a
1 B/ z5 T6 U1 T6 D" ^
! @6 h1 [6 L5 Q" e' G& D6 E: ~
4 @; I1 A$ j+ U. @. P6 a4 G
' V# H/ F* V8 \+ S, G2 e
0 b9 N' s9 O3 ?/ j) o, B* u3 `' }1 t& _! Z
</ul>* I" b: S* j6 C  \6 I' C# _

9 _1 w: y. I$ R% y8 {' p" l0 e: g0 H+ e8 I" \8 n* g  r5 m4 Y

, l; o2 S/ r3 Q% w8 I/ p2 h: P3 V: u# |9 ]" s

6 {: q% l9 V! W" [5 t
. m) R$ k5 O% b* g+ t</li>
7 i6 h" \, M0 W! C0 @! k<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的32 ~8 y- R, n  J" P; Q/ V0 Y
<ul>
8 x2 g9 V3 {3 Q* x<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>/ o& _8 g& N1 G0 Z
$ @' _5 L0 o, v& Q; m' P' s7 Z% D) `

0 C% G5 _: Q/ ?1 y( P4 d- w" \: g* J( U- i

& Z# H% ~3 x7 f6 S0 i- v1 E# b
" I$ n7 k/ W* n' A/ ~
3 ~1 z( d. T* ]! R' D</ul>
' g& G$ _, k  h4 y. F2 Q" X5 i1 F( m

. p$ J, `, J; Z. l' ?+ y( R0 s! T! x# r5 Y( J4 W

/ k! p! \( y% ~: u0 y. X' f% M
0 N3 v4 `0 B! q( Y; y7 N3 d5 j! W
</li>
* Z; r. I) z& b# Z) ~( H: P8 P4 Z& I! z* a& q( B

, m0 X8 K8 I0 B. E+ q* ]$ l7 c, R& O
1 C8 D2 ~  {- t. T0 H* d1 \' G' ~* H
. r9 U* q* \: K

( E; o) h; L0 P. T! x% I, o</ul>6 ~+ i+ {0 B. }/ [& c: d0 d* `5 O  Y
<hr>
# y2 u: f3 j- U) S5 H* ?<p>Leader宕机了如何进行选举?</p>
% K. [6 A; ?' p& g2 B2 F<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>- f0 R5 D+ f6 k. }/ E, \
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>2 y/ B7 p2 n9 S7 C
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
! X: h* ?- J6 z<hr>' K0 J6 J& R6 H: V' g* T
<p>选举后数据如何同步?</p>+ v% N7 G2 u  U- ~, y
<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>* n' l/ A( d# j  W6 ?* a
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
, ^  B, J' j7 l3 H; v" X; J' H4 M<ul>' \0 Y5 ^2 L: R$ R( t/ g/ y% [
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>* U( `6 C3 F& {
<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>/ t+ @* L  a6 p2 g
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>
) F$ J8 V8 l5 y" P  W. |9 D" D6 i" m% B% b- P8 I% R1 j
& |5 p! V; L0 C
. L* b' j/ c% b! _) M- _9 ~
5 t8 ^1 D: x4 ~) b
2 c; V% S$ s7 {$ @

" P" Z' m0 r' ^6 X3 ~8 X/ m, e( ?</ul>8 u1 Z! s* ]8 e8 t5 `, U
<p>同步策略:</p>
: P! G- w' y% H1 A  E, w<ul>
, x: I. B/ d2 ?% x2 d1 L; Z5 v<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>( [" J% ~/ [  d4 w( `
<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>6 b# {" k/ N6 u& l$ P
<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 a) A, L, K4 ^) Q+ 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">接着等待集群中过半的Learner响应了ACK之后,就发送一个UPTODATE命令,Learner返回ACK,同步流程结束</li>/ H/ Z, Y1 ?% k2 M0 `
/ y. ~9 K& ~# E" b
0 i0 ~" }" S% R; j: K, r+ B

) ]' B7 O' S: b. z( X8 K" U! R" y7 O! ]
</ol></li>
8 n" N' ^8 r* R1 [% ~' j<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>( M5 R7 W) i; [* l5 {( t1 l
<ul>
0 A0 U3 j1 U) h! j8 ^9 ]. B<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>
1 |$ g" j6 k* j1 F6 i<li style="text-align: justify">- g, ?' D; f' X; d% x" O1 ]2 g
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>* ]; k8 D  m+ h$ h/ {

, }; a0 P+ v8 a: o% q1 r% I& J* c& J9 p0 ?+ Y

# p. u' i5 Y8 ~: v2 e0 T0 h+ Q1 l( q1 n) P
</li>1 Q5 x, Q1 D: E, ?. t' N

; e' t4 a7 B; B$ U' s& {) R" k
6 v8 [. M9 M$ p; v* j/ b+ c/ e* J% J# H  R6 H1 h
2 j* S( ?& U! ]& K  X+ e
</ul>7 V$ C5 ?) I  m$ f- G8 N# T. R
* Y% Z# j1 H* _+ Z

5 y/ H- i7 _2 N- \
5 ?1 L1 W: X1 F  w
- }# O9 G6 }7 l. o( \</li>
, v9 y1 G- `4 H9 H<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
8 I9 o) a: _1 Z5 {; e9 N<ul>/ T" o9 A) k" ~+ b) I) C4 w) C
<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
! A" D# \) Q( o6 c5 J+ }1 N% X<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>
4 x+ z7 o9 s1 v, E5 L, V, |1 Q0 b$ m<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>1 F+ D" V( z3 \, {) \
5 `& ~% q( e- H8 X. u9 _

+ }, b* n/ C; k% W
! K7 _' G& o: H3 k7 u0 }- f4 M) H- N! L, w) W$ n
</ul>
0 }7 t& P: U; |" A# L0 e" J  P. S: y8 l5 V

, e% g* b$ l5 a/ o' B9 L! y7 l$ w; c
: t% G* Y6 w- i% Y
</li>
$ H$ ]' m- Z  a/ e+ y+ t! z<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):# R4 v7 p: p% H  E+ U9 |
<ul>
3 q8 R: K5 _* ^<li>) |* P) [1 y2 \+ K& Y5 O
<p data-tool="mdnice编辑器">适用于两个场景:</p>- f8 k) {, C* {9 I( m3 X0 A8 M; z
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">- J3 i$ i1 F# ?* l! y$ s
<li>PeerLastZxid小于minCommittedLog</li>
4 U& q0 c; T4 x3 J2 k$ ?<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>3 k7 N2 M# k' Y& J
  N# I( a8 ^; q" e
! x' F' [1 f# a9 Y
; @- O+ X: \$ u  V. p

$ L. t7 o4 G, U1 f2 U+ @* M$ o5 U</ol></li>
6 C) R% W: w1 Q. {. b3 ]<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>! V- V- {, d) _

3 ~/ e6 [7 o, r9 r. a8 W1 x8 N# d
4 s$ ]; W. N' L: h+ k! b4 f4 r
4 @# X& x$ q+ E  X) l+ ?$ h/ w
" g0 V6 C& K8 k' |# t- m) `</ul>+ j9 ~# V, t: I2 n. b5 @( r( Q$ s) ~' m
# P% u7 y# t0 [/ n- }# P/ f
& K  o: |* z/ t+ i" g, W. [
" F3 P* F+ C. J8 ]* v  ?& |

& T* o2 {4 @7 \1 C</li>, ^' f" J, Z% S  ^) e+ \

; m* V$ m- I, b. ?8 N7 E) b: n7 z4 r4 b8 G3 Y
4 H/ U: Z: R9 ^( j

4 k1 p0 P  u( Q4 A8 J</ul>& x+ J) r9 V6 R$ V: k1 Y
<hr>! V5 y+ ?% H  ?
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p># B* F! J" c* ?
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>+ K8 r6 C: V( y
<ul>+ Y# A) r. Q$ \+ d, j' }
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
8 i- p* c% p0 A4 z. T6 `' M<ul>
( B1 W! ~/ u# [3 \/ D) E5 b5 f( ~<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
5 d# m" W! l- @: e( `9 C<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
) e( I- [1 y4 N% ^5 O8 M% D6 D8 B* H) X3 T

8 w1 I) b. z  V; f</ul>& y# X* x; H5 L% ~; G6 G0 }! L3 d

9 A8 Q( M- |" }& w1 i
" \, O3 E4 [7 C, _; I5 G</li>4 q; A  \$ a% K
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
8 C; ?- n5 T0 M2 n4 s, s- u* d: l9 ~<ul>
- O9 @$ Q1 f1 v( y# b# E; s<li data-tool="mdnice编辑器">: m) n9 e" }7 H9 r
<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
9 w# N% K: ^: A6 h
( }2 J/ L* m: J
+ J7 |9 ]6 L6 Z: @$ w  c! S% j9 N</li>0 p! g* ?( `/ P% \# ]# S
<li data-tool="mdnice编辑器">( R$ {! g3 z3 H  l, j1 o7 W4 _9 [
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>: B1 X% a5 r+ ^2 L7 o% t6 v
1 X/ m9 X2 T: ^3 M; _" z/ d
1 E6 A1 z: N. c5 R( d9 Y
</li>  o5 A+ [) F" l' c$ c, H
1 N7 d) L3 r& j. I* ?
' f( X# E9 ^: O( `8 `+ e
</ul>! k0 p# I( K( Q6 K, B
5 c5 Q/ P. v6 f2 V% Q/ K) Q
0 \7 [9 P3 j# z! Y
</li>
2 H6 v! P+ H3 Q, D8 [$ f5 D<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>; _5 x. s3 y. s- K0 O' p
<ul>0 z3 v) H! K% c  U: @/ ?( C0 U" d$ q
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>4 [, X5 a) n! G1 E0 N( \4 u
9 D- y0 g2 D' H4 ?

* u2 M6 l2 s7 J* K! ]3 @</ul>
  o  f( T" T1 `( L6 i# Y. c8 T8 R$ Z0 I0 O8 d1 }

: T7 }) b* O0 p4 J) C% k</li>
: F- x% }" r1 e. b$ T
$ ?8 S7 s" p+ r, W6 ^* _9 x1 ?! K( H- A8 v( J8 S: q
</ul>
* r* n' h  F$ H0 W( E# H$ }! k  A/ G<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>0 u2 j9 d9 v; [' a  A' {
<p>zookeeper 是如何保证事务的顺序一致性的?</p>  B2 P/ I! G( B$ _2 \* w4 o4 k
<ul>1 t+ c. F3 I& U. w
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
/ h0 E8 k1 L! I4 _% p0 q8 n# v: S* U8 u/ Y% l. g
; j$ ?8 g2 K+ H, y' t- Q1 s* |; D
</ul>
6 L1 U9 h# h8 f" z# S$ i) X/ h<hr>
3 i* B, T% }. R<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
, f: a6 i/ h8 r  G/ E" `; N4 p<ul>$ Z5 y$ z9 d( ^8 R# j  H' i
<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
3 @  ~; d9 N7 r
' a' R' u! p' l
2 j$ m4 r. ^. S</ul>
; p4 H+ X' ?+ q) E6 _<hr>
; H, g6 H( Q8 q3 ~1 t<p>说几个 zookeeper 常用的命令:</p>. K$ ]3 e1 x  J, X" |
<ul>
/ z; P% I6 l. m- K( N- @# L<li>ls path:查看当前 znode 的子节点</li>
( x& d# }: `& B<li>get path:获取节点的值</li>' _- z9 l) k  C8 |# K. f
<li>set:设置节点的值</li>
8 m! q% G! W9 s3 [2 Q<li> create,delete:创建/删除节点</li>
2 J( k2 X+ D( @0 v, h3 k7 e) X
4 Y6 Y2 L8 r7 A& \2 d& X( P/ L3 x; o1 [+ f) ^
</ul>" c( p1 N8 z( c6 t# B- J2 Z
<hr>
" m& T& j! f  X/ W3 k9 S! y9 Y<p>会话Session:</p>
% G. k' ^+ J' [; f<ul>
8 }7 A. }3 @& {/ l# I  Y<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
" u6 t: H" B) _; U) I" e& v8 W8 w9 q  ]# c4 ]

/ g; K1 ?5 l, X* q</ul>) C3 g; b7 ?) w8 [. ?& ?# c
<p>&nbsp;</p>: R# L! T# s% ~3 d" ?* k5 o
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>0 k- Y/ i* }, C3 L; Y* p8 e
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2025-5-1 07:16 , Processed in 0.140450 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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