飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

9018

主题

9106

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

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

/ v; s9 a& u5 P4 J  _+ h<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>! z; ~$ e, z; W/ D/ g) G
<p>&nbsp;ZooKeeper 是什么?</p>: A+ x! M" C9 h4 X
<ul>( Q6 S; T" ~1 S, i" b- l
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>& H0 E2 G! e: u
<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>
2 D! Z+ x/ l0 P7 R/ ]- z# B( x2 o<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>( Y/ }5 N: F% w- R0 C7 Y" t" b
</ul>0 b7 v& l: H( D- L) i
<p>Zookeeper的作用(应用场景)?</p>4 D9 G: m. E! G. v3 `
<ul>0 u& k- |8 Q' H+ m3 w
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
( H  @' ~+ F' i, T2 Q3 B7 p8 w<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>1 q/ R$ B2 J' G2 r* ^8 e$ L
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>, R1 u- X7 r7 |
<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调用。; \& p2 y! u* C. I
<ul>
! ]* B# W1 ]2 P: V5 F+ R9 M! q<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>7 c2 ~2 c! O( Y
<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
$ P4 D9 Y( w  A) `& a  J% p<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>5 D; @! c7 ?( H4 g
</ul>
/ j8 Q. s- o% [2 B</li>
( L2 m' h' s- u* N$ I+ ^" u1 f<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
! e" y* @3 S3 {, _+ k6 J% A</ul>
, @% A# H( ^! n& P" m* C<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>
" r9 l- C( v$ n/ H0 v<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
" J+ j  m7 D  L# K- b<hr>
& Z1 o- z. a9 w; s<p>&nbsp;文件系统:</p>
2 W8 y# v0 P. f' d<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 @" Z) Q8 ?1 ^<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
7 J! p" M2 M$ \: G' |<p>&nbsp;Znode节点主要有4中类型:</p>
# I( Q7 y; m" I" r& i% D<ul>
6 a2 j4 g, M3 ^, @<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>. L1 d% j' g% T- |1 S
<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
+ g! c7 L) a; E+ [* D6 A) [0 K<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
. O: E6 O+ S3 ?3 q<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>! Y, x* b. B$ A" \8 S. K, }" J
</ul>) Y0 A# S& {) \- X
<hr>: J% i6 A9 h: i7 U7 Z( T
<p>&nbsp;通知机制 (监听机制)</p>
, s. j  w* V- t1 _% @<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
) s- [: [% x. J7 g: a. C<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>( S7 @$ @( R7 y0 u2 r
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
# b# a1 O4 J7 I" e) r( \" M<ol>
1 d! h$ i1 ]; `<li>客户端向服务端注册Wather监听</li>! Q$ m' I* S: b! N
<li>保存Wather对象到客户端本地的WatherManager中</li>
2 v, A* x. X2 w6 Q1 W<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>: \& `" |% `# x% J4 A5 J9 P4 y4 o- r
</ol>
% T  I" h3 E/ V2 I- Y<p>&nbsp;主要监听2方面内容:</p>
4 }" B+ N' s. Q7 S( W; y9 q<ul class="list-paddingleft-2">
* Z& s+ Z( a5 v0 h6 w' K* @1 S# K<li>
  i- ?$ z1 Q% J2 o/ e* y7 g<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
6 |; [: c- _; J/ M</li>
( m7 }: m: m' r6 W9 p4 H% O, K<li>2 q% e* N  @4 K/ F0 r
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
- M  h+ W( ?4 ^5 c</li>
! p" N1 |7 @- `8 J: o5 q</ul>4 Y1 J3 Z& }; d2 o
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>' c- D9 g& g) i5 \
<ul>
) P0 S5 ~7 k: ~9 X: E<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
' c" I+ G6 W2 m" L: c/ V<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
2 U) e0 ?& _! @! Z<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>; V" Q9 v+ n/ y1 z4 Z
</ul>; @! g% E% E' i1 t  q& [" E7 u
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>6 W( H1 X5 u4 w* G  q0 U/ {
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>! t" x' P; q9 |
<ul>5 u) p, V4 b2 J
<li>Leader:负责写数据。(写数据都有事务)</li>
" \' T2 R+ ?: i) L- }8 ~7 Q9 _( h<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>
9 W! W) ^4 o5 b' G! l: X) J<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>
- A) |$ R# A, X3 e9 _' O
+ M* W7 D2 I- v2 `1 L</ul>  k* ^! J; |' [0 _
<hr>5 |+ d) s$ `1 j2 y2 H. U& \) N
<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>! i2 c' N$ @& r( J6 i; `
<ul>
: x* J$ `. _: S/ ~, K<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>* G+ P6 X0 U4 W/ z; ]( i8 u
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>' E5 |  x2 O( Q3 i
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>$ N8 d9 ~) ?# K9 K* t1 f3 J; e6 x
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>9 F7 I- W' d8 k8 y' s+ N
6 p$ f/ g: x, f8 |% C  R
</ul>
5 y$ X* k* N, ?9 a<hr>* p3 x. `0 N0 j  v
<p>其他概念:</p>8 \7 f0 e# v" A6 s: J3 i( B
<ul>4 T6 W5 O4 c/ k% Q- T$ e3 l  ?
<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
5 I1 k" l( N! |, [* s<ul>' G/ ~5 M+ c5 B; b" ^5 d
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
8 u- g7 E5 l  j6 D, Y<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
6 S/ i* o! z8 S1 y8 Z% Z' V/ S" a1 i/ `1 j' P, g* m# r2 ]

  Q! X; X# R- d8 H</ul>
, V/ Z' S! e, I8 Z& k- w# e) Y/ U6 B
  K0 z, h( q$ V  f0 }8 W: g
</li>8 q5 K9 g) g2 O( }2 l  u6 \% m, i3 a

: T5 R$ K% P/ X! ~1 A2 P% ]/ J, B0 U$ \* C! W- Y# k
</ul>5 z( _5 A/ E+ L- M) z
<hr>1 l% i- G) q' i% @. P* R
<p>写数据原理:</p>2 k7 m* @- F. ^) R% d2 e, j
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>, b+ {  |% n  K2 r/ `
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>9 q" e5 ]" s8 C5 T+ _5 L
<ul>3 [& ]/ ~- j: w* _
<li>写给leader,leader再通知其他节点 </li>
2 r& s& j( Z" @" t* Y" r0 f<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>& I9 V0 E+ h8 _0 e8 |
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>
- n' {# |! Z" \6 o" k% S1 m" W5 Q
1 N: y  g( I9 P. }8 M" v
</ul>" B* S6 B+ L- y8 l4 u
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
: n$ r. b% U! G<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
; {* F) c7 B2 E- y<hr>
$ ~9 M3 \7 O  `; ?4 {- H; `. Y) e6 `# @<p>&nbsp;ZK怎么保证数据一致性?</p>
2 x8 v; d: y  W<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>
( i6 n3 X& m/ S" ]. d<p>&nbsp;一般我们正常是消息广播:</p>
8 t; I+ e- M4 j/ R3 b<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>  S" b$ b# q! `0 b) V
<ul>
! e* h2 p" ^5 [/ y4 O' d<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2: C0 W, D: t. A
<ul>% H" H* L: V; m, }# A- o) m
<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>! e: B, T8 s( P- E
<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>/ l1 Q% b3 ?4 B4 c  e% @( C6 |

( M* O& W& O2 J: O2 w
4 ?5 b0 W  M4 v
! N) A; _  z! v- d4 V
& x$ y+ y) }3 a+ J
6 H* k5 g1 k6 P; s( c) q0 X) g& j6 z6 _8 t
</ul>
7 J% A7 b' n7 U" A3 C9 n0 c7 _, k/ l& ^) [
- k# O# V* v. y1 {2 g6 P. ^3 Q
+ x2 M* Z' I; V1 }
9 {. r7 M5 ?$ l

- O. z, m# @# @; b9 X; v
. s; w1 [$ l- x$ l</li>- [9 X4 i; K7 s7 [. H1 v0 }
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
) Q2 w% ?9 M  e; O<ul>% B+ i- t  W) l; L
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>
; G; S/ O/ h0 x- p# R8 l* U2 |8 M! ]+ ]  X. v( ~
9 p; h- i$ ~! P- T0 A6 @

# [' F9 `% j; n3 Z+ V  v( y
7 y1 e8 B) g9 u% @
. r- s8 a; S. [- q9 h8 }4 W5 Y% E, C3 z7 y% p
</ul># Y+ f0 ]% k" l6 R9 D. q4 D/ S% M

: z3 t1 [" o5 Z6 D+ l0 q6 P+ v0 u( B5 [
6 }& V% H9 I) `8 C* r
& c7 K- c& Q" V* U! U' `% I: H- x

" V, c) c$ Y! ]! b$ g3 H) Q2 V
4 l" K9 C/ w4 j5 `$ X</li>
7 j5 u, ?/ M$ }! Q& A
( ]( h  P3 |  k( u4 W: e
/ D3 k- u. o" [
* {+ a5 ?' c" x0 ~( q% u
9 s! l& i9 U& q/ k( J& @! e# _5 a6 u+ B  E4 }

0 c' K) U; J+ @</ul>
4 |# P6 C* C. k3 f1 U9 ^& Y' U<hr>
7 a; F7 K9 `4 S0 h* l0 |<p>Leader宕机了如何进行选举?</p>8 S- Q  W' y! x& x/ `
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>/ O2 z7 G; q8 l
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>8 F: p1 F7 ~, G* ~
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
( J' }# l- K3 I. d# v<hr>
) ?1 f- ?2 B3 n; l& L<p>选举后数据如何同步?</p>
: N$ B7 {& X$ l% \- M8 a<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>8 O+ Y; D8 r% e! ~9 l7 k5 W
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p># a8 w# D- G- `. m  p% G! w1 K: X* m
<ul>* T, x2 a1 ]! b& d0 h" U2 K
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
% C/ f6 N6 _& S5 x* V) n; p7 O) h<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
9 M% j1 v  `# {; F<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>
: H. V2 Y4 T- F& W
) a" |# u: h2 D7 v' F2 Y$ _9 R9 |5 m4 W$ q. t. \

5 V/ O" ?  `0 Q  Z. g0 _/ M+ J! }7 F$ E7 ?8 k. h

( d  ?9 \6 }# t+ `- ~" L; Z# D/ S, }3 W6 J/ ?! s
</ul>
) }5 z% @5 S" f: {; n" ?<p>同步策略:</p>1 [# j/ C% g, H/ z% @/ G& P
<ul>
4 v# f1 @9 R/ |<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
1 m' c) |8 ?. [8 G<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>
, a0 q/ ]" Y& n, P! x, g& `, }7 Z<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>
# n9 [4 \8 m3 f9 n# o<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>% \8 h' v, m( S0 F* p1 F, h
& |$ a8 ?, w! b: l8 ~
" @0 D/ u# ?' {" o: A7 j% f

3 n4 i+ }* F4 s( n7 \7 h' |6 a1 u' f+ `  ^  f  t1 X
</ol></li>- Z1 H0 ?  n. b% f6 g
<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>" L% v1 n' N0 k4 i6 I! Q! F2 ^4 C
<ul>
4 [/ f7 N2 i& t, s, {" P1 f7 L<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>
8 L7 F4 @; ~1 u2 p- O; l<li style="text-align: justify">( y% U/ F0 l0 \3 S! }
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p># Z0 T/ p* f+ ?# s2 P' @9 F
! ?5 ~" }2 [2 l- ^/ e. Q$ e
: Q+ x) w, j& d  {! G/ b  ~
: k3 _, n& _  y  I6 N8 |# z: H6 \
/ I- e/ P7 y3 }( J+ s" Y5 X
</li>: {2 u0 S6 a8 w% p5 t
) o- s" v0 W- b5 m, `) B
8 S  t5 z* l  d2 L5 E( {; V6 x

' G/ n; h6 z% p0 ]9 v
" y3 h( {* L$ O0 y0 f</ul>0 M* j6 [3 {0 a& B. M1 z* L9 S

" o9 V' d0 F9 u' `2 |2 e+ L* r4 G  G; U3 A+ v+ `9 w3 e  H
% R' t- p" `2 B7 `7 B; `+ y0 ?- m

& Y5 I  i& D# n0 C( Z  M) r</li>0 ]' F3 M: t" _7 D
<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):9 \" n6 W& N2 P
<ul>3 A8 Y& m( I7 M: |( e% O  V# M
<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
$ D- Q9 S8 @' l8 s- C3 [<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>; u" X' ]3 j, [0 u% U, Y0 Y
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>
+ z; Z, q+ ]) u# C
2 ^* Q9 R" v* V( L6 y2 P4 d0 V1 g  `# I# B# A0 I. @9 Q

1 R9 U7 r) ?/ A# }/ G* z
5 R2 x9 |8 R0 ^% z. M0 K- V</ul>
5 D" C; V# [/ X$ Z% A; {1 [; a8 y  f; `" [5 U, i6 [- k7 U
4 Q; n- l2 E9 C' \$ W

% ^* l  d1 D7 V, ^/ x' Q
- R0 @: X9 I6 X" c8 f* d7 o8 J6 m</li>5 }( E! N/ e7 G  g7 e5 A% F' ~) }
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):
  d- a! m- V$ J" |& _. I5 Z' c<ul>
4 {* {/ a' i: `4 O: x, W+ x<li>
9 \/ y+ u; b# S6 \<p data-tool="mdnice编辑器">适用于两个场景:</p>
1 n' a' F8 Z8 C$ H* e<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
& P; s. U% p5 E8 c; v' {  b<li>PeerLastZxid小于minCommittedLog</li>
; e. K* g: ?  [( ~1 D% r) f) e2 f<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>
" H. \2 R6 h0 R! F+ L- S  q: v6 v
5 {/ Z! }+ D5 X
- s! o+ i2 q$ Y
8 s# F! U( s, e# q3 A
% s- Z: T5 [5 V  T</ol></li>
# u& y+ @2 _0 n( u<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>8 ~. c0 |7 M& f! A9 T' C# e
, M& z5 F* Y5 H! L1 u7 _6 V9 W

% y+ n9 G4 `: b1 Z; L/ [5 Z3 r0 K, @

1 H0 e, x" F! N4 H- x</ul>
( q% S" D# Y& N- w+ S9 e2 o0 z& ^

% ~, o. p. a, J' R
9 @" @- J' C  ~$ c5 [4 K" C$ y$ _0 `3 K" e& C! T1 n
</li>
- K7 \3 d& ^1 s. u8 x1 Q7 Q$ u. ?: Q

( h5 h0 n' y9 o5 J" H5 [' u$ l; N
% f& T1 M( c# F2 a" }) _+ {" n( j- f( g8 E
</ul>0 G: B8 V4 R- l- i( N( X
<hr>6 Z, p( _" w* @3 A! D
<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>. h5 t: s( O1 ^) _
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
0 P$ X4 ^* ?# _' h2 ]2 ?3 Y<ul>
5 A$ A/ y' X% b; W, T<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
: [! q2 ]5 [/ Y<ul>
1 X/ O" D3 V% ]! {' t: J<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>: ]* G3 }& n/ K0 `9 H* r
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>1 ^% a% J; j% E: L8 |2 b4 n* J: C

$ f$ S# ~# X/ f- o/ ?9 r. E2 ~! S2 C" i' V
</ul>
& C% v# H+ T! e! K
8 K, Z0 {) i' e  _/ v$ L2 w/ M' y8 H4 H/ g
</li>
8 l# q" q) E! K% Z+ T<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
' x6 c& {& m& `<ul>
6 `+ n; q$ D" Z$ a6 }8 W<li data-tool="mdnice编辑器">
& U+ ^- |6 w7 T7 v; c$ ~- V<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
/ A" A; ^( Q% ^
- [) I. t4 K2 ^. Q5 n
  Q( O/ V' h! G0 s% w</li>
6 _6 U+ Z. x9 B! P<li data-tool="mdnice编辑器">
6 @0 j- Q9 g; y* D<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>! [4 R* V( d! h* j5 Q
/ \6 v2 j/ a1 ^: @. l5 c

0 A2 B4 j/ D% s2 i( \) n</li>/ \. l; j5 c0 w6 m. _
- W9 i) O" W9 S1 F
7 x; N7 {5 q+ w
</ul>
5 a1 Q" C! C( s' D' Q$ Q* v) j' b6 U1 D' T
" V# h6 c2 y! }1 r* C2 ^
</li>
; y: a* Z% H4 G& `$ s. k. V<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong># M1 U' ]( Y6 r" T* Q: h  c
<ul>
: l' ?% \& ^% ]" O( S1 B; ~<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>9 s$ D8 I6 o- v; L: w3 V7 w

% _$ U8 v5 r7 u/ J8 b2 G# x5 d
. J, u5 C+ @) S</ul>. V# ^& R9 u% C
5 A/ Z  J( E, o  n
" I. @1 X( L2 I1 F8 o7 Y1 v# _3 X1 F
</li>
* W# O) n# }% n+ o  g% s. R0 F- t( z" y/ d: l' I; L5 H4 U

6 M: L: F7 Z. d3 X</ul>
% f9 Q6 f  _! b0 [<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
- [& b; [% H2 x6 [! b5 }4 [& d5 |<p>zookeeper 是如何保证事务的顺序一致性的?</p>
5 P& z% N6 [, u: R# W/ ^/ x<ul>
) a: n5 B6 ^. [5 [9 N<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
4 ~3 |7 \4 f( \0 ^: u) j
% u" x+ _9 |9 C
3 D3 X+ q1 I' }+ f( F0 f</ul>6 u0 T" r/ j) O4 q
<hr>
* B+ r4 u3 k! K( Q! H# ~: r<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>! m/ p. ?: G! i" y0 g4 j
<ul>
0 p, d* Y* K2 m<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
8 Z* ?% _' f0 {' y% ?! y; @
' l+ `; b; m3 f$ V
" l" g& v5 n3 ]6 A: f0 B& S</ul>
# A/ N% V6 A( d) m( n, }+ x# y5 m<hr>+ \& S& G8 z; j- f
<p>说几个 zookeeper 常用的命令:</p>
# w4 O, y1 _3 N* {<ul>
% M# l' U) t; P, e$ A- h, V: ]<li>ls path:查看当前 znode 的子节点</li>; v6 R* |& ~; H0 M$ I) q$ i5 A
<li>get path:获取节点的值</li>
9 ^* d& n9 w7 }<li>set:设置节点的值</li>
8 E# L9 y/ G" \# L/ P: i<li> create,delete:创建/删除节点</li>
/ M1 e5 J1 P1 f7 @( |3 |9 x) ~7 B+ v
0 B! ^  a- g6 B" s, ^. }
</ul>9 R1 ~% M) M; q6 J
<hr>- K; c8 m  w: Y( u, Q- e
<p>会话Session:</p>: r& h4 u0 O+ Y! S- U, |3 u* y
<ul>
0 o2 u, \6 L: S<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>
2 d/ P$ [) ~6 ]
$ \( M8 A+ H- k+ V! f/ ~1 r& U/ W6 Q- v9 M8 o
</ul>- O2 G: f; J1 C& N
<p>&nbsp;</p>" E* ?  V# i2 s' y$ |2 y
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
2 d$ Z1 L( S8 X- n! [
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-7-29 06:25 , Processed in 0.066798 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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