飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

8825

主题

8913

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

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

! c( j! X; P& u  c<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>; J$ h1 b- U! c6 O& {* c% h  o
<p>&nbsp;ZooKeeper 是什么?</p>
& m, Q9 i) \" i9 y9 ^$ `8 i+ u<ul>( W1 D, `7 t2 z7 E/ p
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>; _0 e& d" t( w0 s8 X( m5 _: o
<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>. L1 U  ~7 m& p, ^6 O5 P
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>
. f0 g# W8 B5 R# n. z5 q</ul>
) G8 g) a1 ^9 [/ L# ^3 ^<p>Zookeeper的作用(应用场景)?</p>
( Q, C2 W+ g# G9 C<ul>
! [+ a& @4 v- _<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>
( l" I+ ?. J8 K7 |6 b; f<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>
2 q$ h- k" h' y8 t<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>
3 u. W% ^7 \9 c3 n0 D9 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调用。! I' B3 B, x& z% |; Q1 O
<ul>
  c: \4 X( h  e- W2 R4 R<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
- e& c' q$ b( P<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>5 y+ l0 a& d- G
<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>
6 F6 I# ]$ p4 O% J* h</ul>
) T: |8 y4 c9 h( K6 g5 d9 U</li>
. N* i' G2 w1 I0 K5 I  `; }+ e& ?<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>) y. Y& ]! W5 Z  E5 x6 N5 V% Q# r
</ul>% h& J7 ~( e2 _2 `" I/ j& [
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>
, N/ v# C3 u; k8 }7 k7 k<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>$ U% P6 U- Y: r; m! g
<hr>
" P! B7 e/ M/ m& I+ U4 W2 C# `<p>&nbsp;文件系统:</p>
  `+ r" |& T" j/ I6 @6 m<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>- Y5 r, r7 @; W$ ?' \
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>4 V+ y, B: T. {
<p>&nbsp;Znode节点主要有4中类型:</p>
# l4 y2 `2 I8 `- y- |3 n<ul>5 P7 @" R3 A9 l# D
<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
; H: J# ]) a3 I8 H( \' A) Z<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>' f2 A! W3 ?. M& o+ |
<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>
2 G- t% x1 q( ]* m) t) j5 Z% S/ O<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
' q% D# U2 b0 H; P. f  M</ul>) ?. d! w" e/ E/ x3 _
<hr>
, K7 q- j; M. K4 d<p>&nbsp;通知机制 (监听机制)</p>* o* U! U& |7 X, x: \5 H
<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
6 K! o& e1 K2 t8 }: S<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>
2 D# ]1 t; B- e( O7 q$ t<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>
+ O& m1 N+ P- X4 x: G<ol>
+ i5 c2 i4 M4 r& o0 o+ W0 i<li>客户端向服务端注册Wather监听</li>
0 b  l7 J& J2 y. q4 y9 U+ r" i  w9 w<li>保存Wather对象到客户端本地的WatherManager中</li>& W, ~+ l8 a4 \
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>/ t2 L8 I# V$ m% U, R* ~$ @
</ol>9 ], f, E( E$ M% p  F8 t# H
<p>&nbsp;主要监听2方面内容:</p>5 {+ J, V/ `8 O  `% @- k
<ul class="list-paddingleft-2">
! }& u/ C( e' O! q) X3 l<li>/ d7 V5 y) Z% o1 m% U- I' M  K
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
% V+ d& R- _: r$ g6 B4 o  y# @4 n</li># R& K$ n7 [* K' \& c4 ]
<li>
9 {" o0 e: J3 P# N  e8 J<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>/ X- E0 Y# c' N# R
</li>
; s, y0 o1 ^1 h2 n3 P</ul>! W6 _0 f9 `' H' T) J
<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>8 D4 y1 _7 S8 ~* r2 W  S# T, V
<ul>2 s4 @+ _: G( s. V4 r: s
<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>0 j3 w) {8 M+ E! [4 R. ]
<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>
# E+ l" U& \# K& R/ \6 a; u<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li># j$ H3 E( Y) k( F
</ul>( x' @0 P' E! |* ~& o& y
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>, q+ \' T' y8 A9 f! n
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>" Y8 H- z; t3 y0 O5 `
<ul>6 U" l9 v8 b( Y/ W
<li>Leader:负责写数据。(写数据都有事务)</li>
4 f& d! z  \( d& m" J' K1 J  [<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>
* v8 O: g' z9 n6 ^' l$ T/ V" f! p' T<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>' V- N  N+ Y- x3 f  ^' @

, I- s$ V6 l2 @</ul>
) p3 j) S" g/ B& X* e7 X<hr>
, Z3 E/ l# g+ ?7 y2 Y2 L<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>) P5 X7 w5 u/ L' @! f
<ul>
) T& j, R, G$ H2 x# U<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>/ m6 p, m: l$ P7 [
<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>) l' L' D# Y1 o7 s+ t0 ^2 _. Y3 L$ D
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
$ [. T1 `- J7 I: l% L' F" A<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>
/ i8 B8 H6 q9 \* @5 e  H" _4 {% S. }8 ^" n; i
</ul>
. R* E  A2 y* s3 \8 g/ u<hr>4 t. }3 Z" r9 S# d9 m" L9 ^/ H6 e" J; ~
<p>其他概念:</p>
  Q+ U. B( p8 f( h<ul>
; u% m6 {, O' a% a& R<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:" D$ q. ~" B& n) a, A) d% r
<ul>
; p/ x- ^  D% j<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
( F: C! \; B/ c( ^<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
* B0 w3 r, ^2 U/ r  a0 a9 `9 U+ G8 H( Y! p. m/ h: |% m

0 R6 [; ?# Y6 {' `# `</ul>8 S4 E% R. {' S8 |9 e$ R2 \! C

' O$ B! A) G- T/ Z" i
( b7 _# F( F: n( `! w) ~4 {4 V. J  j</li>3 ~% f* j0 T0 n) r2 n7 l
; L  z; [4 Z: X: p( c6 Y
: F% z' N, e. Q' F/ }2 Y3 H+ {' r# [
</ul>% H2 y6 j/ d) ^/ |  b( m0 }
<hr>: T% N2 w  }2 n- d
<p>写数据原理:</p>
1 l% y! L$ g9 F+ b4 X1 z+ g<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>7 [; ?4 d- ^; M' R8 ^, w* C# h5 J& ^
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>2 H& o; _' O9 d" B- r
<ul>& `8 j* S0 _- H) |5 l, I0 I
<li>写给leader,leader再通知其他节点 </li>0 H# F+ _, o$ X% q' X3 F% b
<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>! |$ a8 h4 S9 Y0 x: {3 q8 `
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>; y6 }. G3 f9 r7 m7 ]* f$ J
# m( w7 x) D4 \6 G9 o
% L+ [6 ~0 w9 |
</ul>. a8 X# H7 ]* {/ m. o! [
<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>5 v+ d9 Z) |* D" v- U0 J8 X8 j+ b
<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
% o& W8 L5 S6 t! D- o<hr>
% o/ ?. h1 ^( M8 F6 t<p>&nbsp;ZK怎么保证数据一致性?</p>
0 n6 H2 Q8 L  o<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>4 d( y( T6 R0 z9 ^7 U8 B! W
<p>&nbsp;一般我们正常是消息广播:</p>& P+ w. W, r4 F/ s+ j6 d* X
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p>
9 o( {* p3 D) d8 s) H2 g. Q<ul>2 |! S' C8 E4 v0 _- _/ }
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2% u& P% N4 f: p- V6 a0 k
<ul>
6 ~! t/ G" W% i4 O<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
# `* o6 s3 X+ B9 d$ q<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>
3 j5 [( c/ K+ }$ k4 j1 ]  V" |7 c7 v

3 Q9 j6 i7 c; G/ n" |3 s4 n" h& B
/ N" B% Q$ \& I/ S# ^7 d$ [- j, E: H* K; h* v5 A
% m8 m0 J, u5 t  r' g2 o1 T; E# D: L

9 h3 V% I7 j% U( b" P+ c</ul>% g8 R6 C: H: r# V

1 C" l: j6 _) c5 J- [2 H# P* G( V% t- m0 @" a: r' r
0 e+ D& x$ E1 m! ?9 A
" v; w! k: b, K8 k
. l6 z0 n3 R4 Z9 T. v$ M* X. w3 r

& v2 ?: E2 h4 J. r, U$ ^0 ?</li># A- B1 j/ Q( s7 a
<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3, x/ l) Q6 X  K
<ul>
4 b+ e. f2 w0 d2 K<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>
' `2 E: u3 V% {& q( i6 v1 B* M$ W
& G' t  c6 B7 Z
7 y# m. U# P% `, Y
% Y3 s) P$ j7 M$ k- C: h1 r* S* k( h1 E3 c. c8 _5 _8 n6 p6 z
6 B$ B  d, Q3 h6 G! e  z. w
1 C  G, N0 I4 ~! X" J+ W8 y
</ul>. g) k" O1 J8 _9 [. o

0 W* Z  E* c/ s7 I* T  _7 F% N

6 D. r+ K0 X1 g+ c" e+ Y9 ]# @9 I
$ P) O( ]" Y; f' W2 H  ]& o/ X$ L+ t
" V7 ^7 X) d. E) d
: @  p' h4 G6 O" p</li>6 f. P9 a2 F2 W! z) v/ J7 s
. [' K& @2 X) W3 Z2 \6 y
/ ~  k8 S" M# h. E/ T
6 B" p; ?, j! [7 V: ^
/ j1 h3 l; y7 F; @4 o2 k, s

1 O/ ~: {6 i* o
# M5 H$ D( l6 w9 B</ul>
( A- {+ r/ L" U3 P, |# e<hr>1 V9 {2 c: h: W) k0 c
<p>Leader宕机了如何进行选举?</p>
6 y8 K# w" N. u( }<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>) v& ]/ T. F! n7 a+ }' @5 r4 u
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>
7 h: \- q8 e$ |, Q<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
+ G+ U  B/ d* w. H<hr>. y8 z; K, t/ H% C! L, N; y
<p>选举后数据如何同步?</p>
. \( `5 G3 Y7 f4 X<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>! g& c' `, m# Q; z" L% R% r
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>1 m. |7 `; s3 @0 W, A" L
<ul>5 l" x& a7 N0 p- v( z
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>
7 ^- l4 ^6 j& ?" W5 V<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>8 `& D, ?; n& e, A9 w6 D1 t+ B
<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>4 K2 H- Q9 {! X( O( E! d

/ G# S$ a4 i0 J+ Y4 L; E7 j
) @5 Q2 N" `  F, l( B
5 U1 l+ C+ t2 _' ~
, \2 f9 O) x1 b# n& ?, E8 N2 a3 X
* \6 z- @. t- V) L* D0 s7 |8 I) G" W$ N
</ul>7 ^+ \: @0 [/ U# G& Q- j
<p>同步策略:</p>
0 M' T0 s3 m" ^<ul>
# ?" t2 b1 G3 M- K. [8 G/ C4 c7 A<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>) [3 f8 U- c5 q" T
<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>
8 ^; U6 }" c; V<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>
( E& K9 I* o; o) J& U  @/ k7 Z- {8 J<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>
) s7 y( d. r' W: |. r' D, v  r3 w" A7 [+ g; T# W8 M
0 o$ E$ X9 i  y# t. J0 M8 _

! X1 P5 q! i" b+ T+ y3 C
9 m  h5 c- Q/ _9 B</ol></li>
9 r2 u" Z) k5 D+ n+ w3 f! q<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>  J7 U, n2 G5 H
<ul>( E( I! H& z7 H/ x* g% g6 p7 Q0 t
<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 B/ J2 K$ R8 }9 G- w) D
<li style="text-align: justify">- {, H, ^) j' N2 L8 g
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
  `/ T' |6 e3 g$ G
2 S1 X# C/ a4 [* }' E/ L# o5 D3 T; L7 B. ~

4 A+ T+ U' m( W3 d  y7 A% c9 t( v% t' _7 r+ [* Y
</li>3 K; m: `) B4 o# o* M7 w

2 X5 V/ o  ]  U% w2 ~
( n- v' g3 Z0 L* N6 J
0 d# Q2 j8 `6 U+ R" N3 ]% m
& b& v& u* B0 B* {5 }</ul>
- x- t, R4 q8 U# d2 K3 E
# p, S, q8 c/ c
4 G: U# ?0 H1 E# x1 F( z$ V6 B4 Q% M

9 q) Z7 e( L$ O! g+ k</li>, Q# Q! j- _( n, v1 I+ [6 i" }' @% r% y
<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):
& M2 c( h6 j5 D. f& K% `<ul>
; I. e+ ~2 `1 O% w& B. h2 u- ]# x& q7 ]<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>  h+ b& V3 ~7 `) S3 K* |2 s9 `3 T
<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li># Y0 s7 P# n3 c5 ~+ d' C& M
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>5 Q3 O+ M% X# p$ ^! v3 d

4 g1 }* r9 i, s. U8 |+ d& W6 w  x" o1 v. W& A) Y$ F4 D( t5 {9 P( h, u
+ w, |, G, x" N
# W7 x) z3 y% J4 x
</ul>6 d' Y0 J- R1 Z, m

5 K: T! u& [' V% M2 Y# `
! v4 B. d1 L# W6 t$ d0 W1 W
! W& c3 j9 w- L- a7 T5 ?& X
0 y1 @  r& m% F2 x) e1 t</li>
; Q* b0 O! W& O3 D<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):
0 n/ l6 Y& i  n6 m<ul>
4 `- p6 k2 o3 S: Q9 r<li>+ q. S* b( b8 q; u$ v
<p data-tool="mdnice编辑器">适用于两个场景:</p>
4 ]+ `/ \) l5 M<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
; T) b. B/ F* ?" N# N( l<li>PeerLastZxid小于minCommittedLog</li>
. a$ y0 |7 x  c1 d' @/ Q<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>
3 G2 f/ U# P6 P
$ a1 g4 e- H2 u! d& ^. `
# g$ K3 [0 w% n4 a3 E- y5 g6 E( ]$ a9 ~7 r" U
6 _4 k7 S8 f2 S0 {5 {
</ol></li>  Q4 k( t. @! }, N( D
<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
5 V" a0 h& W3 @' y
& U) w& D/ T$ V2 ^8 i: W9 i6 r
. U: Z; `( V2 U4 ^$ O1 D! i6 n, f8 \3 z" ^$ f9 v2 Z
. x7 \) L7 `! d+ ^
</ul>% H- Z; i* f: R! v- ^4 M4 ]

/ K) l% t- K& \' \* Z9 K" L- o
, d+ z7 [& ~' W8 [2 r
2 n# y  g# T: H) ]' D) i- S$ u# K* s, |! z& B8 i
</li>
1 i* `  q& K; X8 E2 J; k
2 P# f. k# I+ L
" ]; M2 K/ n4 Z+ r8 @
) u/ B& o" l! \: r  X
* w) t) d& i9 }5 }' f</ul>* f' g3 ]9 w# `
<hr>
9 N  U' c: T# G! T7 V/ F8 h2 n. P<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>2 u+ S  D; L5 ]4 C; x: q
<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>  P; R3 \1 P4 y5 A! f
<ul>
! [8 L4 }3 l. j<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
( e- a0 `+ `$ `- A( k5 p5 q6 \) [: f<ul>
1 R. a9 _% W) x0 T8 U$ z% b<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>) @. p. @4 y# h
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
; x. [9 [/ W+ _8 v9 _+ ]3 m5 ^7 F1 ~8 w

# X- o# R! @, L</ul>: `7 s5 `, m& S, S' D& Z) D
* o* H+ y& T/ r! z7 i7 V
9 O% @& y% k  F
</li>
# m$ f$ h: {+ i/ g$ ?7 A<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>: z3 f" Y. }% @: v7 @9 P
<ul>
; s. D: C8 d  s* p9 g; r<li data-tool="mdnice编辑器">
) a# l: O7 _. A2 I1 P7 C5 o7 S<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>
+ |3 m* y1 j0 b/ ~" u4 ?3 u/ w- G
; d- Y5 R% B3 J4 r3 X! u: M0 t# Q+ L" L& u: J& T3 {
</li>- A4 _3 o# H0 K# V
<li data-tool="mdnice编辑器">
$ W  v! G7 B! [6 c9 S. b<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>
) o( u) m1 A8 O; x7 D# y6 k
1 J. y0 v6 a6 q
+ l0 ~  Q/ H$ M; i$ \6 h</li>/ \* P# j6 |( c: X! x1 r' T8 {3 E7 `

2 Q: n, H5 w) d/ V/ V1 \- L) D) v7 {1 u
</ul>
, U, {4 p) [' L2 ~, l
. i' Y- x, m: ^7 y  J
' j4 P6 I0 S* j+ C+ C</li>
6 V- Z/ v$ v+ S3 Q: L; I" a$ Y# q<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
! `. l5 d$ k/ J' M<ul>
. h/ A! e' g8 I! }+ j8 T5 R0 {<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>$ x; @  H: V4 |; S
3 g8 W7 B, L& w) p- e3 U
' \8 L) Y- z  r3 l1 T
</ul>
& i% I" e5 i0 [, b1 ]
0 M* X$ [3 U6 r' H' T  _* A1 @8 u- z# m! c2 i6 Y2 U% w1 T' _7 w
</li>" Q) L7 x3 b3 p, I$ i

! K: g9 W2 `, I0 t/ V& W
" J* |" a5 h/ ]* [0 `# f7 w7 r/ j</ul>
. g1 x& ], K6 ?0 k2 E<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
. f& _; X2 g- l; e* ]<p>zookeeper 是如何保证事务的顺序一致性的?</p>! j) ?8 _: [2 L: ], ?. v' q* s
<ul>
) v2 x; q( ]2 ?$ k! ~/ {: C<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>
; I* k. F$ q0 h
% t/ \$ m6 `( L7 }, `
5 t3 L) I/ z# m. H3 n: s</ul>
2 g% q% q5 \" z  Q" F<hr>
: p6 ^9 V3 i- A<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>& K5 P; g6 W4 N" f- H
<ul>
( ~0 o9 x( k# C5 A6 F- m5 q8 d<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>" f( C! @6 T# L  I6 g6 m

, i, p1 q) U. j! ?+ T( b) {# [( J& A1 ~3 j6 K" s6 _
</ul>2 y5 z! i, i8 J( h: w
<hr>; A* }5 v' ], z5 G( A
<p>说几个 zookeeper 常用的命令:</p>
& O' _7 \, L+ z, x" M) Y<ul>- |  I( H* ?8 g8 m2 h1 [: u
<li>ls path:查看当前 znode 的子节点</li>
7 U( i! k- p! T. q5 h) z: L<li>get path:获取节点的值</li>
% S) a+ y) d+ u<li>set:设置节点的值</li>
, E1 H" N; }4 \* W' M( Q& r; F+ Q* G<li> create,delete:创建/删除节点</li>
# ~# `4 O. J+ r3 r
* ?1 m$ V+ ]! v6 n( r2 y# b) r( y+ k  F1 Y. q- u: I
</ul>. P9 Z9 Q( T, t- x: D; T
<hr>+ N3 M: ^) n  V
<p>会话Session:</p>0 g- |  v/ x0 v1 m& q- z+ Z9 x5 C& r
<ul>0 k+ ?7 F7 R; G$ R
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>+ e% z/ y! w* F  m+ q& m& j4 d7 t

* M7 d, S7 s5 T4 d/ N
1 \% J+ G% i) d</ul>! ?! @3 M" ]& j. ~0 |
<p>&nbsp;</p>' Y5 W. h' T8 g: _' [. W! e
<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>
, X, L$ _! W8 O7 |8 H* W9 T3 Z
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2026-5-30 03:23 , Processed in 0.067854 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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