華文網

「ZooKeeper.net」1 模仿dubbo實現一個簡要的http服務的註冊 基於webapi

今天來試著模仿下dubbo實現一個簡要的http服務的註冊,雖說是模仿不過是很廉價的那種,只是模仿了一點點點......

先放上demo目錄結構:

開頭還是把ZooKeeper的一些簡要介紹搬過來看看,這樣讓大家也能多瞭解點兒:

ZooKeeper是一個分散式的,開放源碼的分散式應用程式協調服務,它包含一個簡單的原語集,分散式應用程式可以基於它實現同步服務,配置維護和命名服務等。Zookeeper是hadoop的一個子項目,其發展歷程無需贅述。在分散式應用中,由於工程師不能很好地使用鎖機制,

以及基於消息的協調機制不適合在某些應用中使用,因此需要有一種可靠的、可擴展的、分散式的、可配置的協調機制來統一系統的狀態。Zookeeper的目的就在於此。

Zoopkeeper 提供了一套很好的分散式集群管理的機制,就是它這種基於層次型的目錄樹的資料結構,並對樹中的節點進行有效管理,從而可以設計出多種多樣的分散式的資料管理模型。

OK,更多介紹大家自行搜索吧,

主要點【基於層次型的目錄樹的資料結構,並對樹中節點進行有效管理】,這句話是不是可以理解就是樹形結構,我也放個圖,省的還要大家腦補......

ps.有關ZooKeeper的安裝不管是windows還是linux不論是單機還是集群網上一搜好多的,我用的zookeeper-3.4.6 windows版的

首先我們要獲取到ZooKeeper.Net的用戶端

首先定義個IZooKeeperFactory

public interface IZooKeeperFactory { ZooKeeper Connect(string address); ZooKeeper Connect(string address, TimeSpan timeoutSpan); ZooKeeper Connect(string address, TimeSpan timeoutSpan, IWatcher watcher); ZooKeeper Connect(string address, TimeSpan timeoutSpan, IWatcher watcher, long sessionId, byte[] password); }

然後ZooKeeperFactory 和 Watcher

public sealed class ZooKeeperFactory : IZooKeeperFactory { public static IZooKeeperFactory Instance = new ZooKeeperFactory; private ZooKeeperFactory { } //創建一個Zookeeper實例,第一個參數為目標伺服器位址和埠,第二個參數為Session超時時間,第三個為節點變化時的回檔方法 public ZooKeeper Connect(string address) { return Connect(address, new TimeSpan(0, 0, 0, 30), new Watcher); } public ZooKeeper Connect(string address, TimeSpan timeoutSpan) { return Connect(address, timeoutSpan, new Watcher); } public ZooKeeper Connect(string address, TimeSpan timeoutSpan, IWatcher watcher) { return new ZooKeeper(address, timeoutSpan, watcher); } public ZooKeeper Connect(string address, TimeSpan timeoutSpan, IWatcher watcher, long sessionId, byte[] password) { return new ZooKeeper(address, timeoutSpan, watcher, sessionId, password); } }

ZooKeeperFactory

internal class Watcher : IWatcher { public void Process(WatchedEvent @event) { if (@event.Type == EventType.NodeChildrenChanged) { // Console.WriteLine(@event.Path + " 此路徑節點變化了!"); } if (@event.Type == EventType.NodeDataChanged) { // Console.WriteLine(@event.Path + " 此路徑節點變化了!"); } if (@event.Type == EventType.NodeDeleted) { // Console.WriteLine(@event.Path + " 此路徑節點變化了!"); } } }

Watcher

以上直接用於創建Zookeeper的實例,各個參數代碼中也寫了說明

然後我們創建UserServicesController 裡面寫了兩個服務 1.User/sayhello 2.User/saybye

public class UserServicesController : ApiController { [Route("User/sayhello")] public string sayhello { return "hello world!"; } [Route("User/saybye")] public string saybye { return "byebye world!"; } }

View Code

我們最終要實現的結果是這樣子的:【x】只是為了起到標示作用

︱-------【1】UserServices

︱-----------【1.1】User.sayhello

︱----------------【1.1.1】192.168.0.1:80

︱----------------【1.1.2】192.168.0.2:80

︱-----------【1.2】User.saybye

︱----------------【1.2.1】192.168.0.1:81

︱----------------【1.2.2】192.168.0.2:81

接下來來看看最主要的一個類 ScanConfig.cs

先說下大概邏輯:

1.初始化zk用戶端

2.將服務名稱UserServices 註冊為主節點 (永久節點)

3.獲取所有webapi服務路徑 例如:User/saybye

4.迴圈將所有服務路徑註冊為zk節點(永久節點) 讀取伺服器IP地址(臨時節點 會話斷開 節點自動消失) 註冊到 每個服務節點下

下面把代碼貼一下,代碼中也有注釋,最後會給出demo

public class ScanConfig { private String connectString = "127.0.0.1:2181"; private ZooKeeper zk = null; public ScanConfig { init; } public void init { try { //初始化zk用戶端 buildZKclient; //綁定服務父節點 services名稱 registBiz; //得到所有服務位址 List services = getServicePath; //把服務註冊到zk registBizServices(services); } catch (Exception) { //日誌記錄 } } /// /// 初始化連接 /// private void buildZKclient { IZooKeeperFactory _factory = ZooKeeperFactory.Instance; zk = _factory.Connect(connectString, new TimeSpan(0, 0, 0, 30)); } /// /// 綁定父節點 /// private void registBiz { if (zk.Exists("/" + WebApiConfig.bizCode, true) == null) { //創建一個目錄節點,不進行ACL許可權控制,
節點為永久性的 zk.Create("/" + WebApiConfig.bizCode, ("Test:" + WebApiConfig.bizCode).GetBytes, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent); } } /// /// 通過注解得到所有Api服務位址 /// /// private List getServicePath { List PathString = new List; Collection ApiString = GlobalConfiguration.Configuration.Services.GetApiExplorer.ApiDescriptions; foreach (var api in ApiString) { PathString.Add(api.RelativePath.Replace("/", ".")); } return PathString; } /// /// 把服務註冊到zk /// /// 所有Api服務位址 private void registBizServices(List Services) { //獲取服務IP位址埠 IPAddress IPList = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName).AddressList; string hostName = IPList[0].ToString; //創建臨時節點 會話斷開 節點自動消失 foreach (var apiname in Services) { if (zk.Exists("/" + WebApiConfig.bizCode + "/" + apiname, true) == null) { //創建一個目錄節點,不進行ACL許可權控制,節點為永久性的 服務名稱 zk.Create("/" + WebApiConfig.bizCode + "/" + apiname, ("Test:" + WebApiConfig.bizCode).GetBytes, Ids.OPEN_ACL_UNSAFE, CreateMode.Persistent); } //創建一個目錄節點,不進行ACL許可權控制,節點為臨時性的 服務提供者ip位址 zk.Create("/" + WebApiConfig.bizCode + "/" + apiname + "/" + hostName, ("Test:" + WebApiConfig.bizCode).GetBytes, Ids.OPEN_ACL_UNSAFE, CreateMode.Ephemeral); } } }

ScanConfig

最後Global.asax中 Application_Start方法加入就OK了

ScanConfig scan = new ScanConfig;

有關註冊後節點過一段時間消失的問題可以參考下 _xxy的http://www.cnblogs.com/xxyBlogs/p/5538583.html 這篇文章。

之前做過一個webservices介面裡面有一些資料是調用別的專案的webservices介面,然後第一次訪問這個介面處理時間老是很慢,處理方法跟 _xxy這種處理大體一致,因為iis有閒時超時跟回收設置,所以到時間或回收後,webservices的xml序列化的操作需要從新做,所以調用很耗時,不過往往都是第一次很耗時,序列化過完後面就變的很快,加上當是部署了六台,不定那一台就處於閒時狀態,當是就因為這個問題項目耽擱好幾天沒上線,後來設置了iis的閒時超時跟回收設置,又寫了個頁面,頁面的load事件訪問一下webservices,然後Application_Start的時候就啟動一個計時器,設置一定時間後就http get一下這個頁面,等於也是讓序列化的過程在不知不覺中處理掉,這樣有實際業務來的時候序列化的操作不用做了,耗時的問題也不存在了,專案也成功上線了,希望能幫助到有此類問題的童鞋哈!

之前做過一個webservices介面裡面有一些資料是調用別的專案的webservices介面,然後第一次訪問這個介面處理時間老是很慢,處理方法跟 _xxy這種處理大體一致,因為iis有閒時超時跟回收設置,所以到時間或回收後,webservices的xml序列化的操作需要從新做,所以調用很耗時,不過往往都是第一次很耗時,序列化過完後面就變的很快,加上當是部署了六台,不定那一台就處於閒時狀態,當是就因為這個問題項目耽擱好幾天沒上線,後來設置了iis的閒時超時跟回收設置,又寫了個頁面,頁面的load事件訪問一下webservices,然後Application_Start的時候就啟動一個計時器,設置一定時間後就http get一下這個頁面,等於也是讓序列化的過程在不知不覺中處理掉,這樣有實際業務來的時候序列化的操作不用做了,耗時的問題也不存在了,專案也成功上線了,希望能幫助到有此類問題的童鞋哈!