1. 程式人生 > >利用Zookeeper實現分布式鎖及服務註冊中心

利用Zookeeper實現分布式鎖及服務註冊中心

建立連接 oid 等待 某個文件 改名 解壓 als 中文字符 blog

原文:利用Zookeeper實現分布式鎖及服務註冊中心

對於Zookeeper的定義以及原理,網上已經有很多的優秀文章對其進行了詳細的介紹,所以本文不再進行這方面的闡述。

本文主要介紹一些基本的準備工作以及zookeeper.net的使用。

本文源代碼github地址:https://github.com/Mike-Zrw/ZookeeperHelper

zookeeper下載地址:https://archive.apache.org/dist/zookeeper/

ZooInspector下載地址:https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip

Zookeeper下載及安裝

目前版本最新的為3.5.3 。 本文使用的版本為3.4.10

隨意下載一個版本的壓縮包,解壓到某個文件夾中. zookeeper服務的啟動腳本在bin目錄下的文件:zkServer.cmd

在服務啟動之前,需要對配置文件進行基本的設置:

conf目錄下的zoo_sample.cfg改名為 zoo.cf

修改裏面的日誌文件路徑,我修改完之後的文件內容如下

# 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
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=data
dataLogDir=data\log
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1

  • tickTime:Zookeeper之間維持心跳的時間間隔
  • initLimit:集群中的follower服務器(F)與leader服務器(L)之間 初始連接 時能容忍的最多心跳數(tickTime的數量),這裏為2000*10 ,超過10次心跳仍然沒同步完成,會重新選舉leader
  • syncLimit:集群中的follower服務器(F)與leader服務器(L)之間 請求和應答 之間能容忍的最多心跳數(tickTime的數量)。超時follwer將被丟棄
  • dataDir:Zookeeper保存數據的目錄 ,目錄不可含中文字符
  • dataLogDir:Zookeeper保存日誌文件的目錄
  • clientPort:Zookeeper服務器的端口

配置完成之後就可以雙擊zkServer.cmd啟動zookeeper服務了
技術分享圖片

ZooInspector的安裝

ZooInspector是zookeeper的監視工具,可以查看zookeeper的數據信息

下載完成後直接解壓,運行zookeeper-dev-ZooInspector.jar。如果默認端口沒有修改,直接點擊連接就可以了

技術分享圖片

ZooKeeper.Net

通過Nuget來安裝ZooKeeper.Net的開發包到項目中
技術分享圖片

下面的代碼會建立一個zookeeper的連接,並創建一個名為parent的臨時目錄

   public static void TestConnect()
    {
        Console.WriteLine("建立連接");
        //服務地址為:localhost:2181  超時連接30秒
        using (ZooKeeper Instance = new ZooKeeperNet.ZooKeeper("localhost:2181", new TimeSpan(0, 0, 30), new Watcher("new")))
        {
            Console.WriteLine("檢測是否有parent目錄");
            var sdata = Instance.Exists("/parent", new Watcher("exists"));
            Console.WriteLine(sdata==null?"否":"是");
            if (sdata==null)
            {
                Console.WriteLine("開始創建parent目錄");
                //data:目錄關聯的data為:this is the parentnode data
                //acl:目錄的訪問權限控制
                //CreateMode:Ephemeral:目錄為臨時目錄,斷開連接目錄會自動清除  永久目錄:Persistent  自增目錄:***Sequential
                Instance.Create("/parent", "this is the parentnode data".GetBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.Ephemeral);
                Console.WriteLine("創建parent目錄完成");
                Console.WriteLine("檢測是否有parent目錄");
                sdata = Instance.Exists("/parent", new Watcher("exists2"));
                Console.WriteLine(sdata == null ? "否" : "是");
                if (sdata != null)
                {
                    Console.WriteLine("刪除parent目錄");
                    Instance.Delete("/parent", 0);
                }
            }
        }
    }

運行結果如下

技術分享圖片

需要註意的是執行這段代碼可能會報錯提示connection loss,出現錯誤的原因是建立連接的這一步是異步的操作,應當等待zookeeper連接成功之後再執行下面的代碼。所以這段代碼只是進行一個簡單的演示,nuget上附有正確的使用方式

關於zookeeper的watch定義如下:

監視是一種簡單的機制,使客戶端收到關於ZooKeeper集合中的更改的通知。客戶端可以在讀取特定znode時設置Watches。Watches會向註冊的客戶端發送任何znode(客戶端註冊表)更改的通知。

Znode更改是與znode相關的數據的修改或znode的子項中的更改。只觸發一次watches。如果客戶端想要再次通知,則必須通過另一個讀取操作來完成。當連接會話過期時,客戶端將與服務器斷開連接,相關的watches也將被刪除。

Zookeeper的每個節點稱之為znode,znode的類型分為四種:持久節點,臨時節點,持久順序節點,臨時順序節點

這裏簡單說下順序節點:比如我需要建立一個名稱為 /app的節點,那麽zookeeper會建立一個名為/app0000000001的節點,如果再有人要創建一個名為/app的順序節點,新建的節點名稱會變為/app0000000002

也就是說順序節點生成之後的名字是指定的名字加十位序列號,序列號不會重復,即使是兩個操作同時創建。 我們可以利用這一個特性來實現分布式鎖:

分布式鎖的偽代碼如下:

public void lock(){
var parentpath=/lock_
var path = CreateEphemeralSequentialNode(parentpath)
while(true){
var children = 獲取父節點的所有子節點
if(path是children中的最小的){
成功獲取鎖
return;
}else{
監控等待前一個節點刪除
wait();
}
}
}

具體的代碼可以在github上查看

利用Zookeeper實現分布式鎖及服務註冊中心