Zookeeper介紹
什么是Zookeeper
ZooKeeper 是?種分布式協(xié)調(diào)組件
,用于管理大型主機(jī)。在分布式環(huán)境中協(xié)調(diào)和管理服務(wù)是一個復(fù)雜的過程。ZooKeeper 通過其簡單的架構(gòu)和 API 解決了這個問題。ZooKeeper 允許開發(fā)人員專注于核心應(yīng)用程序邏輯,而不必?fù)?dān)心應(yīng)用程序的分布式特性。
Zookeeper的應(yīng)用場景
- 分布式協(xié)調(diào)組件
在分布式系統(tǒng)中,需要有zookeeper作為分布式協(xié)調(diào)組件,協(xié)調(diào)分布式系統(tǒng)中的狀態(tài)。
- 分布式鎖
zk在實(shí)現(xiàn)分布式鎖上,可以做到強(qiáng)?致性,分布式鎖相關(guān)的知識,在后續(xù)的ZAB協(xié)議中介紹。
- 無狀態(tài)化實(shí)現(xiàn)
zookeeper可以保存登錄信息,保證分布式登錄系統(tǒng)多個系統(tǒng)無需重復(fù)登錄。
搭建Zookeeper服務(wù)器
安裝啟動
zk由Java編寫,安裝依賴Java環(huán)境
-
上傳zookeeper壓縮包
-
解壓縮
tar -zxvf apache-zookeeper-3.7.1-bin.tar.gz
zk目錄結(jié)構(gòu):
- 啟動zookeeper,conf文件夾中需要
zoo.cfg
文件
zoo_sample.cfg配置文件說明:
# zookeeper時間配置中的基本單位 (毫秒)
tickTime=2000
# 允許follower初始化連接到leader最?時?,它表示tickTime時間倍數(shù)即:initLimit*tickTime
initLimit=10
# 允許follower與leader數(shù)據(jù)同步最?時?,它表示tickTime時間倍數(shù)
syncLimit=5
#zookeper 數(shù)據(jù)存儲?錄及?志保存?錄(如果沒有指明dataLogDir,則?志也保存在這個?件中)
dataDir=/tmp/zookeeper
#對客戶端提供的端?號
clientPort=2181
#單個客戶端與zookeeper最?并發(fā)連接數(shù)
maxClientCnxns=60
# 保存的數(shù)據(jù)快照數(shù)量,之外的將會被清除
autopurge.snapRetainCount=3
#?動觸發(fā)清除任務(wù)時間間隔,?時為單位。默認(rèn)為0,表示不?動清除。
autopurge.purgeInterval=1
稍作修改,然后重命名為zoo.cfg
mv zoo_sample.cfg zoo.cfg
Zookeeper服務(wù)器的操作命令
- 啟動zk服務(wù)器(指定配置文件)
./zkServer.sh start ../conf/zoo.cfg
- 重啟zk服務(wù)器
./zkServer.sh restart ../conf/zoo.cfg
- 查看zk服務(wù)器狀態(tài)
./zkServer.sh status ../conf/zoo.cfg
- 停止zk服務(wù)器
./zkServer.sh stop ../conf/zoo.cfg
Zookeeper內(nèi)部的數(shù)據(jù)模型
zk是如何保存數(shù)據(jù)的
zk中的數(shù)據(jù)是保存在節(jié)點(diǎn)上的,節(jié)點(diǎn)就是znode
,多個znode之間構(gòu)成?顆樹的?錄結(jié)構(gòu)
(類似Linux目錄結(jié)構(gòu))。
頂層目錄為/
樹是由節(jié)點(diǎn)所組成,Zookeeper的數(shù)據(jù)存儲也同樣是基于節(jié)點(diǎn),這種節(jié)點(diǎn)叫做 Znode
不同于樹的節(jié)點(diǎn),Znode 的引用方式是路徑引用,類似于?件路徑:
/service/org-service
這樣的層級結(jié)構(gòu),讓每?個 Znode 節(jié)點(diǎn)擁有唯?的路徑,就像命名空間?樣對不同信息作出清晰的隔離。
zk中的znode結(jié)構(gòu)
zk中的znode,包含了四個部分:
- data:保存數(shù)據(jù)
- acl:權(quán)限,定義了什么樣的用戶能夠操作這個節(jié)點(diǎn),且能夠進(jìn)行怎樣的操作。
- c:create 創(chuàng)建權(quán)限,允許在該節(jié)點(diǎn)下創(chuàng)建?節(jié)點(diǎn)
- w:write 更新權(quán)限,允許更新該節(jié)點(diǎn)的數(shù)據(jù)
- r:read 讀取權(quán)限,允許讀取該節(jié)點(diǎn)的內(nèi)容以及?節(jié)點(diǎn)的列表信息
- d:delete 刪除權(quán)限,允許刪除該節(jié)點(diǎn)的?節(jié)點(diǎn)
- a:admin 管理者權(quán)限,允許對該節(jié)點(diǎn)進(jìn)?acl權(quán)限設(shè)置
- stat:描述當(dāng)前znode的元數(shù)據(jù)
- child:當(dāng)前節(jié)點(diǎn)的?節(jié)點(diǎn)
zk中節(jié)點(diǎn)znode類型
創(chuàng)建節(jié)點(diǎn)命令:(不同參數(shù)代表不同類型znode)
-
持久節(jié)點(diǎn)
: 創(chuàng)建出的節(jié)點(diǎn),在會話結(jié)束后依然存在。保存數(shù)據(jù)(默認(rèn)) -
持久順序節(jié)點(diǎn)
: 創(chuàng)建出的節(jié)點(diǎn),根據(jù)先后順序,會在節(jié)點(diǎn)之后帶上?個數(shù)值,越后執(zhí)行數(shù)值越?,適用于分布式鎖的應(yīng)用場景- 單調(diào)遞增(-s) -
臨時節(jié)點(diǎn)
: 臨時節(jié)點(diǎn)是在會話結(jié)束后,自動被刪除的,通過這個特性,zk可以實(shí)現(xiàn)服務(wù)注冊與發(fā)現(xiàn)的效果。(-e)
如何維持心跳?
-
臨時順序節(jié)點(diǎn)
:跟持久序號節(jié)點(diǎn)相同,適?于臨時的分布式鎖。(-es) -
Container節(jié)點(diǎn)
(3.5.3版本新增):Container容器節(jié)點(diǎn),當(dāng)容器中沒有任何子節(jié)點(diǎn),該容器節(jié)點(diǎn)會被zk定期刪除(60s)。(-c) -
TTL節(jié)點(diǎn)
:可以指定節(jié)點(diǎn)的到期時間,到期后被zk定時刪除。只能通過系統(tǒng)配置 zookeeper.extendedTypesEnabled=true 開啟(-t)【不穩(wěn)定,了解即可】
zk的數(shù)據(jù)持久化
zk的數(shù)據(jù)是運(yùn)行在內(nèi)存中,zk提供了兩種持久化機(jī)制:
- 事務(wù)日志
zk把執(zhí)行的命令以日志形式保存在dataLogDir指定的路徑中的文件中
(如果沒有指定 dataLogDir,則按dataDir指定的路徑)。
- 數(shù)據(jù)快照
zk會在?定的時間間隔內(nèi)做?次內(nèi)存數(shù)據(jù)的快照,把該時刻的內(nèi)存數(shù)據(jù)保存在快照?件中
。
【默認(rèn)兩種都是開啟的】
zk通過兩種形式的持久化,在恢復(fù)時先恢復(fù)快照文件中的數(shù)據(jù)到內(nèi)存中,再用日志文件中的數(shù)據(jù)做增量恢復(fù),這樣的恢復(fù)速度更快。
Zookeeper客戶端(zkCli)的使用
啟動客戶端
啟動客戶端:./zkCli.sh -server ip:port(如果連接本地Zookeeper,ip:port可省略)
./zkCli.sh
退出客戶端
quit
常用命令
-
ls / (查看znode目錄結(jié)構(gòu))
-
help (幫助命令)
-
創(chuàng)建Znode(不同參數(shù)對應(yīng)不同類型節(jié)點(diǎn))【不加參數(shù),默認(rèn)持久節(jié)點(diǎn)】
-
存取設(shè)置數(shù)據(jù)
- create /test abc (存)
- get /test (?。?/li>
- set /test abc (設(shè)置數(shù)據(jù))
-
普通查詢
get /test
ls /
ls /test (-R參數(shù) 遞歸查詢)
-
查看節(jié)點(diǎn)詳細(xì)信息
get -s /test (-s參數(shù))
- cZxid: 創(chuàng)建節(jié)點(diǎn)的事務(wù)ID
- mZxid:修改節(jié)點(diǎn)的事務(wù)ID
- pZxid:添加和刪除?節(jié)點(diǎn)的事務(wù)ID
- ctime:節(jié)點(diǎn)創(chuàng)建的時間
- mtime: 節(jié)點(diǎn)最近修改的時間
- dataVersion: 節(jié)點(diǎn)內(nèi)數(shù)據(jù)的版本,每更新?次數(shù)據(jù),版本會+1
- aclVersion: 此節(jié)點(diǎn)的權(quán)限版本
- ephemeralOwner: 如果當(dāng)前節(jié)點(diǎn)是臨時節(jié)點(diǎn),該值是當(dāng)前節(jié)點(diǎn)所有者的session id。如果節(jié)點(diǎn)不是臨時節(jié)點(diǎn),則該值為零。 dataLength: 節(jié)點(diǎn)內(nèi)數(shù)據(jù)的長度
- numChildren: 該節(jié)點(diǎn)的節(jié)點(diǎn)個數(shù)
-
刪除節(jié)點(diǎn)
1、普通刪除
節(jié)點(diǎn)不為空時,直接用delete無法刪除,此時可以用deleteall進(jìn)行刪除
2、樂觀鎖刪除 (-v 參數(shù))
需要與節(jié)點(diǎn)詳細(xì)信息dataVersion對應(yīng),否則無法刪除
-
權(quán)限設(shè)置
- 注冊當(dāng)前會話的賬號和密碼
addauth digest xiaowang:123456
- 創(chuàng)建節(jié)點(diǎn)并設(shè)置權(quán)限
create /test-node abcd auth:xiaowang:123456:cdwra
在另?個會話中必須先使用賬號密碼(步驟一),才能擁有操作該節(jié)點(diǎn)的權(quán)限
Curator客戶端的使用
Curator介紹
Curator是Netflix公司開源的?套zookeeper客戶端框架,Curator是對Zookeeper支持最好的客戶端框架。Curator封裝了?部分Zookeeper的功能,比如Leader選舉、分布式鎖等,減少了技術(shù)人員在使用Zookeeper時的底層細(xì)節(jié)開發(fā)?作。
引入Curator
- 引入依賴
<!--Curator-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<!--Zookeeper-->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.7.14</version>
</dependency>
- 配置文件
curator:
retryCount: 5
elapsedTimeMs: 5000
connectString: 124.222.253.33:2181
sessionTimeoutMs: 60000
connectionTimeoutM: 5000
- 注入配置Bean
@Data
@Component
@ConfigurationProperties(prefix = "curator")
public class WrapperZK {
private int retryCount;
private int elapsedTimeMs;
private String connectString;
private int sessionTimeoutMs;
private int connectionTimeoutMs;
}
- 注?CuratorFramework
@Configuration
public class CuratorConfig {
@Autowired
WrapperZK wrapperZk;
@Bean(initMethod = "start")
public CuratorFramework curatorFramework() {
return CuratorFrameworkFactory.newClient(
wrapperZk.getConnectString(),
wrapperZk.getSessionTimeoutMs(),
wrapperZk.getConnectionTimeoutMs(),
new RetryNTimes(wrapperZk.getRetryCount(), wrapperZk.getElapsedTimeMs()));
}
}
創(chuàng)建節(jié)點(diǎn)
@Autowired
CuratorFramework curatorFramework;
@Test
void createNode() throws Exception {
// 添加持久節(jié)點(diǎn)
String path = curatorFramework.create().forPath("/curator-node");
// 添加臨時序號節(jié)點(diǎn)
String path1 = curatorFramework.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/curator-node", "some-data".getBytes());
System.out.printf("curator create node :%s successfully.%n", path);
System.in.read();
}
獲得節(jié)點(diǎn)數(shù)據(jù)
@Test
public void testGetData() throws Exception {
byte[] bytes = curatorFramework.getData().forPath("/curator-node");
System.out.println(new String(bytes));
}
修改節(jié)點(diǎn)數(shù)據(jù)
@Test
public void testSetData() throws Exception {
curatorFramework.setData().forPath("/curator-node", "changed!".getBytes());
byte[] bytes = curatorFramework.getData().forPath("/curator-node");
System.out.println(new String(bytes));
}
創(chuàng)建節(jié)點(diǎn)同時創(chuàng)建父節(jié)點(diǎn)
@Test
public void testCreateWithParent() throws Exception {
String pathWithParent = "/node-parent/sub-node-1";
String path = curatorFramework.create().creatingParentsIfNeeded().forPath(pathWithParent);
System.out.printf("curator create node :%s successfully.%n", path);
}
刪除節(jié)點(diǎn)
@Test
public void testDelete() throws Exception {
String pathWithParent = "/node-parent";
curatorFramework.delete().guaranteed().deletingChildrenIfNeeded().forPath(pathWithParent);
}
zk實(shí)現(xiàn)分布式鎖
zk中鎖的分類
- 讀鎖:大家都可以讀,要想上讀鎖的前提是,之前的鎖沒有寫鎖 【
讀鎖共享
】 - 寫鎖:只有得到寫鎖的才能寫。要想上寫鎖的前提是,之前沒有任何鎖。 【
寫鎖獨(dú)占
】
zk如何上讀鎖
- 創(chuàng)建?個臨時序號節(jié)點(diǎn),節(jié)點(diǎn)的數(shù)據(jù)是read,表示是讀鎖
- 獲取當(dāng)前zk中序號比自己小的所有節(jié)點(diǎn)
-
判斷最小節(jié)點(diǎn)是否是讀鎖:
- 如果不是讀鎖的話,則上鎖失敗,為最小節(jié)點(diǎn)設(shè)置監(jiān)聽。阻塞等待,zk的watch機(jī)制會當(dāng)最小節(jié)點(diǎn)發(fā)?變化時通知當(dāng)前節(jié)點(diǎn),于是再執(zhí)行第?步的流程
- 如果是讀鎖的話,則上鎖成功
zk如何上寫鎖
- 創(chuàng)建?個臨時序號節(jié)點(diǎn),節(jié)點(diǎn)的數(shù)據(jù)是write,表示是寫鎖
- 獲取zk中所有的子節(jié)點(diǎn)
-
判斷自己是否是最小的節(jié)點(diǎn):
- 如果是,則上寫鎖成功
- 如果不是,說明前面還有鎖,則上鎖失敗,監(jiān)聽最小的節(jié)點(diǎn),如果最小節(jié)點(diǎn)有變化, 則回到第?步。
羊群效應(yīng)
如果用上述的上鎖方式,只要有節(jié)點(diǎn)發(fā)?變化,就會觸發(fā)其他節(jié)點(diǎn)的監(jiān)聽事件,這樣的話對 zk的壓力?常?,——羊群效應(yīng)??梢哉{(diào)整成鏈?zhǔn)奖O(jiān)聽
。解決這個問題。
監(jiān)聽當(dāng)前節(jié)點(diǎn)的上一個節(jié)點(diǎn)即可。
curator實(shí)現(xiàn)讀寫鎖
1)獲取讀鎖
@Autowired
private CuratorFramework client;
@Test
void testGetReadLock() throws Exception {
// 讀寫鎖
InterProcessReadWriteLock interProcessReadWriteLock = new InterProcessReadWriteLock(client, "/lock1");
// 獲取讀鎖對象
InterProcessLock interProcessLock = interProcessReadWriteLock.readLock();
System.out.println("等待獲取讀鎖對象!");
// 獲取鎖
interProcessLock.acquire();
for (int i = 1; i <= 100; i++) {
Thread.sleep(3000);
System.out.println(i);
}
// 釋放鎖
interProcessLock.release();
System.out.println("等待釋放鎖!");
}
2)獲取寫鎖
@Test
void testGetWriteLock() throws Exception {
// 讀寫鎖
InterProcessReadWriteLock interProcessReadWriteLock = new InterProcessReadWriteLock(client, "/lock1");
// 獲取寫鎖對象
InterProcessLock interProcessLock = interProcessReadWriteLock.writeLock();
System.out.println("等待獲取寫鎖對象!");
// 獲取鎖
interProcessLock.acquire();
for (int i = 1; i <= 100; i++) {
Thread.sleep(3000);
System.out.println(i);
}
// 釋放鎖
interProcessLock.release();
System.out.println("等待釋放鎖!");
}
zk的watch機(jī)制
Watch機(jī)制介紹
可以把 Watch 理解成是注冊在特定 Znode 上的觸發(fā)器。當(dāng)這個 Znode 發(fā)?改變,也就是調(diào)用了 create ,delete ,setData 方法的時候,將會觸發(fā) Znode 上注冊的對應(yīng)事件, 請求 Watch 的客戶端會接收到異步通知。
客戶端使用了NIO通信模式監(jiān)聽服務(wù)端的調(diào)用。
zkCli客戶端使用watch
create /test xxx
get -w /test ?次性監(jiān)聽節(jié)點(diǎn)
ls -w /test 監(jiān)聽目錄,創(chuàng)建和刪除子節(jié)點(diǎn)會收到通知。子節(jié)點(diǎn)中新增節(jié)點(diǎn)不會收到通知
ls -R -w /test 對于子節(jié)點(diǎn)中子節(jié)點(diǎn)的變化,但內(nèi)容的變化不會收到通知
get
-w
/test 監(jiān)聽節(jié)點(diǎn) 監(jiān)聽是一次性的(監(jiān)聽當(dāng)前節(jié)點(diǎn)內(nèi)容變化,無法監(jiān)聽子節(jié)點(diǎn)),每次獲取數(shù)據(jù)時都帶-w參數(shù)可以實(shí)現(xiàn)持續(xù)監(jiān)聽節(jié)點(diǎn)ls
/w
/test 監(jiān)聽節(jié)點(diǎn)目錄變化,可以監(jiān)聽子節(jié)點(diǎn)(一級子節(jié)點(diǎn))加上-R
參數(shù)可以遞歸監(jiān)聽所有子節(jié)點(diǎn)
curator客戶端使用watch
@Test
public void addNodeListener() throws Exception {
NodeCache nodeCache = new NodeCache(curatorFramework, "/curator-node");
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
log.info("{} path nodeChanged: ", "/curator-node");
printNodeData();
}
});
nodeCache.start();
System.in.read();
}
public void printNodeData() throws Exception {
byte[] bytes = curatorFramework.getData().forPath("/curator-node");
log.info("data: {}", new String(bytes));
}
Zookeeper集群實(shí)戰(zhàn)
Zookeeper集群角色
zookeeper集群中的節(jié)點(diǎn)有三種角色
- Leader:處理集群的所有事務(wù)請求(可讀可寫),集群中只有?個Leader。
- Follower:只能處理讀請求,參與Leader選舉。
- Observer:只能處理讀請求,提升集群讀的性能,但不能參與Leader選舉。
集群搭建
搭建4個節(jié)點(diǎn),其中?個節(jié)點(diǎn)為Observer
1)創(chuàng)建4個節(jié)點(diǎn)的myid,并設(shè)值
在/usr/local/zookeeper中創(chuàng)建以下四個文件
/usr/local/zookeeper/data/zk1# echo 1 > myid
/usr/local/zookeeper/data/zk2# echo 2 > myid
/usr/local/zookeeper/data/zk3# echo 3 > myid
/usr/local/zookeeper/data/zk4# echo 4 > myid
2)編寫4個zoo.cfg
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# 修改對應(yīng)的zk1 zk2 zk3 zk4
dataDir=/usr/local/zookeeper/data/zk1
# 修改對應(yīng)的端? 2181 2182 2183 2184
clientPort=2181
# 2001為集群通信端?,3001為集群選舉端?,observer表示不參與集群選舉
server.1=124.222.253.33:2001:3001
server.2=124.222.253.33:2002:3002
server.3=124.222.253.33:2003:3003
server.4=124.222.253.33:2004:3004:observer
# 上述配置,啟動zk時,無法監(jiān)聽端口,可以加下面一行配置解決 (監(jiān)聽所有網(wǎng)卡)
quorumListenOnAllIPs=true
3)啟動4臺Zookeeper
./bin/zkServer.sh start ../conf/zoo1.cfg
./bin/zkServer.sh start ../conf/zoo2.cfg
./bin/zkServer.sh start ../conf/zoo3.cfg
./bin/zkServer.sh start ../conf/zoo4.cfg
查看集群狀態(tài)
./bin/zkServer.sh status ../conf/zoo1.cfg
./bin/zkServer.sh status ../conf/zoo2.cfg
./bin/zkServer.sh status ../conf/zoo3.cfg
./bin/zkServer.sh status ../conf/zoo4.cfg
連接Zookeeper集群
./bin/zkCli.sh -server 124.222.253.33:2181,124.222.253.33:2182,124.222.253.33:2183,124.222.253.33:2184
ZAB協(xié)議
什么是ZAB協(xié)議
zookeeper作為非常重要的分布式協(xié)調(diào)組件,需要進(jìn)行集群部署,集群中會以?主多從的形式進(jìn)行部署。zookeeper為了保證數(shù)據(jù)的?致性,使用了ZAB(Zookeeper Atomic Broadcast 原子消息廣播)協(xié)議,這個協(xié)議解決了Zookeeper的崩潰恢復(fù)和主從數(shù)據(jù)同步
的問題。
ZAB協(xié)議定義的四種節(jié)點(diǎn)狀態(tài)
- Looking :選舉狀態(tài)。
- Following :Follower 節(jié)點(diǎn)(從節(jié)點(diǎn))所處的狀態(tài)。
- Leading :Leader 節(jié)點(diǎn)(主節(jié)點(diǎn))所處狀態(tài)。
- Observing:觀察者節(jié)點(diǎn)所處的狀態(tài)。
集群上線時的Leader選舉過程
Zookeeper集群中的節(jié)點(diǎn)在上線時,將會進(jìn)?到Looking狀態(tài),也就是選舉Leader的狀態(tài),具體過程如下:
選票格式
myid | zXid
myid:myid文件配置數(shù)字
zXid:事務(wù)id,記錄節(jié)點(diǎn)數(shù)據(jù)變化次數(shù)(增刪改操作)
崩潰恢復(fù)時的Leader選舉
Leader建立完后,Leader周期性地不斷向Follower發(fā)送心跳(ping命令,沒有內(nèi)容的 socket)。當(dāng)Leader崩潰后,F(xiàn)ollower發(fā)現(xiàn)socket通道已關(guān)閉,于是Follower開始進(jìn)?到 Looking狀態(tài),重新回到上?節(jié)中的Leader選舉過程,Leader選舉期間集群不能對外提供服務(wù)。
主從服務(wù)器之間的數(shù)據(jù)同步
一定是主節(jié)點(diǎn)寫數(shù)據(jù),client連接到從節(jié)點(diǎn)也是發(fā)給主節(jié)點(diǎn)寫數(shù)據(jù)
半數(shù)以上
【相對于集群數(shù)量】(說明集群網(wǎng)絡(luò)是沒問題的)為了提高集群寫數(shù)據(jù)的性能
Zookeeper中的NIO與BIO的應(yīng)用
-
NIO
- 用于被客戶端連接的2181端口,使用的是NIO模式與客戶端建立連接
- 客戶端開啟Watch時,也使用NIO,等待Zookeeper服務(wù)器的回調(diào)
-
BIO
- 集群在選舉時,多個節(jié)點(diǎn)之間的投票通信端口,使用BIO進(jìn)行通信
- 心跳機(jī)制
CAP理論
CAP 定理
2000 年 7月,加州大學(xué)伯克利分校的 Eric Brewer 教授在 ACM PODC 會議上提出 CAP 猜想。2年后,麻省理?學(xué)院的 Seth Gilbert 和 Nancy Lynch 從理論上證明了 CAP。之后, CAP理論正式成為分布式計(jì)算領(lǐng)域的公認(rèn)定理。
CAP 理論為:?個分布式系統(tǒng)最多只能同時滿足一致性(Consistency)、可?性 (Availability)和分區(qū)容錯性(Partition tolerance)這三項(xiàng)中的兩項(xiàng)
。
- 一致性(Consistency)【數(shù)據(jù)一致】
?致性指 “all nodes see the same data at the same time”,即更新操作成功并返回客戶端完成后,所有節(jié)點(diǎn)在同?時間的數(shù)據(jù)完全?致。
- 可用性(Availability)【服務(wù)可用】
可用性指“Reads and writes always succeed”,即服務(wù)?直可用,而且是正常響應(yīng)時間。
- 分區(qū)容錯性(Partition tolerance)【分布式系統(tǒng)必須滿足】
分區(qū)容錯性指“the system continues to operate despite arbitrary message loss or failure of part of the system”,即分布式系統(tǒng)在遇到某節(jié)點(diǎn)或網(wǎng)絡(luò)分區(qū)故障的時候,仍然能夠?qū)ν馓峁M足?致性或可?性的服務(wù)?!?strong>避免單點(diǎn)故障,就要進(jìn)行冗余部署,冗余部署相當(dāng)于是服務(wù)的分區(qū),這樣的分區(qū)就具備了容錯性。
CAP 權(quán)衡
通過 CAP 理論,我們知道?法同時滿足?致性、可用性和分區(qū)容錯性這三個特性,那要舍棄哪個呢?
對于多數(shù)大型互聯(lián)網(wǎng)應(yīng)用的場景,主機(jī)眾多、部署分散,而且現(xiàn)在的集群規(guī)模越來越大,所以節(jié)點(diǎn)故障、網(wǎng)絡(luò)故障是常態(tài),而且要保證服務(wù)可用性達(dá)到 N 個 9,即保證 P 和 A,舍棄 C(退而求其次保證最終?致性)。雖然某些地方會影響客戶體驗(yàn),但沒達(dá)到造成用戶流失的嚴(yán)重程度。
對于涉及到錢財(cái)這樣不能有?絲讓步的場景,C 必須保證。網(wǎng)絡(luò)發(fā)生故障寧可停止服務(wù),這是保證 CA,舍棄 P。還有?種是保證 CP,舍棄 A。例如網(wǎng)絡(luò)故障是只讀不寫。
孰優(yōu)孰略,沒有定論,只能根據(jù)場景定奪,適合的才是最好的!
BASE 理論
eBay 的架構(gòu)師 Dan Pritchett 源于對大規(guī)模分布式系統(tǒng)的實(shí)踐總結(jié),在 ACM 上發(fā)表?章提出 BASE 理論,BASE 理論是對 CAP 理論的延伸,核心思想是即使無法做到強(qiáng)?致性(Strong Consistency,CAP 的?致性就是強(qiáng)?致性),但應(yīng)用可以采用適合的方式達(dá)到最終?致性 (Eventual Consitency)。
-
基本可用(Basically Available)
BA
基本可用指分布式系統(tǒng)在出現(xiàn)故障的時候,允許損失部分可用性,即保證核心可用。
電商大促時,為了應(yīng)對訪問量激增,部分用戶可能會被引導(dǎo)到降級頁面,服務(wù)層也可能只提供降級服務(wù)。這就是損失部分可用性的體現(xiàn)。
-
軟狀態(tài)(Soft State)
S
軟狀態(tài)是指允許系統(tǒng)存在中間狀態(tài),而該中間狀態(tài)不會影響系統(tǒng)整體可?性。分布式存儲中?般?份數(shù)據(jù)至少會有三個副本,允許不同節(jié)點(diǎn)間副本同步的延時就是軟狀態(tài)的體現(xiàn)。mysql replication 的異步復(fù)制也是?種體現(xiàn)。
-
最終一致性(Eventual Consistency)
E
最終?致性是指系統(tǒng)中的所有數(shù)據(jù)副本經(jīng)過?定時間后,最終能夠達(dá)到?致的狀態(tài)。弱?致性和強(qiáng)?致性相反,最終?致性是弱?致性的?種特殊情況。
Zookeeper追求的一致性
Zookeeper在數(shù)據(jù)同步時,追求的并不是強(qiáng)?致性,而是順序?致性(事務(wù)id的單調(diào)遞增)。
zk追求CP原則,但也不是強(qiáng)一致性(集群半數(shù)以上ACK),而是順序一致性。文章來源:http://www.zghlxwxcb.cn/news/detail-637354.html
Curator客戶端相關(guān)代碼:https://github.com/lkl778/zk.git文章來源地址http://www.zghlxwxcb.cn/news/detail-637354.html
到了這里,關(guān)于分布式協(xié)調(diào)組件Zookeeper的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!