飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

7726

主题

7814

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
25508
发表于 2022-2-12 14:35:41 | 显示全部楼层 |阅读模式
0 T+ U0 s" T$ u
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>2 M$ j6 s$ w! ~) X3 _3 h. D& q7 y$ F- T# F
<p>&nbsp;ZooKeeper 是什么?</p>) v4 d. w- `+ Y5 t
<ul>+ X, W. O0 M) c& V+ e
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>8 z  n% a+ 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>  d/ U: e+ T) O* L( E. B
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>
# b  b( s, b- r2 P</ul>
( L* h4 x/ ^1 z1 O, _<p>Zookeeper的作用(应用场景)?</p># u2 p/ k" g! v5 H
<ul>0 B' H' q6 k/ |
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
$ F6 J* {0 T' @7 @8 N<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
/ x3 P- l% G" _: ^0 U4 ^6 }4 t<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
! T1 \3 P# N( Z% Q9 D5 ]<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调用。
' _& k. K0 I! m" j3 P' b  ]<ul>5 y. }4 z  q( s. {! F$ @; Y2 o4 \
<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
/ h1 ]  g  B" [$ `7 @6 t<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
: l% x% ?! C3 ]; x4 i% n, }<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>) N+ q4 [5 o9 V) m" m
</ul># Q/ E: ^* \# C9 E
</li>; T0 S4 J6 C  E; [. a. R- K! x
<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
3 Z$ Z, U4 p; W</ul>! L/ F7 B6 s! ?$ H& y* f! s- S2 f
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>
& ]2 b- j. c- p1 M! s<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
9 N, ]4 }- T1 i. I" j9 }<hr>; `/ U6 x) a& ^, c/ s; y
<p>&nbsp;文件系统:</p>
7 U2 R7 h( W0 J* y<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>
3 P7 [# P* }& l  j8 F<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
2 t) y, a" o5 k* Q: J& f<p>&nbsp;Znode节点主要有4中类型:</p>
3 _3 b- Y: s6 W3 b* X3 X$ Y<ul>
% T8 v( U2 U) f/ }7 M" `<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>5 I1 [- R& {/ D
<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
+ a. j# b- d& y9 D1 D<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
/ a: U* _1 J( u<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
% T& }6 d: b* W2 W. X</ul>
! {: z9 x, k0 I) [" Q/ `+ j: }<hr>
; o6 m7 A) H$ I6 {' B$ ]<p>&nbsp;通知机制 (监听机制)</p>+ O% o4 X; P5 S) o+ ~1 c
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>$ ^7 r; |& s5 S7 Y$ u; x
<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>
" j9 i  @) i0 n% F<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>  C! x/ @$ G% ?, Y9 [
<ol>7 h& }# I* G( Q/ }( d
<li>客户端向服务端注册Wather监听</li>, R) _+ F5 K/ t3 \) @; H
<li>保存Wather对象到客户端本地的WatherManager中</li>* G" ?% J2 z* y
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>& Z4 f$ J- n+ M; ]/ b& g
</ol>: d; E+ X6 e6 p& O& D( w
<p>&nbsp;主要监听2方面内容:</p>( b' Q5 R; y9 X# i, |
<ul class="list-paddingleft-2">
# y1 n) X) H5 X& y* [<li>
! x( S4 T' l0 h4 O<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
9 ^) j* |" _/ O4 k, @</li>$ P2 Q; q5 o1 F4 u" w4 T/ v
<li>
' i, u9 G" [4 N) a. Q<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>
5 ~3 p1 l: q8 ^. K; z</li>, s  d* a$ K2 ~/ E8 Y
</ul>8 U, j, l# p0 |) }+ Y! Z* ^7 i
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>
1 z4 r3 L1 T: l& r+ O/ q" a6 P) H<ul>$ n, S+ J( M- |* T" H4 Y
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>8 a! a8 C/ B9 [4 ?. c5 s
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>7 @7 o0 l7 d7 B7 O! [" a$ S: {; n
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
) X4 B( F  O& L0 `8 |</ul>
% p+ @$ h3 }$ x0 x# q: c2 J<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>
6 {! [* ]% ^" Q; L5 f& ]<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>
. }& H  F9 Z2 y5 L& X/ E# Q+ X! w<ul>+ J1 m1 a, {9 r4 j5 b" p
<li>Leader:负责写数据。(写数据都有事务)</li>4 l- n, [  A+ d5 g9 O
<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>4 a3 z# o* ]$ J) ]
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>
4 e9 t) `" k4 ]2 u- S8 L
' ?. V; ^9 \% [. j% h3 e1 v</ul>
' q8 m1 y. v0 _. K. n<hr>
7 F/ b. I. |( T2 U. e! j! q! n+ ~<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
" U% T; t2 l7 P$ i. X<ul>
7 B5 R# `. u7 [! ?; g- h0 m<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>% h5 J; r3 q; p* H
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>$ h6 F) b6 I, B- u4 o0 s9 H7 s
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li># V$ y" {7 o5 h: A5 u
<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
# H* c+ d) u8 ]% N) d4 B4 r0 I4 t
</ul>" A8 f% l- v; ~3 b2 z
<hr>, \# I7 @  C# `" }
<p>其他概念:</p>
! P- d/ D$ y7 g<ul># U' g3 J7 J' c0 r! O: k9 u
<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
" `) Y6 F5 Z+ f5 a" o<ul>
% b7 t; I* {# h8 u! @<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
, l. f; `+ x% @  F9 v" K. p* n<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>4 F$ o* H! S$ J1 I" e

* o2 E4 f  g) f8 N  w, e, L  K+ f. G* u$ x
</ul>
9 _6 @* G! T5 g$ H- v3 F1 f& z( b6 F1 K1 e3 l4 v

& ~# L) P( X, t& g7 s" Y3 e6 c</li>
4 N7 q) D& D; V- C8 E7 L
" t3 R2 H0 m& t; @  t/ u% y6 f  s) K5 ^* X* e& u' [% V
</ul>
0 j! G& {$ f  O<hr>
9 E1 w, [4 C4 R<p>写数据原理:</p>4 |" i; h5 @* t! v
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
8 W5 N; ], V- P: g0 }# R# @<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>" W- ?# a8 i+ `1 h  ?
<ul>3 ?, }0 s' H2 G% B$ `) l
<li>写给leader,leader再通知其他节点 </li>% {: e4 u3 n7 l7 i0 H
<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>
2 \: a# R+ M& r- ?2 S* N& {4 @<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>
! y; p7 z' K+ [7 f9 U8 n/ g; E% ]# x3 w. Y( C3 H2 y
/ ]" A. C4 f3 H8 k
</ul>
9 ?% ]# n8 @5 X- l<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>: L2 w( Q1 v9 P% t, j
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>" I1 r; @% U6 X! I1 W, W2 p
<hr>! U/ D7 W8 _" y! r; l
<p>&nbsp;ZK怎么保证数据一致性?</p>
! [/ R* {5 A( q9 A5 {<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>
$ G9 ~/ I0 N# d2 W* r& c<p>&nbsp;一般我们正常是消息广播:</p>
. F% z2 ?7 x+ a, Q: d  o<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
6 j& ~1 ]4 a/ T$ J6 Z0 K& Y<ul>
& g& \7 R' _6 w8 D6 M<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
4 H$ c% R; R5 e* U. y- g1 m<ul>
/ E% c% Y2 F- H1 T* U1 B2 _<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>" k% \  {5 p- z" C
<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>6 a8 D# x0 p# l

, c! v6 D. q! o, n) W/ i, U+ g0 l1 @+ N
& m" x* O2 d8 y4 u/ U2 k

+ h3 ~5 D, M( j# }2 z/ g; h2 C) l" H4 d  X' l. G
1 l$ V# z2 W4 D6 o% V8 z+ L
</ul>
( x0 a  m9 ^) u' N9 ]1 L; c5 U/ d$ z/ r
( ~% x! |2 j) H. O  n3 N' A; _
( }. }! y, b/ O" N/ j

+ G0 E2 ~1 r* L3 T+ z& k+ \" `$ C3 l; {; \/ @

3 ?9 s0 V0 n$ q( H</li>) l5 R/ y' q4 w* w/ k; _" e
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3/ Z  r; K2 s/ Y1 [( J! s, h
<ul>
2 V7 ]( W- Q" W' x. c9 {<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>
9 @+ I+ R! e$ E2 |) }7 O- S9 l
- X2 i3 G* b" G+ n/ V0 {9 ]3 {8 W! r8 p" ]! f' A3 l5 t3 p0 |

. |! b, b. s; \0 ]3 z3 u+ g) ^6 ]& Y/ M0 @

$ |; w7 R5 J, A! q9 x6 U6 I/ \& n- A# @' \
</ul>% W9 M% c: [4 O% ]# X
7 v( Z+ u7 F; S$ J8 u

" f2 @* o8 o; n, q2 j: D" S& i. R: I7 W

0 V' h( P# t2 e3 T: l+ i# h% x" H% Z+ i% v1 X  C/ H

4 J- v% n, b1 H3 ^- _5 N( V/ X" a% }5 p1 t</li>1 U; ]# x% L; K$ ~
. u) r, L- p" Q1 U* ~6 j

# ?" n# J: E" H3 N: s5 e8 n& C
6 |# c! P0 S; m5 S( v
; G3 {% G3 U1 R! S
" k0 Z( @/ f- ]3 I7 H  L, b8 {  a0 l
</ul>
0 O; q- d% l6 @7 J<hr>
9 y6 p6 k4 h0 L/ b' t- P<p>Leader宕机了如何进行选举?</p>' H  N; g; N; b: e; v# G
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>
+ L6 X# q) }' P8 L, u<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>% r5 m7 u! F* [
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>( {: l* p8 h% h- ?' L4 K  u
<hr>: |! U5 k; T1 }" {3 M& i5 v+ N
<p>选举后数据如何同步?</p>8 e4 w! i3 i- j( R
<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>
* e/ t) H  m' b  C<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
2 M! `/ x' _5 E7 o. V1 E<ul>  J# ^8 c% y+ I' C; w* x( Z
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>7 N/ E+ {/ h/ {3 o
<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
7 M- ~% @9 _4 r, V9 ^! E. x0 _) \<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>' B! `. q" V) k% A
( z) C( g1 D# S  A8 z* Q
0 \8 t' @# b- ?
) n9 n4 ^! q9 e) A0 W( p
6 `7 j+ X6 f7 u% z7 c
* \) ]& h8 `7 t1 ?2 W

( z/ M6 F8 H5 e) f. }$ @% C</ul>4 v4 ~/ }/ W8 Z" y9 J4 K
<p>同步策略:</p>  n. |0 Q3 |0 m6 ^4 n. _( H1 `
<ul>" A% I# m9 c5 S/ Y" }7 c
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>
, m( t+ |2 _3 I2 K0 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>
0 W5 k( q4 }/ C" J8 A& i: D3 S4 r<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>' m4 X$ D6 y  \5 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>
3 \! ?' O  n( e
- ]* _) M' a5 g. G! M! o% F0 M% w  x- Y( E4 g

: ]& s" H1 d. I/ r
' T; ?" L2 i9 a1 \" m% ]0 u4 k; c</ol></li>
3 ]" M2 K* e# v- c. y2 j& T3 @6 ?<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>
& I% j' X8 P  |<ul>, x* U; o  C. e5 b0 `' q* h7 D8 Q
<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>3 I& K+ ~6 v5 P, {7 L  V! R+ b
<li style="text-align: justify">5 p0 M* B2 @" r: h
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
( i; l' D  k' _! Y+ K* ~
) G- x7 P/ z6 [3 k& k7 Z* B" B! I# P* x% P+ k/ {

. N" ]* w1 \$ ^4 j% K  Z) p' B7 R
7 u1 E5 o& t. C3 M+ ~9 C6 ]2 l</li>
/ a! |) t2 A; n8 q; ~+ g& h
8 F' U; m* H/ o# |3 l8 i( ^
, f+ I3 s8 Z0 e$ g3 N2 P# S: k3 H
) n( ]& m5 {: ?! F+ g
</ul>
5 r/ ?, A' m. X! ^9 U5 N
2 D/ Y  v) l( T1 E4 \3 g  E! D, x' K( W( X/ d9 \2 E

& K% H1 N  w1 u8 h" D  h
* ~  p; S, U3 e& }- F  k5 e</li>. F4 U1 y( R# X) l
<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):5 m/ ?+ X9 l6 |# I
<ul>1 N( `/ `3 A) q& y+ I* L
<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>. h' b8 v6 B8 G
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li># @2 A( E, b; `
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>- t5 {" b) s% D0 r/ m
( \9 C& ?/ _& J! [2 d4 x7 i' X

* Y/ Z# i, p: H9 F4 k1 w6 R" y& K0 j' r0 R  F0 ~

0 L% [4 d7 n, V4 J' c  s</ul>
3 U6 i9 O2 W9 _9 `
4 w: V# W0 R  E& n9 ?- M6 p6 f: ~" {' q( ^6 P
8 a4 n) P  V6 t- y4 L& ]4 C: q6 w
* w( s% J& o2 K) \) T  x
</li>+ M8 d: P) J; r
<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):0 l* W1 w; r! W6 H
<ul>6 Q+ `$ i0 M9 ]- d7 \& d; ]
<li>; f0 w; G! _7 Z3 p, N' }$ e
<p data-tool="mdnice编辑器">适用于两个场景:</p>6 W9 v) O" [4 z/ ^3 A8 G% y; f
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
- ]0 }9 t+ M3 L$ Z# g3 L<li>PeerLastZxid小于minCommittedLog</li>: N& U: A9 t; ~& \
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>
/ _! f4 b' j! W& K. u# ^
* h$ ~% X/ p: v! @4 G3 r5 C* t6 x: F( {5 B) i
7 G& a% V1 n6 C4 _" C% l9 a

; W) }/ O; O- V( l7 O' k1 q) N* ~) g</ol></li># u: F! Z8 {8 F- b
<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
# b+ X* b  r% M' U# a- I
. Q- X! g! t& W' t7 ?7 k5 E  _/ \4 N5 n  f  V( q

! E8 G. Q$ ^& }: r) u0 V% D/ _- w! P* L  R# q% c3 ^1 m
</ul>: E7 J0 I5 {1 o5 N
6 i2 s0 R1 n  P, z
# D! o) T5 |  m% S2 m
( n+ l& {5 D- B: q/ W

! T$ F4 m& i4 |# {</li>+ A/ W! J; A8 ~, d3 ?

% Y' ^* D8 P8 l) r4 a1 R9 C6 D8 e' I+ [/ ^; F/ U0 g" Y
! F- _' ]0 {4 C, b1 n1 {! f$ @
0 n2 i4 [1 c% y% H1 i. w7 O; I0 G
</ul>
& a7 F. d: X6 X8 l7 Q7 k<hr>
, g) t4 x8 j( k( I) V5 p# J<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>) x5 c! c% s; u
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>% P2 a! ^& x6 a/ Q- D# C) m' {
<ul>4 B+ C9 ]! E( L0 J* _, w
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>: l+ R: h. Q& K  O  j
<ul>
1 ~- i# \: n1 K<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>
. q% r0 K& N4 X/ Y5 Y<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>5 m4 r' }( H/ B5 V! a1 H9 v

3 g& \6 S9 V- _7 S6 I
; U9 |+ G& N- s: J</ul>. Z1 ^0 a5 B  E' ~

* {" ?" w) Y3 v# _4 i
" L! Y  [& D$ I$ D</li>1 E- n3 L/ v# E3 j
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>- g; f3 \$ \/ h( b
<ul>+ A" m* U( v- K# A) g! k* g% q# S' n
<li data-tool="mdnice编辑器">1 k' p4 U1 @& D5 w( ?0 M
<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>, r" l8 F9 a; a0 X( H  a/ P

8 L+ F2 ?7 T! T
1 b% i* |0 ?0 `. C9 |- `. j( n6 S</li># x# e( a  I- n  L
<li data-tool="mdnice编辑器">2 I7 N( O& G! K& n# Y7 r- V
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>$ s  u/ j) t* U- \, s9 L' d

! s0 d" n+ v! c4 Q$ p- J5 a: s; ]3 a8 K7 x8 h
</li>
7 w7 x+ q' h" x1 g* C
, l2 @0 Q, |+ ]5 @" I  H" K( r
1 |- \' K. y$ F1 X: I+ Z, X6 ~</ul>8 H9 ]- M  r7 k$ U3 H8 f/ r3 o( O
- {5 R% K7 o+ K
/ t8 H6 ^; T. S' g' X  U
</li>) t3 O! n5 c1 j% F) R
<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
+ ?) \$ X; y: o. C9 r" q- u<ul>: E# E/ T0 x$ S( B1 a/ i
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>/ r4 u/ g0 E: ^8 w
6 x% R+ K/ S7 F$ s" B& g
* i& |. g: I' X: s: \
</ul>, O" j6 I/ x' }$ {% P

6 q9 V5 g* P0 E+ f
6 O4 E; j6 @. S* B0 O</li>
4 Z8 f, x) L! r& p7 u0 {2 T0 q3 r& Z* W; c. K4 I

  W8 k/ b& j. G# b6 t</ul>
+ m: g' r+ ^/ D<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
7 h3 x: w) k# V4 H<p>zookeeper 是如何保证事务的顺序一致性的?</p>, ^* \1 l7 E8 D9 d* W/ s/ L) x
<ul>
% r: d- b0 D9 D3 F) p<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>" ]/ C+ d7 a5 @- E6 L
7 ?& i* K3 s( S! C2 A" K
2 k. O: `- M2 Q: k0 M0 }, f9 o
</ul>" G% b7 B- H" @( w
<hr>
- f8 D& J, |/ F: a& t8 b9 @* V<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
. D# z2 y9 v+ v' V. |<ul>6 i+ {: K, m6 X3 q: h8 h
<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>
5 ]/ k* Q6 t' `9 {. [# j0 G% @) l4 X; n- h

' @$ V" y; l' K& L</ul>  p- E" ^+ y9 i' U# X$ f0 q' \% [+ w- m9 ?
<hr>
4 W# {) f9 a! C<p>说几个 zookeeper 常用的命令:</p>4 p9 E6 L5 ]0 R0 E/ Z7 R
<ul>! N& E# G% F$ N7 ~( Z  Z% B$ k
<li>ls path:查看当前 znode 的子节点</li>
. _) Q8 R% B, _  C; b7 U<li>get path:获取节点的值</li>- k5 J0 ~" J! W# [2 z
<li>set:设置节点的值</li>
" L' z. w/ a/ J5 F& a1 y- ?2 @<li> create,delete:创建/删除节点</li>
4 a9 F2 C3 Y% A5 l( |3 M' E
. B7 x( ^3 }# E8 D& I1 l: S; l1 O: q
/ ?9 S5 |- D$ w8 `</ul>; v! N$ j7 l3 B0 n# L! B; ^& M
<hr>  ?1 F& U, j/ w
<p>会话Session:</p>
0 u( J: ?6 J1 M% X, q<ul>! l7 W% |& K8 D1 D+ r9 B
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>1 o" ]" \" h% T$ |0 m  r3 l

# V. f- X: L% N* v2 u+ `. w4 J7 V! X1 P' i, k
</ul>1 r' S8 A: y) }8 [# r0 {
<p>&nbsp;</p>& k' L1 R8 m9 F, v$ s/ {0 i; j
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
. Y0 w7 {. K0 ~$ I( \% q
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2025-10-30 23:25 , Processed in 0.069006 second(s), 22 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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