Zookeeper分散式服務
Zookeeper(CP)
以叢集的方式【leader和follower】為分散式應用提供協調服務、負責儲存和管理大家都關係的資料,接受觀察者註冊、訊息分發等服務
特點:
-
只要有半數以上的節點存活就能保證zookeeper的叢集正常工作
-
每一個Client儲存一份相同的資料副本
-
來自同一個client的跟新請求可以順序執行
-
資料更新原子性,且在一定時間範圍內,client能讀到最新的資料
-
軟負載均衡:可以讓訪問數最少的伺服器去處理最新伺服器請求
啟動命令
#啟動zk伺服器,查詢zk伺服器狀態 ./zkServer.sh status ./zkServer.sh start path/to/zoo.config ./zkServer.sh stop #連線伺服器 ./zkCli.sh
客戶端命令列
create treeNode data #在某個節點下存放資料 create -s treeNode data #有序號的節點,記錄節點建立的先後順序,沒有從零開始 ls TreeNode # 獲取某個樹節點下的所有資料 delete TreeNode #刪除節點,如果下面還有子節點是無法刪除的 rmr Tree #刪除某個子樹 get treeNode #獲取某個節點下的資料 get treeNode watch #註冊對某個節點修改的監聽,只會有效一次 set treeNode data #改變值 get path #獲取某個樹形結構下的值 get -w path #獲取某個樹形結構下的值,並監聽一次變化
helo #幫助手冊
quit#退出
短暫資料:
create -e treeNode data #在某個節點下存放資料,當前客戶端與伺服器斷開則節點消失
節點引數:
czxid:建立該節點的事務ID、mzxid:最後更新的事務ID、cversion:節點的修改次數、dataversion:資料變化號
節點型別【樹形結構】
持久節點,順序【名字遞增】&非順序:persistent、persistent_sequential,除了在記憶體中,在磁碟中也會儲存一份
臨時節點,順序&非順序:ephemeral、ephemeral_sequential 短暫節點在與客戶端斷開連線後,建立的節點自己刪除了。其原理是通過ping去保活,如果斷開,則會發生session-timeOut,順序的臨時節點適合用來充當分散式鎖。相比redis叢集而言,zookeeper叢集具備更好的資料一致性。另外臨時節點不允許建立子節點
容器節點:當沒有子節點時,會被伺服器刪除【預設60s檢查一次】
TTL節點:過了TTL指定節點的時間就會被伺服器刪除,和redis一致
叢集搭建
在.cfg檔案中設定儲存資料的目錄,同時在目錄下touch一個myid,並設定一個唯一標識
配置檔案:預設是/conf/zoo.cfg
server.A1=B:C:D
server.A2=B:C:D
server.A3=B:C:D
#Ai:myid,唯一的數字標識,在資料儲存目錄中myid檔案裡定義
#B:伺服器的IP地址
#C:leader伺服器交換資訊的埠號
#D:傳遞選舉資訊的埠號
當半數啟動後集群開始正常運轉【一般三個或以上】
節點的讀寫
在zookeeper中,某個節點接受到客戶端寫請求,會將請求轉發給leader,由leader廣播寫請求,並統計成功數。當超過半數回覆成功時,才會向客戶端返回成功
CAP:
CAP原則又稱CAP定理,指的是在一個分散式系統中,一致性(Consistency)、可用性(Availability)、分割槽容忍性(Partition tolerance):將資料項複製到多個節點上,當某些節點發生故障,仍然可以訪問到資料的能力
整合spring
連線並設定監聽是否變化,產生變化後再次監聽:不依賴spring,只依賴一個zookeeper.jar包,如果是Spring框架,可以通過配置pean的方式,直接注入 zookeeper
public class ZooKeeperTest {
private int sessionTimeout = 2000;
private String connectString="222.201.144.247:2181";
public ZooKeeper zooKeeper;
@Test
public void init() throws IOException, KeeperException, InterruptedException {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
try {
System.out.println("_________________start___________________");
List<String> list = zooKeeper.getChildren("/",true);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// String s=zooKeeper.create("/connect", "spring".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
zooKeeper.getChildren("/",true);//設定監聽,只會生效一次
Thread.sleep(Long.MAX_VALUE);
}
}
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.10</version>
</dependency>
監聽伺服器上下線:
伺服器叢集向Zookeeper叢集上傳訪問量總量,且資料型別為ephemeral,伺服器掉線則資料消失
客戶端叢集向Zookeeper監聽資料getChildren(),伺服器宕機、上線時做出處理並重設監聽
//客戶機,主機部分向Zookeeper叢集裡的/下寫上帶序號、ephemeral的key 和 hostName即可,而由客戶機負責監聽
public class DistributeClient {
public static void main(String[] args) throws KeeperException, InterruptedException, IOException {
DistributeClient client = new DistributeClient();
//連線到Zookeeper叢集
client.getConnect();
//設定監聽在控制檯列印節點
client.getChildren();
//業務部分
client.business();
}
private int sessionTimeout = 2000;
private String connectString="222.201.144.247:2181";//如果是Zookeeper叢集的話直接在用逗號隔開即可
public ZooKeeper zooKeeper;
private void getConnect() throws IOException {
zooKeeper = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
try {
getChildren();
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
private void getChildren() throws KeeperException, InterruptedException {
List<String> children = zooKeeper.getChildren("/",true);
ArrayList<String> host = new ArrayList<>();
for (String child :children){
byte[] data = zooKeeper.getData("/"+child,false,null);
host.add(new String(data));
}
System.out.println(host);
}
private void business(){
}
}
利用zookeeper實現分散式鎖
方法一:
獲取鎖:create -e /ephemeral/lock threadName 建立失敗則監聽 get -w /ephemeral/lock【此時處於阻塞狀態】,利用Watcher重新獲取鎖。弊端:所有的執行緒都需要反覆的監聽,嘗試獲取鎖,產生系統資源浪費
方法二:【只能實現公平鎖】
請求直接在 create -e -s /ephemeral/lock threadName 建立一個臨時順序節點,並判斷自己是否是最小的節點,如果是,則獲取鎖,若不是,則只監聽前一個節點【只比其小一的節點】並等待。最後通過delete釋放鎖