1.什麼是服務註冊與發現 微服務將傳統的"巨石"應用拆分成一個一個的組件應用,每個組件應用提供特定的服務,可以是一個,也可以是多個,並且組件所含服務應該是可以動態擴展的,隨著時間推移、系統進化,可任意拆分、合併。 組件化應用和顆粒化的服務,遍佈在系統的各個角落,由不同的項目成員進行維護,微服務的核心... ...
1.什麼是服務註冊與發現
微服務將傳統的"巨石"應用拆分成一個一個的組件應用,每個組件應用提供特定的服務,可以是一個,也可以是多個,並且組件所含服務應該是可以動態擴展的,隨著時間推移、系統進化,可任意拆分、合併。
組件化應用和顆粒化的服務,遍佈在系統的各個角落,由不同的項目成員進行維護,微服務的核心是化整為零、各司其職,這就要求開發人員不得操作其業務或服務範圍以外的數據模型等資源,只能通過介面的訪問,使用某一服務。
由於服務的跨度很大(公司很大的情況下)、數量很多(數以百計甚至更多),為保障系統的正常運行,必然需要有一個中心化的組件完成對各個服務的整合,即將分散於各處的服務進行彙總,彙總的信息可以是提供服務的組件名稱、地址、數量等,每個組件擁有一個監聽設備,當本組件內的某個服務的狀態變化時報告至中心化的組件進行狀態的更新。服務的調用方在請求某項服務時首先到中心化組件獲取可提供該項服務的組件信息(IP、埠等),通過預設或自定義的策略選擇該服務的某一提供者進行訪問,實現服務的調用。
隨著分散式系統的發展,出現了越來越多的分散式調度系統,典型的有Zookeeper、Consul、etcd,在分散式系統中需要解決的一個問題即拜占庭將軍問題,參考網站8btc(比特幣咨詢網站)《拜占庭將軍問題深入探討》http://www.8btc.com/baizhantingjiangjun
其中Zookeeper最為成熟,是Yahoo貢獻給Apache基金會的一個頂級開源項目,基於Paxos演算法,參考維基百科條目Paxos (computer science) https://en.wikipedia.org/wiki/Paxos_(computer_science),是Hadoop和HBase的重要組件。
下麵一段是官網對於Zk的介紹。
ZooKeeper is a high-performance coordination service for distributed applications. It exposes common services - such as naming, configuration management, synchronization, and group services - in a simple interface so you don't have to write them from scratch. You can use it off-the-shelf to implement consensus, group management, leader election, and presence protocols. And you can build on it for your own, specific needs.
Zk是為分散式應用設計的一個高性能協調服務,提供瞭如下的通用服務,如命名、配置管理、通過鎖和分組服務,封裝成簡單易用的介面而無需開發人員從頭編寫代碼。可以拿來即用,應用的領域有取得共識、分組管理、領導者選舉和協議呈現。還可以按需自定義功能。
Zk和etcd的比較如下表
語言 | 演算法 | 存儲方式 | 量級 | |
Zookeeper | Java | Paxos | 名稱空間(文件系統) | 重 |
etcd | Go | Raft | K-V存儲 | 輕 |
2.Zookeeper集群配置
2.1 Standalone單機部署
Zookeeper 的配置文件在 conf 目錄下,這個目錄下有 zoo_sample.cfg 和 log4j.properties,你需要做的就是將 zoo_sample.cfg 改名為 zoo.cfg,因為 Zookeeper 在啟動時會找這個文件作為預設配置文件。下麵詳細介紹一下,這個配置文件中各個配置項的意義。
tickTime=2000 dataDir=D:/devtools/zookeeper-3.2.2/build clientPort=2181
tickTime:這個時間是作為 Zookeeper 伺服器之間或客戶端與伺服器之間維持心跳的時間間隔,也就是每個 tickTime 時間就會發送一個心跳。
dataDir:顧名思義就是 Zookeeper 保存數據的目錄,預設情況下,Zookeeper 將寫數據的日誌文件也保存在這個目錄里。
clientPort:這個埠就是客戶端連接 Zookeeper 伺服器的埠,Zookeeper 會監聽這個埠,接受客戶端的訪問請求。
當這些配置項配置好後,你現在就可以啟動 Zookeeper 了,啟動後要檢查 Zookeeper 是否已經在服務,可以通過 netstat – ano 命令查看是否有你配置的 clientPort 埠號在監聽服務。
2.2 集群部署
Zk進行集群部署時,需保證集群的數量為奇數個,即3、5、7…。
Zookeeper 的集群模式除了上面的三個配置項還要增加下麵幾個配置項:
initLimit=5 syncLimit=2 server.1=192.168.211.1:2888:3888 server.2=192.168.211.2:2888:3888
initLimit:這個配置項是用來配置 Zookeeper 接受客戶端(這裡所說的客戶端不是用戶連接 Zookeeper 伺服器的客戶端,而是 Zookeeper 伺服器集群中連接到 Leader 的 Follower 伺服器)初始化連接時最長能忍受多少個心跳時間間隔數。當已經超過 10 個心跳的時間(也就是 tickTime)長度後 Zookeeper 伺服器還沒有收到客戶端的返回信息,那麼表明這個客戶端連接失敗。總的時間長度就是 5*2000=10 秒
syncLimit:這個配置項標識 Leader 與 Follower 之間發送消息,請求和應答時間長度,最長不能超過多少個 tickTime 的時間長度,總的時間長度就是 2*2000=4 秒
server.A=B:C:D:其中 A 是一個數字,表示這個是第幾號伺服器;B 是這個伺服器的 ip 地址;C 表示的是這個伺服器與集群中的 Leader 伺服器交換信息的埠;D 表示的是萬一集群中的 Leader 伺服器掛了,需要一個埠來重新進行選舉,選出一個新的 Leader,而這個埠就是用來執行選舉時伺服器相互通信的埠。如果是偽集群的配置方式,由於 B 都是一樣,所以不同的 Zookeeper 實例通信埠號不能一樣,所以要給它們分配不同的埠號。
除了修改 zoo.cfg 配置文件,集群模式下還要配置一個文件 myid,這個文件在 dataDir 目錄下,這個文件裡面就有一個數據就是 A 的值,Zookeeper 啟動時會讀取這個文件,拿到裡面的數據與 zoo.cfg 裡面的配置信息比較從而判斷到底是那個 server。
3.Zookeeper服務註冊
對Zookeeper新增、刪除節點的操作可以通過zk提供的基礎api進行操作,也可以選擇一些框架,方便我們的使用,這裡採用的是Curator。
服務url路徑,舉例如下:
myapp/service/user/info
myapp/service/configuration
存儲為zk集群如下名稱格式
myapp/service/
| user/info
| configuration
提供服務的節點信息:
public class ServiceProvider { // 提供服務的實例id private Integer instanceId; // 提供服務的實例ip private String ip; // 提供服務的實例埠號 private Integer port; // 實例註冊時間 private Date registTime; 省略getter、setter方法 public byte[] toBytes(){ try { return JSONObject.fromObject(this).toString().getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new SystemException(); } } }
註冊服務的方法如下:
public void register(String url, ServiceProvider node) { CuratorFramework client = CuratorFrameworkFactory.newClient(url, sessionTimeout, connectionTimeout, new ExponentialBackoffRetry(1000, 3)); client.getConnectionStateListenable().addListener(new ConnectionStateListener() { @Override public void stateChanged(CuratorFramework client, ConnectionState newState) { watcher.process(); } }); client.start();
logger.info("正在註冊 zookeeper 服務節點:path=" + path + ", data=" + node);
client.create().creatingParentsIfNeeded().forPath(path, node.toBytes());
}
logger.info("正在註冊 zookeeper 服務節點:path=" + path + ", data=" + node);
4.Zookeeper服務發現
服務發現的策略可以自定義,如隨機分發、定比例分發、根據伺服器狀態分發等等,其中某種分發策略需要註冊時提供額外的伺服器負載信息等。
監聽器如下:
public class ZookeeperGlobalCacheWatcher { public void watch(Closeable client, String path){ cache = new TreeCache((CuratorFramework)client, path); cache.getListenable().addListener(new TreeCacheListener() { @Override public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception { publishEvent(new CacheWatcherEvent(event)); } }); try { cache.start(); logger.info("啟動treeCache watcher"); } catch (Exception e) { throw new SystemException(); } } }
服務發現:
public List<ServiceProvider> getServiceProvider() { if(treeCache == null){ // 監聽器zookeeperGlobalCacheWatcher,監聽zookeeper的狀態 treeCache = (TreeCache)zookeeperGlobalCacheWatcher.getCache(); } List<ServiceProvider> providers = new ArrayList<ServiceProvider>(); try { // 數據轉換 if(treeCache.getCurrentChildren(path) != null){ for(Entry<String, ChildData> item : treeCache.getCurrentChildren(path).entrySet()){ ChildData data = item.getValue(); if(data != null && data.getData() != null && data.getData().length > 0){ providers.add(JsonUtil.json2Object(new String(item.getValue().getData(), "UTF-8"), ZookeeperServiceProvider.class)); } } } } catch (Exception e) { throw new SystemException(); } return providers; }
服務註冊和發現是整個微服務軟體架構的核心,zookeeper成熟穩定的性能廣受青睞,在系統技術選型和開發過程中,zookeeper都是占有這絕對的優勢。