拜託,別再問我Zookeeper如何實現分佈式鎖了!

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#ffffff","name":"user"}},{"type":"bgcolor","attrs":{"color":"#FC8F99","name":"red"}}],"text":"導讀"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"真是有人("},{"type":"codeinline","content":[{"type":"text","text":"鎖"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":")的地方就有江湖("},{"type":"codeinline","content":[{"type":"text","text":"事務"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"),今天不談江湖,來撩撩人。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"分佈式鎖的概念、爲什麼使用分佈式鎖,想必大家已經很清楚了。前段時間作者寫過Redis是如何實現分佈式鎖,今天這篇文章來談談Zookeeper是如何實現分佈式鎖的。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"陳某今天分別從如下幾個方面來詳細講講ZK如何實現分佈式鎖:ZK的四種節點排它鎖的實現讀寫鎖的實現Curator實現分步式鎖"}]}]}]},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#ffffff","name":"user"}},{"type":"bgcolor","attrs":{"color":"#FC8F99","name":"red"}}],"text":"ZK的四種節點"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"持久性節點:節點創建後將會一直存在"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"臨時節點:臨時節點的生命週期和當前會話綁定,一旦當前會話斷開臨時節點也會刪除,當然可以主動刪除。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"持久有序節點:節點創建一直存在,並且zk會自動爲節點加上一個自增的後綴作爲新的節點名稱。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"臨時有序節點:保留臨時節點的特性,並且zk會自動爲節點加上一個自增的後綴作爲新的節點名稱。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#ffffff","name":"user"}},{"type":"bgcolor","attrs":{"color":"#FC8F99","name":"red"}}],"text":"排它鎖的實現"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"排他鎖的實現相對簡單一點,利用了zk的創建節點不能重名的特性"},{"type":"text","text":"。如下圖:"}]}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f2/f2df6ff09cf380622883315a338ba689.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"根據上圖分析大致分爲如下步驟:嘗試獲取鎖:創建"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"臨時節點"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":",zk會保證只有一個客戶端創建成功。創建臨時節點成功,獲取鎖成功,執行業務邏輯,業務執行完成後刪除鎖。創建臨時節點失敗,阻塞等待。監聽刪除事件,一旦臨時節點刪除了,表示互斥操作完成了,可以再次嘗試獲取鎖。遞歸:獲取鎖的過程是一個遞歸的操作,"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"獲取鎖->監聽->獲取鎖"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"如何避免死鎖"},{"type":"text","text":":創建的是臨時節點,當服務宕機會話關閉後臨時節點將會被刪除,鎖自動釋放。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"代碼實現"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者參照JDK鎖的實現方式加上模板方法模式的封裝,封裝接口如下:"}]}]}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * @Description ZK分佈式鎖的接口\n * @Author 陳某\n * @Date 2020/4/7 22:52\n */\npublic interface ZKLock {\n /**\n * 獲取鎖\n */\n void lock() throws Exception;\n\n /**\n * 解鎖\n */\n void unlock() throws Exception;\n}\n"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"模板抽象類如下:"}]}]}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * @Description 排他鎖,模板類\n * @Author 陳某\n * @Date 2020/4/7 22:55\n */\npublic abstract class AbstractZKLockMutex implements ZKLock {\n\n /**\n * 節點路徑\n */\n protected String lockPath;\n\n /**\n * zk客戶端\n */\n protected CuratorFramework zkClient;\n\n private AbstractZKLockMutex(){}\n\n public AbstractZKLockMutex(String lockPath,CuratorFramework client){\n this.lockPath=lockPath;\n this.zkClient=client;\n }\n\n /**\n * 模板方法,搭建的獲取鎖的框架,具體邏輯交於子類實現\n * @throws Exception\n */\n @Override\n public final void lock() throws Exception {\n //獲取鎖成功\n if (tryLock()){\n System.out.println(Thread.currentThread().getName()+\"獲取鎖成功\");\n }else{ //獲取鎖失敗\n //阻塞一直等待\n waitLock();\n //遞歸,再次獲取鎖\n lock();\n }\n }\n\n /**\n * 嘗試獲取鎖,子類實現\n */\n protected abstract boolean tryLock() ;\n\n /**\n * 等待獲取鎖,子類實現\n */\n protected abstract void waitLock() throws Exception;\n\n /**\n * 解鎖:刪除節點或者直接斷開連接\n */\n @Override\n public abstract void unlock() throws Exception;\n}\n"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"排他鎖的具體實現類如下:"}]}]}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"/**\n * @Description 排他鎖的實現類,繼承模板類 AbstractZKLockMutex\n * @Author 陳某\n * @Date 2020/4/7 23:23\n */\n@Data\npublic class ZKLockMutex extends AbstractZKLockMutex {\n\n /**\n * 用於實現線程阻塞\n */\n private CountDownLatch countDownLatch;\n\n public ZKLockMutex(String lockPath,CuratorFramework zkClient){\n super(lockPath,zkClient);\n }\n\n /**\n * 嘗試獲取鎖:直接創建一個臨時節點,如果這個節點存在創建失敗拋出異常,表示已經互斥了,\n * 反之創建成功\n * @throws Exception\n */\n @Override\n protected boolean tryLock() {\n try {\n zkClient.create()\n //臨時節點\n .withMode(CreateMode.EPHEMERAL)\n //權限列表 world:anyone:crdwa\n .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)\n .forPath(lockPath,\"lock\".getBytes());\n return true;\n }catch (Exception ex){\n return false;\n }\n }\n\n /**\n * 等待鎖,一直阻塞監聽\n * @return 成功獲取鎖返回true,反之返回false\n */\n @Override\n protected void waitLock() throws Exception {\n //監聽節點的新增、更新、刪除\n final NodeCache nodeCache = new NodeCache(zkClient, lockPath);\n //啓動監聽\n nodeCache.start();\n ListenerContainer listenable = nodeCache.getListenable();\n\n //監聽器\n NodeCacheListener listener=()-> {\n //節點被刪除,此時獲取鎖\n if (nodeCache.getCurrentData() == null) {\n //countDownLatch不爲null,表示節點存在,此時監聽到節點刪除了,因此-1\n if (countDownLatch != null)\n countDownLatch.countDown();\n }\n };\n //添加監聽器\n listenable.addListener(listener);\n\n //判斷節點是否存在\n Stat stat = zkClient.checkExists().forPath(lockPath);\n //節點存在\n if (stat!=null){\n countDownLatch=new CountDownLatch(1);\n //阻塞主線程,監聽\n countDownLatch.await();\n }\n //移除監聽器\n listenable.removeListener(listener);\n }\n\n /**\n * 解鎖,直接刪除節點\n * @throws Exception\n */\n @Override\n public void unlock() throws Exception {\n zkClient.delete().forPath(lockPath);\n }\n}\n"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"可重入性排他鎖如何設計"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"可重入的邏輯很簡單,在本地保存一個"},{"type":"codeinline","content":[{"type":"text","text":"ConcurrentMap"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":","},{"type":"codeinline","content":[{"type":"text","text":"key"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"是當前線程,"},{"type":"codeinline","content":[{"type":"text","text":"value"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"是定義的數據,結構如下:"}]}]}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" private final ConcurrentMap threadData = Maps.newConcurrentMap();\n"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"重入的僞代碼如下:"}]}]}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public boolean tryLock(){\n //判斷當前線程是否在threadData保存過\n //存在,直接return true\n //不存在執行獲取鎖的邏輯\n //獲取成功保存在threadData中\n}\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#ffffff","name":"user"}},{"type":"bgcolor","attrs":{"color":"#FC8F99","name":"red"}}],"text":"讀寫鎖的實現"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"讀寫鎖分爲讀鎖和寫鎖,區別如下:讀鎖允許多個線程同時讀數據,但是在讀的同時不允許寫線程修改。寫鎖在獲取後,不允許多個線程同時寫或者讀。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"如何實現讀寫鎖?ZK中有一類節點叫臨時有序節點,上文有介紹。下面我們來利用臨時有序節點來實現讀寫鎖的功能。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"讀鎖的設計"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"讀鎖允許多個線程同時進行讀,並且在讀的同時不允許線程進行寫操作,實現原理如下圖:"}]}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/24/24e9c3e5f501e29d6682e30b9a69b8d0.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"根據上圖,獲取一個讀鎖分爲以下步驟:創建臨時有序節點(當前線程擁有的"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"讀鎖"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"或稱作"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"讀節點"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":")。獲取路徑下所有的子節點,並進行"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"從小到大"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"排序獲取當前節點前的臨近寫節點(寫鎖)。如果不存在的臨近寫節點,則成功獲取讀鎖。如果存在臨近寫節點,對其監聽刪除事件。一旦監聽到刪除事件,重複2,3,4,5的步驟(遞歸)。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"寫鎖的設計"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"線程一旦獲取了寫鎖,不允許其他線程讀和寫。實現原理如下:"}]}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a9/a95d774f487309309cf0494074765afb.png","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"從上圖可以看出唯一和寫鎖不同的就是監聽的節點,這裏是監聽臨近節點(讀節點或者寫節點),讀鎖只需要監聽寫節點,步驟如下:創建臨時有序節點(當前線程擁有的"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"寫鎖"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"或稱作"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"寫節點"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":")。獲取路徑下的所有子節點,並進行"},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"strong"}],"text":"從小到大"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"排序。獲取當前節點的臨近節點(讀節點和寫節點)。如果不存在臨近節點,則成功獲取鎖。如果存在臨近節點,對其進行監聽刪除事件。一旦監聽到刪除事件,重複2,3,4,5的步驟(遞歸)。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"如何監聽"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"無論是寫鎖還是讀鎖都需要監聽前面的節點,不同的是讀鎖只監聽臨近的寫節點,寫鎖是監聽臨近的所有節點,抽象出來看其實是一種鏈式的監聽,如下圖:"}]}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c1/c130a5ac212cd27922b3bcb7e169731f.png","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"每一個節點都在監聽前面的臨近節點,一旦前面一個節點刪除了,再從新排序後監聽前面的節點,這樣遞歸下去。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"代碼實現"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者簡單的寫了讀寫鎖的實現,先造出來再優化,不建議用在生產環境。代碼如下:"}]}]}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ZKLockRW {\n\n /**\n * 節點路徑\n */\n protected String lockPath;\n\n /**\n * zk客戶端\n */\n protected CuratorFramework zkClient;\n\n /**\n * 用於阻塞線程\n */\n private CountDownLatch countDownLatch=new CountDownLatch(1);\n\n private final static String WRITE_NAME=\"_W_LOCK\";\n\n private final static String READ_NAME=\"_R_LOCK\";\n\n public ZKLockRW(String lockPath, CuratorFramework client) {\n this.lockPath=lockPath;\n this.zkClient=client;\n }\n\n /**\n * 獲取鎖,如果獲取失敗一直阻塞\n * @throws Exception\n */\n public void lock() throws Exception {\n //創建節點\n String node = createNode();\n //阻塞等待獲取鎖\n tryLock(node);\n countDownLatch.await();\n }\n\n /**\n * 創建臨時有序節點\n * @return\n * @throws Exception\n */\n private String createNode() throws Exception {\n //創建臨時有序節點\n return zkClient.create()\n .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)\n .withACL(ZooDefs.Ids.OPEN_ACL_UNSAFE)\n .forPath(lockPath);\n }\n\n /**\n * 獲取寫鎖\n * @return\n */\n public ZKLockRW writeLock(){\n return new ZKLockRW(lockPath+WRITE_NAME,zkClient);\n }\n\n /**\n * 獲取讀鎖\n * @return\n */\n public ZKLockRW readLock(){\n return new ZKLockRW(lockPath+READ_NAME,zkClient);\n }\n\n private void tryLock(String nodePath) throws Exception {\n //獲取所有的子節點\n List childPaths = zkClient.getChildren()\n .forPath(\"/\")\n .stream().sorted().map(o->\"/\"+o).collect(Collectors.toList());\n\n //第一個節點就是當前的鎖,直接獲取鎖。遞歸結束的條件\n if (nodePath.equals(childPaths.get(0))){\n countDownLatch.countDown();\n return;\n }\n\n //1. 讀鎖:監聽最前面的寫鎖,寫鎖釋放了,自然能夠讀了\n if (nodePath.contains(READ_NAME)){\n //查找臨近的寫鎖\n String preNode = getNearWriteNode(childPaths, childPaths.indexOf(nodePath));\n if (preNode==null){\n countDownLatch.countDown();\n return;\n }\n NodeCache nodeCache=new NodeCache(zkClient,preNode);\n nodeCache.start();\n ListenerContainer listenable = nodeCache.getListenable();\n listenable.addListener(() -> {\n //節點刪除事件\n if (nodeCache.getCurrentData()==null){\n //繼續監聽前一個節點\n String nearWriteNode = getNearWriteNode(childPaths, childPaths.indexOf(preNode));\n if (nearWriteNode==null){\n countDownLatch.countDown();\n return;\n }\n tryLock(nearWriteNode);\n }\n });\n }\n\n //如果是寫鎖,前面無論是什麼鎖都不能讀,直接循環監聽上一個節點即可,直到前面無鎖\n if (nodePath.contains(WRITE_NAME)){\n String preNode = childPaths.get(childPaths.indexOf(nodePath) - 1);\n NodeCache nodeCache=new NodeCache(zkClient,preNode);\n nodeCache.start();\n ListenerContainer listenable = nodeCache.getListenable();\n listenable.addListener(() -> {\n //節點刪除事件\n if (nodeCache.getCurrentData()==null){\n //繼續監聽前一個節點\n tryLock(childPaths.get(childPaths.indexOf(preNode) - 1<0?0:childPaths.indexOf(preNode) - 1));\n }\n });\n }\n }\n\n /**\n * 查找臨近的寫節點\n * @param childPath 全部的子節點\n * @param index 右邊界\n * @return\n */\n private String getNearWriteNode(List childPath,Integer index){\n for (int i = 0; i < index; i++) {\n String node = childPath.get(i);\n if (node.contains(WRITE_NAME))\n return node;\n\n }\n return null;\n }\n\n}\n"}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#ffffff","name":"user"}},{"type":"bgcolor","attrs":{"color":"#FC8F99","name":"red"}}],"text":"Curator實現分步式鎖"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Curator是Netflix公司開源的一個Zookeeper客戶端,與Zookeeper提供的原生客戶端相比,Curator的抽象層次更高,簡化了Zookeeper客戶端的開發量。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Curator在分佈式鎖方面已經爲我們封裝好了,大致實現的思路就是按照作者上述的思路實現的。中小型互聯網公司還是建議直接使用框架封裝好的,畢竟穩定,有些大型的互聯公司都是手寫的,牛逼啊。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"創建一個排他鎖很簡單,如下:"}]}]}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"//arg1:CuratorFramework連接對象,arg2:節點路徑\nlock=new InterProcessMutex(client,path);\n//獲取鎖\nlock.acquire();\n//釋放鎖\nlock.release();\n"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"更多的API請參照官方文檔,不是此篇文章重點。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"至此ZK實現分佈式鎖就介紹完了,如有想要源碼的朋友,老規矩,回覆關鍵詞"},{"type":"codeinline","content":[{"type":"text","text":"分佈式鎖"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"獲取。"}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","marks":[{"type":"color","attrs":{"color":"#ffffff","name":"user"}},{"type":"bgcolor","attrs":{"color":"#FC8F99","name":"red"}}],"text":"一點小福利"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"對於Zookeeper不太熟悉的朋友,陳某特地花費兩天時間總結了ZK的常用知識點,包括ZK常用shell命令、ZK權限控制、Curator的基本操作API。目錄如下:"}]}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e0/e033980a42cd68fb818f0a6ae2f9eeaf.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"需要上面PDF文件的朋友,老規矩,回覆關鍵詞"},{"type":"codeinline","content":[{"type":"text","text":"ZK總結"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章