飞雪团队

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

一文搞懂Zookeeper原理

[复制链接]

7327

主题

7415

帖子

2万

积分

管理员

Rank: 9Rank: 9Rank: 9

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

" v  x1 S; V( }% y" W<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">一.概述</span></strong></blockquote>
8 ?% ~8 n  t$ P5 B3 F- E* c<p>&nbsp;ZooKeeper 是什么?</p>! }0 R4 F2 s1 m9 Q0 {( Q2 a6 U' W9 n; B
<ul>$ L4 ]6 e* b$ m
<li>是一个开源的<span style="color: rgba(51, 204, 204, 1)">分布式协调服务</span>。使用分布式系统就无法避免对节点管理的问题(需要实时感知节点的状态、对节点进行统一管理等等),而由于这些问题处理起来可能相对麻烦和提高了系统的复杂性,ZooKeeper作为一个能够<span style="color: rgba(51, 204, 204, 1)">通用</span>解决这些问题的中间件就应运而生了。</li>
! x3 i9 ~3 F( q0 V: @: e. @6 q<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, [( k; ?
<li>实现原理:zookeeper=<span style="color: rgba(51, 204, 204, 1)">文件系统</span>+<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。</li>1 R4 |; i" E* n" J0 S
</ul>
! x$ G6 F0 _+ u<p>Zookeeper的作用(应用场景)?</p>2 I" A" Q( I) {! B, V2 N) ]
<ul>" S) u; W: M2 M+ V
<li><span style="color: rgba(51, 204, 204, 1)">统一配置管理</span>:比如现在有A.yml,B.yml,C.yml配置文件,里面有一些公共的配置,但是如果后期对这些公共的配置进行修改,就需要修改每一个文件,还要重启服务器。比较麻烦,现在将这些公共配置信息放到ZK中,修改ZK的信息,会通知A,B,C配置文件。多方便</li>' a" l! G" z- w* h3 W9 {# q6 {& O0 C" p
<li><span style="color: rgba(51, 204, 204, 1)">统一命名服务</span>:这个的理解其实跟<span style="color: rgba(51, 204, 204, 1)">域名</span>一样,在某一个节点下放一些ip地址,我现在只需要访问ZK的一个Znode节点就可以获取这些ip地址。</li>& ]3 Z! q: Y, U* A& ]- j# ~: t
<li><span style="color: rgba(51, 204, 204, 1)">同一集群管理</span>:分布式集群中状态的监控和管理,使用Zookeeper来存储。</li>4 J! ?1 F/ g, B& d2 d/ q
<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调用。" |* C2 S0 Q7 A; ?
<ul>
/ w& |4 W, `! Y3 D<li><span style="color: rgba(51, 204, 204, 1)">服务节点动态上下线:<span style="color: rgba(0, 0, 0, 1)">如何提供者宕机,就会删除在ZK的节点,然后ZK通知给消费者。</span></span></li>
" C  ]& o9 I% S% u<li><span style="color: rgba(51, 204, 204, 1)">软负载均衡</span></li>
& W- x9 \: {; s' _  b3 D<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>
2 A" g! o0 X3 O7 k' z</ul>
, F6 Z" `+ ?) u& G/ D! H</li># x: Q( h$ I, \& ~  e- D
<li><span style="color: rgba(51, 204, 204, 1)">分布式锁</span>(后续出文章讲)</li>
0 Q# m. k! y$ _8 F6 t% L3 g- B</ul>
- N1 ]! o4 l8 l- n' r1 c<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">二.原理</span></strong></blockquote>
0 v. I7 @" i- t<p>之所以能做上述功能,主要是归功于ZK的<span style="color: rgba(51, 204, 204, 1)">文件系统</span>和<span style="color: rgba(51, 204, 204, 1)">通知机制</span>。下面我们来分析这两个机制</p>
( t5 ]5 t) M" W; ^6 S<hr>
! K- T& ~& y* L% t. W<p>&nbsp;文件系统:</p>- I2 _( i7 C) r, U
<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>( g9 K9 W. B& v9 F" H5 |, T
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211170746939-2004306213.png" ></p>
) G( U3 X: t& Y6 G. H<p>&nbsp;Znode节点主要有4中类型:</p>
, |: J+ }: L- R! m- L7 p' J<ul>
  E$ n9 ?2 Z/ N& w+ [! K<li><span style="color: rgba(51, 204, 204, 1)">临时目录节点</span>:客户端与Zookeeper断开连接后,该节点被删除</li>
4 M1 }! X; |, N4 f<li><span style="color: rgba(51, 204, 204, 1)">临时顺序编号目录节点</span>:基本特性同临时节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
8 N3 w" n1 ]" N1 c<li><span style="color: rgba(51, 204, 204, 1)">持久化目录节点</span>:客户端与Zookeeper断开连接后,该节点依旧存在</li>/ ]( Z# P# w# r( h
<li><span style="color: rgba(51, 204, 204, 1)">持久化顺序编号目录节点</span>:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。</li>
. V3 M7 ?& K5 N3 q+ K5 y1 Z</ul>7 Z. F' I3 }* f
<hr>" k- t6 V0 V  r: S  u
<p>&nbsp;通知机制 (监听机制)</p>
: j1 T$ D" R9 @<p>Zookeeper可以提供分布式数据的<span style="color: rgba(51, 204, 204, 1)">发布/订阅</span>功能,依赖的就是Wather监听机制。</p>
6 ?, S$ \1 l5 x+ 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># H# n( W3 s8 i  \* Z. U: A: G; U
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211172333942-1239203073.png" ></p>2 ~% L, z' x1 n9 y% P4 ]; `( \
<ol>
% ^3 L, O3 O/ Z& T" W- }3 a<li>客户端向服务端注册Wather监听</li>/ L6 ~: r3 B. e0 i
<li>保存Wather对象到客户端本地的WatherManager中</li>! m) s6 x8 }. A/ t
<li>服务端Wather事件触发后,客户端收到服务端通知,从WatherManager(watcher管理器)中取出对应Wather对象执行回调逻辑</li>( `; `- @$ L6 H* {2 x
</ol>9 k% I" q3 a3 W# O3 p( V
<p>&nbsp;主要监听2方面内容:</p>
$ h( P1 J, e* k( z% S<ul class="list-paddingleft-2">- x) L6 B6 B/ v  h" l
<li>4 h! d+ E. n! o8 }* Y, v( Z" C
<p>监听Znode节点的<span style="color: rgba(51, 204, 204, 1)">数据变化:<span style="color: rgba(0, 0, 0, 1)">就是那个节点信息更新了。</span></span></p>
4 n! i8 `: L! y) g  S4 ]4 B: J</li>+ _5 ?" e+ N7 w6 F) g( S  g: U
<li>9 D+ }8 C7 O+ K4 }
<p>监听子节点的<span style="color: rgba(51, 204, 204, 1)">增减变化<span style="color: rgba(0, 0, 0, 1)">:就是增加了一个Znode或者删除了一个Znode。</span></span></p>, M- M$ P8 M7 @) g
</li>
/ l( {" N# i8 _7 c" i</ul>
: v& {$ M+ V" M3 G" ~<p><span style="color: rgba(0, 0, 0, 1)">几个特性:</span></p>3 J. U- i3 b( I) |+ }1 h) s" a
<ul>
2 U  o- m2 L. w9 L" ^<li>一次性:一旦一个Wather触发之后,Zookeeper就会将它从存储中移除</li>
1 i7 m8 A& ^0 C7 ~* B<li>客户端串行:客户端的Wather回调处理是串行同步的过程,不要因为一个Wather的逻辑阻塞整个客户端</li>5 j4 z0 o) K2 R* d
<li>轻量:Wather通知的单位是WathedEvent,只<span style="color: rgba(51, 204, 204, 1)">包含通知状态、事件类型和节点路径,不包含具体的事件内容</span>,具体的时间内容需要客户端主动去重新获取数据</li>
8 r; F0 ]6 f7 l4 ^8 Z2 w</ul>
) N6 D6 R) L0 v/ s<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(相关概念)</span></strong></blockquote>6 F1 \6 P& R) A2 C7 I) O
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211182203890-1695256509.png" ></p>7 F' J; W6 T0 a# o* X' j: b1 O  s
<ul>
. j% _( `$ `; Q0 Y! i3 x8 A, H<li>Leader:负责写数据。(写数据都有事务)</li>
9 w- t  D7 n% _/ F( U<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>' _1 b+ L3 v/ Q9 v9 g
<li><span style="color: rgba(51, 204, 204, 1)"><span style="color: rgba(0, 0, 0, 1)">Observer:只负责读。</span></span></li>2 H# p: ?/ ?* j! v: |

# D- e1 O# E  d, B4 m</ul>1 n2 ?1 ]  Q' R# z% {1 l, F) K; n
<hr>
  x9 E7 U7 S0 T, d<p>从上面的角色种,我们可以总结ZK节点的工作状态(服务状态)</p>
1 X: ~% ~. K9 Z" ?3 }& J* V  V3 c( @  R<ul>2 y; G% Q) c0 c# \. T( {/ U: f* b
<li>LOOKING:寻 找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。</li>
0 _% y+ ]+ ]6 N3 o<li>FOLLOWING:跟随者状态。表明当前服务器角色是 Follower。</li>) v; O' [+ z1 S$ A5 T
<li>LEADING:领导者状态。表明当前服务器角色是 Leader。</li>
, t/ W2 X. @+ b9 Q' B<li>OBSERVING:观察者状态。表明当前服务器角色是 Observer。</li>. _& R+ a0 r5 \$ u0 d2 b1 p  h
' X5 s4 a5 `7 e+ q7 C; q
</ul>: e) F; U7 w$ U
<hr>" }! K7 i3 v& _
<p>其他概念:</p>
7 t9 y. S' q. M) M0 v<ul>  L/ s: Z# H8 @# @0 z: U$ d
<li>zxid:<span style="color: rgba(51, 204, 204, 1)">全局事务ID</span>,分为两部分:
0 i! y: Y) i, }/ n7 v( E9 _<ul>9 q3 E- X/ b8 p7 n; _* y
<li>纪元(epoch)部分:epoch代表当前集群所属的哪个leader,leader的选举就类似一个朝代的更替,你前朝的剑不能斩本朝的官,用epoch代表当前命令的有效性。</li>
$ ^5 J3 |/ o* ]$ W+ ~<li>计数器(counter)部分,是一个<span style="color: rgba(51, 204, 204, 1)">全局有序</span>的数字,是一个递增的数字。</li>
6 T: [+ k- I# q8 L) x
% t6 K' U- {" d; V$ ^! C3 p& P
8 l0 _: @& c! W; M/ _/ B</ul>
* p' i$ |4 m7 h' u- y: Y+ T2 m+ H* ?4 k/ O0 t4 o/ R. w) e1 I

' E: c! P; j( T6 a</li>
- m2 V' ~* W- P3 J8 ^6 a( b
, [# a* r# f' V1 R$ i9 N
% O: \( c9 H8 F0 t, l</ul>
3 m. I; ~) Y+ U' _1 u<hr>- k; M# K- `' G7 ^. p
<p>写数据原理:</p># k" t6 K2 |& {
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214106019-937037786.png" ></p>
6 u9 M& [' N' j. Y" S/ H<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211214136079-1875911582.png" ></p>
. @$ y' Q2 v4 ^* Y<ul>
! `3 Z4 |1 v  |5 R( {" O) Q8 O- _<li>写给leader,leader再通知其他节点 </li>8 n8 h" P0 D5 p, L
<li>写给follower,follower没有写的权限,交给leader写,leader再通知。 </li>5 ?. c, ]; C# G- D5 J
<li><span style="color: rgba(51, 204, 204, 1)">半数机制</span>:比如上图,zookeeper在通知其他节点写的时候,达到半数就通知客户端写完成。 不需要全部写完成。所以集群的数量一般是奇数。</li>  j7 c, o3 L8 {" x# Q% O

7 q+ _; i& F/ E# f9 M; o, G4 |) ]% Y" c9 X& Z
</ul>
: u  ]: C5 L6 o* I4 }0 X* }<blockquote><strong><span style="color: rgba(0, 0, 0, 1)">三.ZK集群(原理)</span></strong></blockquote>
& \5 f& v" Z+ f; t( B+ z( [<p>&nbsp;上面我们知道集群的基本概念,那么也会引出很多问题:ZK怎么保证数据一致性?Leader宕机了如何进行选举?选举后数据如何同步?</p>
1 @2 N2 _! r4 c7 E<hr>
7 j9 \. F1 E, m<p>&nbsp;ZK怎么保证数据一致性?</p>; z: p; d- y2 J0 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>3 i4 H: }5 R( w* s
<p>&nbsp;一般我们正常是消息广播:</p>% T- U* m9 o* T! E+ y) }+ }
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211205808867-321051219.png" ></p># g! v7 Y: J9 d( W, G
<ul>' k! Y0 d! k& e. m
<li>第一阶段:<span style="color: rgba(51, 204, 204, 1)">广播事务阶段</span>:对应图上的1,2
$ e( R+ _( @+ T# i0 {* H1 {# I<ul>
" o  c5 [# F- u5 V9 i<li>Leader收到请求之后,将它转换为一个proposal提议,并且为每个提议分配一个事务ID:zxid,然后把提议放入到一个FIFO的队列中,按照FIFO的策略发送给所有的Follower。</li>
8 b$ P$ G9 I# V' F' C" ~0 g9 m<li>Follower收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回ACK给Leader</li>0 @! [7 j/ G6 e

+ c* i2 [/ R# e; o0 B" t
$ K3 p  K7 W9 b6 b
6 N' H- V6 h5 K, p; [. {7 x. v& Y8 {* ]" G5 u/ k

: f: Z% n" j- k+ |+ \) n! q* s, u1 I0 ?( ^- |9 p' n0 ~
</ul>
0 z$ F# w  n7 l3 |7 u! y. B
3 I' M% I* k7 y& X' q6 F6 u% T, }' g6 \4 j2 d9 \* k: v
1 \7 ]1 W4 s& I& J1 z
5 l7 k* r1 C0 J. H8 R

6 h# K- r) j5 |: {8 L7 n: R2 v) M, j6 x* |
</li>
) C# v! ?6 }& `2 U) z<li>第二阶段:<span style="color: rgba(51, 204, 204, 1)">广播提交操作</span>:对应图上的3
5 z% K5 P5 W$ c<ul>: C9 B2 \3 n& d/ W! w
<li>Leader在收到超过半数的Follower的ACK之后,即可认为数据写入成功,就会发送commit命令给Follower告诉他们可以提交proposal了。</li>
/ a0 |, D* F( c6 B! h8 M) |
% }; R! R) M  |) b" e# F
7 A. ~5 y) G$ b* S9 t: A8 B; v6 e% h8 r

1 V8 e! [- \0 r. l/ _5 t$ |8 B* a
( o1 E& G, u: A" `' j# i/ s3 f& y1 o
</ul>7 D  \$ U8 w1 F. \0 e

2 u3 }6 d0 K4 M/ e  [* W' d8 r) r: g% O4 ~$ k* v+ f! V8 N2 _0 h
5 V# ~# `4 }' q) d! `+ p) m
. g8 \0 k- O& C( c& Y2 j

% g) e2 @8 _( J1 o
) X) G) y0 j* V* ~( N+ z</li>
! H0 ~3 p: o, `% ]) E* L
" X% C0 o3 L! e8 z8 C
* F7 a9 }; h& o2 {9 O/ g; s% ^8 p

+ B9 z- _* H7 K  P9 P! p0 e0 f: _; |

. p  g1 G" {1 W# i, F5 \6 d0 \</ul>* ~8 u6 l3 |. U9 A6 \, B
<hr>
' I: E" _: s7 {<p>Leader宕机了如何进行选举?</p>+ \# T% V5 ~/ x
<p>这就得使用ZAB的第二种模式,崩溃恢复模式:</p>6 R  i5 f+ d8 K; Y$ g4 k, J
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211246367-43062481.png" ></p>. v+ O! W) {- T! h$ g; J
<p><img src="https://img2022.cnblogs.com/blog/2597186/202202/2597186-20220211211725764-329743928.png" ></p>
2 n! E; V  H+ v* @  U" ]<hr>9 r. [+ |- g. o  K" F* @' L
<p>选举后数据如何同步?</p>
$ ]  r& H) `8 A6 U3 }) z' Z% Z<p data-tool="mdnice编辑器">那实际上Zookeeper在选举之后,Follower和Observer(统称为Learner)就会去向Leader注册,然后就会开始数据同步的过程。</p>$ i4 G) G0 |  f9 l0 c- O* K: V
<p data-tool="mdnice编辑器">数据同步包含3个主要值和4种形式。</p>
6 o: S" n3 R0 C& c: c  }1 N<ul>  L: D& |+ h" J6 S6 s& `, P2 R
<li data-tool="mdnice编辑器">PeerLastZxid:Learner服务器最后处理的ZXID</li>: G& K: F$ I' |9 X7 s
<li data-tool="mdnice编辑器">minCommittedLog:Leader提议缓存队列中最小ZXID</li>
0 o/ l! T3 |8 a! T4 u7 j<li data-tool="mdnice编辑器">maxCommittedLog:Leader提议缓存队列中最大ZXID</li>4 b0 W( B4 q& n+ K. T, f/ a
0 }" I5 G4 A. h' Z

5 G+ w" x) j, A: o5 w  ]8 L: d% D$ `7 u3 |

) u. ?/ N3 s9 h
3 j& U/ H" X7 R5 A  t7 c# N$ M5 e+ o  z: i8 f
</ul>
9 Y+ V. s$ Z% g6 w! Q3 n3 q5 P, |7 N% f& m<p>同步策略:</p>$ Y: G9 c7 x: L
<ul>0 c: z* S# e; s( C) ]: k
<li><span style="color: rgba(51, 204, 204, 1)">直接差异化同步</span> (DIFF同步):如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,那么则说明Learner服务器还没有完全同步最新的数据。<ol>! s. f. l* y: f" B6 ?" S7 \
<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>
, q7 m) @# ]$ g" 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">发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步</li>2 E. S4 d' s) d8 u* 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">接着等待集群中过半的Learner响应了ACK之后,就发送一个UPTODATE命令,Learner返回ACK,同步流程结束</li>
2 G% L8 G4 t* }' \" h+ N+ M  Z0 M3 q1 V1 H; [5 Q
1 F( c2 L+ x3 x, V' Z3 J  C+ i- }
1 _9 P' }; Z; Z. Z8 g
: [, @; J5 S5 C( u
</ol></li>
& a; d( g# T8 @" z! e<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>
6 z& _& Q/ T. k7 y/ A<ul>/ p6 e/ _2 ?- ^0 U' f
<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 p% c, m5 ~& O7 u, w8 w( i<li style="text-align: justify">. U  H* O6 G; v8 j9 x! O3 h
<p data-tool="mdnice编辑器">A要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于PeerLastZxid的事务,对于A来说,也就是PeerLastZxid=3。流程和DIFF一致,只是会先发送一个TRUNC命令,然后再执行差异化DIFF同步。</p>
( n, c' j. r1 i; ]/ w3 r% l) E" R, V) n. T" c, z, _6 s

2 C+ ^  {% w+ C6 w# A. o+ {
- A$ I3 B8 G% _) I  J- c8 i
8 I) G/ s2 O# t8 t2 L</li>
+ q1 g, W# R% O
& J% Q: }" ]$ h. L$ x$ |$ R+ V, H! V2 x
9 a3 ^) g! C# X+ F& b6 e( E

  F4 a+ F7 x/ G7 g</ul>/ [3 {6 R) n3 a$ m+ q0 B

+ w- }0 Z2 y1 Q9 m2 q* \. B
4 U& d$ v6 B* i- V  ^1 [' k# O. r$ @' I$ Y2 \
( c5 e- x. J3 `; B7 \' o
</li>$ a* w. F% s8 T* i$ d0 M/ p
<li><span style="color: rgba(51, 204, 204, 1)">仅回滚同步</span>(TRUNC同步):9 b7 F1 r: X* ~) ]$ ^
<ul>4 V+ V7 m' Z; H; r) M% K
<li data-tool="mdnice编辑器">针对PeerLastZxid大于maxCommittedLog的场景,流程和上述一致,事务将会被回滚到maxCommittedLog的记录。</li>
3 u4 |! N, v7 J" R( o<li data-tool="mdnice编辑器">这个其实就更简单了,也就是你可以认为TRUNC+DIFF中的例子,新的Leader B没有处理提议,所以B中minCommittedLog=1,maxCommittedLog=3。</li>% ]) f" V; a* z% N, O% ?& h: ?
<li data-tool="mdnice编辑器">所以A的PeerLastZxid=4就会大于maxCommittedLog了,也就是A只需要回滚就行了,不需要执行差异化同步DIFF了。</li>+ H1 z2 e+ i$ h, M# \. F  G' \" v
* d2 U* s9 n: v6 k2 S' R& u

9 p$ g; J2 M) i4 X3 p2 b, l5 U* m( n+ d9 W; C2 j( z% j
6 h* R& [# e- F% T$ G
</ul>
% l8 N4 W6 n. F: C1 i1 U' A6 C% _: t; s4 a

" Y" i' Z+ |# D
3 z* o5 q# t# b
9 l7 I& n0 }! F1 O$ F7 q" Z</li>
; b" O& G: P# a$ i( M% B<li><span style="color: rgba(51, 204, 204, 1)">全量同步</span> (SNAP同步):9 C. |/ f* L0 E
<ul>8 p4 Z8 Z+ e6 H
<li>
) Y( x& W  y' L: m# L# M7 N<p data-tool="mdnice编辑器">适用于两个场景:</p>0 }8 x& N8 f- T# y9 q9 i4 k6 |
<ol class="list-paddingleft-2" data-tool="mdnice编辑器">
3 V- ^5 l4 N6 }. x+ z2 B( w<li>PeerLastZxid小于minCommittedLog</li>7 I4 _" l9 H, @9 N; U
<li>Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID</li>
, u  v' W% ?' x/ l' v% F4 t% H: u( e3 O, }# X0 @' U/ s: m( W
" @" l* {1 s) c$ E7 B

) p3 _( \* Q" O, z+ g  J: k; c2 g5 s0 H/ r' @" W
</ol></li>
; ]  S, f+ ^3 X  B<li>这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。</li>
1 [. }* r6 j& Y6 |; [4 g8 q9 k8 E: b, B7 n
$ G4 ?9 m- N* r# Z/ x
0 V. o. m; j6 Z8 b2 d5 q) U. T9 C

4 j+ Z. m8 Z2 G! \+ X$ v  K% D</ul>+ v2 ~! p! Q0 b3 G  _/ \

  N' o; F: c' J: R- q& x1 k7 l) ~, G& |4 e1 Y6 }, M9 g9 K* g

3 q' H9 l/ F7 t9 x. I6 m; G6 ~
$ ^5 o( ~" J5 I3 S</li>
! n' e& Q! Z9 F4 Y8 ~" g6 z$ L% q+ y+ I- |$ e3 J" I: Z( J
, ~* w  p' r8 \
7 V, n7 u$ J* o. F; n" n. @
& F5 n; C; D! w( i, h$ K
</ul>- H. w4 b( r. a. _
<hr>
1 k. ]# O9 U1 R. _3 {, v<p data-tool="mdnice编辑器">有可能会出现数据不一致的问题吗?</p>
4 l: J1 u+ {! T. `+ _, S<p data-tool="mdnice编辑器">还是会存在的,我们可以分成3个场景来描述这个问题。</p>
( K- N2 D4 ]9 f) N2 O) y<ul>
  L6 h! J6 I! t5 P3 R$ |<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">查询不一致</span><strong><strong>:</strong></strong>
/ j# [  K. N/ j<ul>
1 h4 \5 L9 x! d* ], B7 B8 y: z<li data-tool="mdnice编辑器">因为Zookeeper是过半成功即代表成功,假设我们有5个节点,如果123节点写入成功,如果这时候请求访问到4或者5节点,那么有可能读取不到数据,因为可能数据还没有同步到4、5节点中,也可以认为这算是数据不一致的问题。</li>$ U  M8 [5 H( ]( F" d  i6 ]1 e
<li data-tool="mdnice编辑器">解决方案可以在读取前使用sync命令。</li>
3 I* x+ s, B0 P
, S) X. `. X# x- D
' ?2 @/ e& l+ F</ul>, B5 M2 I' H: h8 C* K4 s3 m
- p2 O$ m3 X) P* F8 j+ r# P

; `1 t% z) |) J</li>
: o9 y, u2 r% f" \2 O/ I<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader未发送proposal宕机</span><strong>:</strong>
8 A6 K; w/ H, Y  B3 ?) C  {<ul>1 l# ]: Q/ B! ]; Z! Y& P7 S, L# H$ U
<li data-tool="mdnice编辑器">
$ L1 c! l9 d" U- I" j# w3 h: @<p data-tool="mdnice编辑器">这也就是数据同步说过的问题。leader刚生成一个proposal,还没有来得及发送出去,此时leader宕机,重新选举之后作为follower,但是新的leader没有这个proposal。</p>+ L0 q% A+ w: S( C
: s" n* ^  g- `4 A" w& d& r6 [8 |
% {4 b! x) X0 x, W+ {
</li>
' N2 S+ N3 _6 j<li data-tool="mdnice编辑器">8 r* H7 ]& ]& K4 k% N+ C1 a
<p data-tool="mdnice编辑器">这种场景下的日志将会被丢弃。</p>( F4 i0 u# V$ c6 r+ e! o0 [, @. \

9 {; k/ q; |- d, x7 D' L2 q) h+ T5 u3 ]( \% G: L9 K: L* Z
</li>
, V9 l+ g3 i( ?( ~$ V: z9 G; t* \: Y/ O# L" m: @, J9 |: n

1 p5 k1 r+ s; p; C( `</ul>
3 d/ ?3 _; m/ u" [0 \& ?! [7 X5 `5 R' F6 Z7 V$ l: L( |6 O
5 ^5 n1 d" S+ @  n$ t
</li>
5 Y$ K" [" ~8 i. j<li data-tool="mdnice编辑器"><span style="color: rgba(51, 204, 204, 1)">leader发送proposal成功,发送commit前宕机</span><strong>:</strong>
; X. W2 p- M' @* [<ul>. ~4 s/ B! ]5 v0 [7 K6 U( ~1 e7 n6 C
<li data-tool="mdnice编辑器">如果发送proposal成功了,但是在将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,因此,这个日志并不会被丢弃,会在选举出leader之后重新同步到其他节点当中。<strong><br></strong></li>- ~. e: [, m+ ~8 e5 Z5 v

& u8 w: ]0 Y; x& Z+ w: z6 r# y/ O6 T3 D% Q; @* Y  G
</ul>6 ?, ]* @- T) V4 W; [

9 {1 J/ n" I6 {( ?2 P* J7 M3 n
5 y. m9 h7 r/ k2 Y9 k</li>
0 x1 s7 s9 H( n
" e6 s* p0 Y. S) |( q
& f/ ^. u9 X8 c' @: v# y  Z</ul>$ Z9 X( ^9 g# u9 e
<blockquote><span style="color: rgba(0, 0, 0, 1)"><strong>四.ZK其他小问题</strong></span></blockquote>
, {1 s3 D* j  d9 l5 [6 k<p>zookeeper 是如何保证事务的顺序一致性的?</p>' y7 d9 |5 c$ m! V  h
<ul>- S/ k: e, e5 t0 I( \
<li>使用<span style="color: rgba(51, 204, 204, 1)">zxid</span>来保证顺序性。</li>6 z% N7 ~; N" x; q; @
; n4 ]+ K1 F% V6 m# _: g: N

. g  v' r" X7 _3 r8 ?9 O7 ~</ul>
( H' ?% G; |; g- V<hr>7 e2 t' W4 g! x, m  k$ _9 ^' R* K4 c! {
<p>集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?</p>
, j, \/ r4 z! v, n<ul>
" ?1 Z$ [7 n; P# E3 R+ J  ?<li>集群规则为 <span style="color: rgba(51, 204, 204, 1)">2N+1</span>&nbsp;(奇数)台,N&gt;0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。</li>1 F, F$ F' }" d  [: C8 }
9 K8 k; g- v" R( M2 j

+ S( l, j  @. W1 v" X</ul>
% B2 }" |  ?" f8 ^7 R1 p9 T/ N<hr>
, m  Q, z3 ]2 {* o, f0 O5 S! v<p>说几个 zookeeper 常用的命令:</p>! R$ M7 L( z- o% R1 J* D+ Z1 K
<ul>
1 ^+ s0 `' s" P- u5 O  B<li>ls path:查看当前 znode 的子节点</li>
4 p0 Z6 q/ n6 p' _$ u8 I/ q3 e<li>get path:获取节点的值</li>
" M9 |, R( m7 i# X7 @5 {4 \<li>set:设置节点的值</li>+ q7 E  h6 \/ R3 T' T7 W8 }* e2 V
<li> create,delete:创建/删除节点</li>) G" A- R  }* X2 o
! C* T0 ^1 X4 W# g
3 }! }- Y7 |/ m0 @
</ul>* g& n$ y1 z# v3 S7 Z. ]
<hr>2 p$ Y6 }: L( q* C- M' E
<p>会话Session:</p>: K! j+ X; P& _2 G0 ]8 E% z
<ul>( m$ Z# \! B$ G
<li>会话自然就是指Zookeeper客户端和服务端之间的通信,他们使用TCP长连接的方式保持通信,通常,肯定会有<span style="color: rgba(51, 204, 204, 1)">心跳检测</span>的机制,同时他可以接受来自服务器的Watch事件通知。</li>- M$ G1 A/ N) l% e2 W
0 ?, Y3 i; i) N; O, `
$ G$ f1 o3 k# m+ l% ], S# G$ R
</ul>
" g/ q* i  D2 U" h<p>&nbsp;</p>
. X+ C1 S* t5 c2 I, I+ r<p>寄语:<span style="color: rgba(51, 204, 204, 1)">平静的湖面酝酿不出精悍的水手,安逸的环境创造不出时代的伟人</span></p>$ P0 \/ Z5 ]5 D+ e
回复

使用道具 举报

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

本版积分规则

手机版|飞雪团队

GMT+8, 2025-8-23 15:44 , Processed in 0.067054 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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