zookeeper是一個分佈式協調服務,爲用戶的分佈式應用提供協調服務。使用範圍有:主從協調、服務器節點動態上下線、統一配置管理、分佈式共享鎖、統一名稱服務……
文章利用zookeeper實現簡單的分佈式鎖
- zookeeper雖然提供的服務很多,但底層只有兩個功能
1.管理程序提交的數據;
2.爲程序提供數據節點監聽服務
分佈式鎖的簡單實現
分佈式系統多個應用需要操作同一個資源,爲資源操作線程安全問題,創建分佈式鎖,應用取得鎖才能操作資源,否則阻塞直到分佈鎖到該應用節點時才能操作資源。
思路:多個應用在zookeeper上註冊,zookeeper採用排序最小的分配鎖,如果是最小就獲取鎖並操作數據,然後刪除該節點。
zookeeper集羣安裝
- zookeeper超過半數以上集羣便可存活,因此適合安裝奇數節個集羣。
- 官網上下載zookeeper-3.x.x.tar.gz包上傳到linux虛擬機中解壓,在zoo.cfg文件中配置各集羣節點的ip和端口。
# zoo.cfg配置
server.1=192.168.10.121:2888:3888
server.2=192.168.10.122:2888:3888
server.3=192.168.10.123:2888:3888
- 依次啓動各羣集節點的zookeeper:
cd /usr/local/zookeeper
./bin zkServer.sh start
啓動zookeeper服務
Java實現
通過連接集羣的Zookeeper對象操作節點,主要方法有create/delete/exists,創建zookeeper時可創建監聽對象及監聽回調方法,當節點狀態改變時則觸發事件(監聽僅一次有效),因此可重新通過get(… true)使監聽方法激活爲true.
public class DistributedClient {
//zookeeper主機
private String hosts = "192.168.10.121:2181,192.168.10.122:2181,192.168.10.123:2181";
//超時時間
private int SESSION_TIME_OUT = 2000;
private String subNode = "/subNode";
private String groupNode = "/group";
private ZooKeeper zk;
private volatile String thisPath; //當前節點
public void connectZookeeper() throws Exception {
zk = new ZooKeeper(hosts, SESSION_TIME_OUT, new Watcher() {
//監聽回調
public void process(WatchedEvent event) {
if (event.getType() == EventType.NodeChildrenChanged
&& groupNode.equals(event.getPath())) {
//獲取子節點,並對父節點進行監聽
try {
List<String> childrenList = zk.getChildren(groupNode, true);
String thisNode = thisPath.substring((groupNode + "/").length());
//比較自己是否是最小的節點
Collections.sort(childrenList);
if (0 == childrenList.indexOf(thisNode)) {
doSomething(); //特定的業務邏輯,用完鎖之後需要刪除
//重新註冊一次鎖, 帶序號的鎖
thisPath = zk.create(groupNode + subNode,
null,
Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
}
} catch (Exception e) {
System.err.println("zookeeper is error");
}
}
}
});
if(null == zk.exists(groupNode, true)) {
zk.create(groupNode, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); //父節點必須爲persistent纔可創建子節點
}
//初始化創建鎖
thisPath = zk.create(groupNode + subNode,
null,
Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
Thread.sleep(1000);
List<String> childrenNodes = zk.getChildren(groupNode, true);
if (1 == childrenNodes.size()) {
doSomething();
thisPath = zk.create(groupNode + subNode,
null,
Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
}
}
/**
* 特寫業務實現
* @throws Exception
* @throws InterruptedException
*/
private void doSomething() throws Exception {
System.out.println("get this lock" + thisPath);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
System.out.println("finish use this lock, will to release: " + thisPath);
zk.delete(this.thisPath, -1); //-1表示不指定版本
}
}
@Test
public void testNode() {
try {
for (int i = 0; i < 5; i++)
{
new Thread() {
public void run() {
try {
DistributedClient dl = new DistributedClient();
dl.connectZookeeper();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
Thread.sleep(Integer.MAX_VALUE);
} catch (Exception e) {
System.err.println("zookeeper connect error");
e.printStackTrace();
}
}
}