集羣上有不少個節點運行同一個任務,這個任務會有一些可能常常改變的配置參數,要求是當配置參數改變以後可以很快地同步到每一個節點上,若是將這些配置參數放在本地文件中則每次都要修改本地文件費時費力還可能會有遺漏,因此這個時候一個比較天然的想法就是將配置單獨提取出來做爲一個服務,好比本身開發一個http服務器提供一個接口來獲取服務,這有兩個問題,其一是配置下發這實際上是一個推模型,當配置發生改變時須要服務器去主動推給客戶端而不是客戶端不斷地去輪詢,其二是配置中心不能是單點故障,對配置中心的可用性有必定要求,這時候若是有zookeeper集羣的話直接拿來做爲配置中心使用不失爲一種簡單的方案。html
一個配置中心的核心是什麼:java
1. 低延遲:配置改變後可以儘快的將最新配置同步給每個節點。node
2. 高可用:配置中心須要可以穩定不間斷的提供服務。apache
第一點能夠經過zookeeper的watcher機制實現,約定一個節點用來存放配置信息,每一個客戶端都監聽這個節點的NodeDataChanged事件,當配置發生改變時將最新的配置更新到這個節點上(誰更新無所謂,任意一個節點均可以更新,或者作一個另外的配置管理後臺用來更新都沒有問題),這個節點觸發NodeDataChanged事件,通知全部監聽此節點NodeDataChanged事件的客戶端獲取此節點的最新值,由於watcher是一次性的,因此在獲取最新值的時候須要從新設置監聽事件,由於getData是原子性操做,因此可以保證獲取到的必定是最新的值。這裏須要注意的是存放在節點上的配置文件不宜過大,若是配置文件部分很大而每次變動的只是一部分的話或許能夠考慮對其進行拆分,存放在多個節點上。服務器
第二點的高可用性就是交由zookeeper集羣來保證,在應用層面不須要作額外的工做。併發
下面是分佈式配置管理中心簡單的示意圖:dom
程序運行過程當中不斷的重複123步驟。tcp
ConfigManager.java:分佈式
package cc11001100.zookeeper.configManager; import cc11001100.zookeeper.utils.ZooKeeperUtil; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; import java.io.IOException; /** * 使用zookeeper作分佈式配置管理的例子 * * @author CC11001100 */ public class ConfigManager { private String configNode; private ZooKeeper zooKeeper; public ConfigManager(String configNode) throws IOException { this.configNode = configNode; zooKeeper = ZooKeeperUtil.getZooKeeper(); registerConfigChangeListener(); } private void registerConfigChangeListener() { try { byte[] newConfig = zooKeeper.getData(configNode, event -> registerConfigChangeListener(), null); if (newConfig != null) { handleNewConfig(new String(newConfig, "UTF-8")); } } catch (IOException | InterruptedException | KeeperException e) { e.printStackTrace(); } } protected void handleNewConfig(String newConfig) { System.out.println(Thread.currentThread().getName() + " config changed: " + newConfig); } public void updateConfig(String newConfig) { try { zooKeeper.setData(configNode, newConfig.getBytes(), -1); } catch (InterruptedException | KeeperException e) { e.printStackTrace(); } } }
ConfigManagerTest.java:測試
package cc11001100.zookeeper.configManager; import cc11001100.zookeeper.utils.ZooKeeperUtil; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.ZooKeeper; import java.io.IOException; import java.util.Random; import java.util.concurrent.TimeUnit; /** * 測試更新 * * @author CC11001100 */ public class ConfigManagerTest { private static void sleep(int mils) { try { TimeUnit.MILLISECONDS.sleep(mils); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws IOException, KeeperException, InterruptedException { final String CONFIG_NODE = "/config-node"; ZooKeeper zooKeeper = ZooKeeperUtil.getZooKeeper(); if (zooKeeper.exists(CONFIG_NODE, false) == null) { zooKeeper.create(CONFIG_NODE, "default-config".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } // 模擬有10個客戶端,每一個都有必定概率更新配置 for (int i = 0; i < 10; i++) { new Thread(() -> { try { ConfigManager configManager = new ConfigManager(CONFIG_NODE); Random random = new Random(); sleep(random.nextInt(3000)); while (true) { if (Math.random() < 0.2) { // 節點能夠本身更新配置 String newConfig = Thread.currentThread().getName() + " update, new config " + random.nextLong(); configManager.updateConfig(newConfig); } sleep(3000); } } catch (IOException e) { e.printStackTrace(); } }, "client-" + i).start(); } // 也能夠有專門的配置中心來管理更新配置 Random random = new Random(); while (true) { if (Math.random() < 0.2) { String newConfig = "config master update, new config " + random.nextLong(); zooKeeper.setData(CONFIG_NODE, newConfig.getBytes(), -1); } sleep(1000); } } }
控制檯輸出:
... client-4 config changed: client-5 update, new config -845834592719987179 client-5 config changed: client-5 update, new config -845834592719987179 client-3 config changed: client-5 update, new config -845834592719987179 client-6 config changed: client-5 update, new config -845834592719987179 client-1 config changed: client-5 update, new config -845834592719987179 client-7 config changed: client-5 update, new config -845834592719987179 client-2 config changed: client-5 update, new config -845834592719987179 client-8 config changed: client-5 update, new config -845834592719987179 client-9 config changed: client-5 update, new config -845834592719987179 client-0 config changed: client-5 update, new config -845834592719987179 client-1-EventThread config changed: config master update, new config -1193927892495196391 client-2-EventThread config changed: config master update, new config -1193927892495196391 client-6-EventThread config changed: config master update, new config -1193927892495196391 client-9-EventThread config changed: config master update, new config -1193927892495196391 client-8-EventThread config changed: config master update, new config -1193927892495196391 client-3-EventThread config changed: config master update, new config -1193927892495196391 client-4-EventThread config changed: config master update, new config -1193927892495196391 client-7-EventThread config changed: config master update, new config -1193927892495196391 client-5-EventThread config changed: config master update, new config -1193927892495196391 client-0-EventThread config changed: config master update, new config -1193927892495196391 client-1-EventThread config changed: client-9 update, new config -3172102246096982581 client-6-EventThread config changed: client-9 update, new config -3172102246096982581 client-0-EventThread config changed: client-9 update, new config -3172102246096982581 client-2-EventThread config changed: client-9 update, new config -3172102246096982581 client-4-EventThread config changed: client-9 update, new config -3172102246096982581 client-7-EventThread config changed: client-9 update, new config -3172102246096982581 client-8-EventThread config changed: client-9 update, new config -3172102246096982581 client-9-EventThread config changed: client-9 update, new config -3172102246096982581 client-5-EventThread config changed: client-9 update, new config -3172102246096982581 client-3-EventThread config changed: client-9 update, new config -3172102246096982581 client-8-EventThread config changed: config master update, new config -8802002496608532059 client-2-EventThread config changed: config master update, new config -8802002496608532059 client-4-EventThread config changed: config master update, new config -8802002496608532059 client-1-EventThread config changed: config master update, new config -8802002496608532059 client-6-EventThread config changed: config master update, new config -8802002496608532059 client-7-EventThread config changed: config master update, new config -8802002496608532059 client-0-EventThread config changed: config master update, new config -8802002496608532059 client-5-EventThread config changed: config master update, new config -8802002496608532059 client-9-EventThread config changed: config master update, new config -8802002496608532059 client-3-EventThread config changed: config master update, new config -8802002496608532059 client-4-EventThread config changed: client-2 update, new config -4848584377488801943 client-1-EventThread config changed: client-2 update, new config -4848584377488801943 client-6-EventThread config changed: client-2 update, new config -4848584377488801943 client-2-EventThread config changed: client-2 update, new config -4848584377488801943 client-8-EventThread config changed: client-2 update, new config -4848584377488801943 client-7-EventThread config changed: client-2 update, new config -4848584377488801943 client-5-EventThread config changed: client-2 update, new config -4848584377488801943 client-9-EventThread config changed: client-2 update, new config -4848584377488801943 client-3-EventThread config changed: client-2 update, new config -4848584377488801943 client-0-EventThread config changed: client-2 update, new config -4848584377488801943 client-6-EventThread config changed: client-9 update, new config 6591542797374556406 client-4-EventThread config changed: client-9 update, new config 6591542797374556406 client-3-EventThread config changed: client-9 update, new config 6591542797374556406 client-9-EventThread config changed: client-9 update, new config 6591542797374556406 client-1-EventThread config changed: client-9 update, new config 6591542797374556406 client-2-EventThread config changed: client-9 update, new config 6591542797374556406 client-8-EventThread config changed: client-9 update, new config 6591542797374556406 client-7-EventThread config changed: client-9 update, new config 6591542797374556406 client-0-EventThread config changed: client-9 update, new config 6591542797374556406 client-5-EventThread config changed: client-9 update, new config 6591542797374556406 client-6-EventThread config changed: client-8 update, new config -6225415529835558983 client-4-EventThread config changed: client-8 update, new config -6225415529835558983 client-8-EventThread config changed: client-8 update, new config -6225415529835558983 client-2-EventThread config changed: client-8 update, new config -6225415529835558983 client-1-EventThread config changed: client-8 update, new config -6225415529835558983 client-5-EventThread config changed: client-8 update, new config -6225415529835558983 client-0-EventThread config changed: client-8 update, new config -6225415529835558983 client-7-EventThread config changed: client-8 update, new config -6225415529835558983 client-9-EventThread config changed: client-8 update, new config -6225415529835558983 client-3-EventThread config changed: client-8 update, new config -6225415529835558983 client-1-EventThread config changed: client-4 update, new config -703954571020619435 client-4-EventThread config changed: client-4 update, new config -703954571020619435 client-8-EventThread config changed: client-4 update, new config -703954571020619435 client-6-EventThread config changed: client-4 update, new config -703954571020619435 client-2-EventThread config changed: client-4 update, new config -703954571020619435 client-7-EventThread config changed: client-4 update, new config -703954571020619435 client-0-EventThread config changed: client-4 update, new config -703954571020619435 client-5-EventThread config changed: client-4 update, new config -703954571020619435 client-3-EventThread config changed: client-4 update, new config -703954571020619435 client-9-EventThread config changed: client-4 update, new config -703954571020619435 ...
使用zk做爲配置分發的優勢是低延遲、高可靠性,固然也有缺點,由於watcher是跟會話綁定的,而要維護每一個會話須要一個tcp一直鏈接到服務器,這對集羣來講也是一種負載,不過考慮到2c4g單臺機器支持幾百鏈接併發很輕鬆,再加上整個zookeeper集羣中會有多臺機器平均一下,這點負載基本忽略了。
相關資料:
1. 一篇好TM長的關於配置中心的文章 - 阿里中間件團隊博客(廢話有點多,但很值得一看)
.