「沒有最好的技術,只有最合適的技術。」我想這句話也一樣適用於微服務領域,沒有最好的服務框架,只有最適合本身的服務改造。在 Dubbo 的將來規劃中,除了保持自身技術上的領先性,關注性能、大流量、大規模集羣領域的挑戰外,圍繞 Dubbo 核心來發展生態,將 Dubbo 打形成一個服務化改造的總體方案也是重點之一。從本期開始,咱們將推出「服務化改造」系列文章,經過在一些外圍系統和服務化基礎組件上的開發實踐,分享Dubbo 生態下的服務化改造收穫和總結。java
» 8月26日,Aliware Open Source 將首次在成都舉辦 Apache Dubbo 的meetup活動,Dubbo 、Sentinel和 Nacos 的小哥哥和小姐姐都會在現場進行技術分享,歡迎成都的朋友報名參加咱們的活動喔,搜索「阿里巴巴中間件」公衆號,後臺發送「成都meetup」,獲取報名連接。node
在現代的分佈式應用中,每每會出現節點和節點之間的協調問題,其中就包括了:選主,集羣管理,分佈式鎖,分佈式配置管理,統一命名服務,狀態同步等訴求。Apache ZooKeeper,正如它的名字所暗示的那樣,動物園管理員,就是爲了解決這些訴求的一個分佈式協調服務框架。git
爲了保證系統的高可用,ZooKeeper自己也能夠部署成集羣模式,稱之爲ZooKeeper Ensemble。ZooKeeper集羣中始終確保其中的一臺爲leader的角色,並經過ZAB(Zookeeper Atomic Broadcast Protocol)[1] 協議確保全部節點上的信息的一致。客戶端能夠訪問集羣中的任何一臺進行讀寫操做,而不用擔憂數據出現不一致的現象。github
O'Reilly的ebook-Zookeeper-Distributed Process Coordinationspring
ZooKeeper中的數據存儲方式與傳統的UNIX文件系統類似,節點按照樹狀結構來組織,其中,節點被稱之爲znodes(ZooKeeper數據節點)docker
O'Reilly的ebook-Zookeeper-Distributed Process Coordinationapi
能夠經過直接下載的方式[2] 安裝並運行ZooKeeper,在Mac上也能夠經過Homebrew [3] brew install zookeeper 來安裝,考慮到通用性,本文采用docker的方式來運行ZooKeeper。若是沒有安裝docker,請先準備好docker環境 [4]。bash
一、啓動 ZooKeeper
執行命令將 ZooKeeper,運行在docker容器中。app
docker run --rm --name zookeeper -p 2181:2181 zookeeper
二、進入 Zookeeper 容器框架
docker exec -it zookeeper bash
在bin 目錄下有啓動 ZooKeeper 的命令 zkServer 以及管理控制檯 zkCli
bash-4.4# ls -l bintotal 36 -rwxr-xr-x 1 zookeepe zookeepe 232 Mar 27 04:32 README.txt -rwxr-xr-x 1 zookeepe zookeepe 1937 Mar 27 04:32 zkCleanup.sh -rwxr-xr-x 1 zookeepe zookeepe 1056 Mar 27 04:32 zkCli.cmd -rwxr-xr-x 1 zookeepe zookeepe 1534 Mar 27 04:32 zkCli.sh -rwxr-xr-x 1 zookeepe zookeepe 1759 Mar 27 04:32 zkEnv.cmd -rwxr-xr-x 1 zookeepe zookeepe 2696 Mar 27 04:32 zkEnv.sh -rwxr-xr-x 1 zookeepe zookeepe 1089 Mar 27 04:32 zkServer.cmd -rwxr-xr-x 1 zookeepe zookeepe 6773 Mar 27 04:32 zkServer.sh``
三、經過zkCli進入Zookeeper管理界面
因爲是經過Docker啓動,ZooKeeper 進程已經啓動,並經過2181端口對外提供服務。
bash-4.4# psPID USER TIME COMMAND 1 zookeepe 0:02 /usr/lib/jvm/java-1.8-openjdk/jre/bin/java -Dzookeeper.log.dir=. -Dzookeeper.root 32 root 0:00 bash 42 root 0:00 ps
所以能夠直接經過zkCli來訪問 ZooKeeper 的控制檯來進行管理。
bash-4.4# bin/zkCli.sh -server 127.0.0.1:2181Connecting to 127.0.0.1:2181... WATCHER:: WatchedEvent state:SyncConnected type:None path:null[zk: 127.0.0.1:2181(CONNECTED) 0] helpZooKeeper -server host:port cmd args stat path [watch] set path data [version] ls path [watch] delquota [-n|-b] path ls2 path [watch] setAcl path acl setquota -n|-b val path history redo cmdno printwatches on|off delete path [version] sync path listquota path rmr path get path [watch] create [-s] [-e] path data acl addauth scheme auth quit getAcl path close connect host:port
4.zkCli上的一些基本操做
建立 /hello-zone 節點:
[zk: 127.0.0.1:2181(CONNECTED) 19] create /hello-zone 'world'Created /hello-zone
列出 / 下的子節點,確認 hello-zone 被建立:
[zk: 127.0.0.1:2181(CONNECTED) 19] create /hello-zone 'world'Created /hello-zone
列出 /hello-zone的子節點,確認爲空:
[zk: 127.0.0.1:2181(CONNECTED) 21] ls /hello-zone[]
獲取存儲在 /hello-zone節點上的數據:
[zk: 127.0.0.1:2181(CONNECTED) 22] get /hello-zone world
Dubbo使用 ZooKeeper 用於服務的註冊發現和配置管理,在ZooKeeper中數據的組織由下圖所示:
首先,全部Dubbo相關的數據都組織在 /duboo 的根節點下。
二級目錄是服務名,如 com.foo.BarService。
三級目錄有兩個子節點,分別 providers 和 consumers,表示該服務的提供者和消費者。
四級目錄記錄了與該服務相關的每個應用實例的URL信息,在 providers 下的表示該服務的全部提供者,而在 consumers 下的表示該服務的全部消費者。舉例說明,com.foo.BarService 的服務提供者在啓動時將本身的URL信息註冊到 /dubbo/com.foo.BarService/providers 下;一樣的,服務消費者將本身的信息註冊到相應的 consumers 下,同時,服務消費者會訂閱其所對應的 providers 節點,以便可以感知到服務提供方地址列表的變化。
本文代碼能夠在如下連接中找到。
一、接口定義
一個定義簡單的 GreetingService 接口,只有裏面一個簡單的方法 sayHello 向調用者問好。
public interface GreetingService { String sayHello(String name);}
二、服務端:服務實現
實現 GreetingService接口,並經過 @Service 來標註其爲Dubbo的一個服務。
@Servicepublic class AnnotatedGreetingService implements GreetingService { public String sayHello(String name) { return "hello, " + name; }}
三、服務端:組裝
定義 ProviderConfiguration 來組裝Dubbo服務。
@Configuration@EnableDubbo(scanBasePackages = "com.alibaba.dubbo.samples.impl")@PropertySource("classpath:/spring/dubbo-provider.properties")static class ProviderConfiguration {}
dubbo-provider.properties是在Spring應用中外置配置的方式,內容以下:
dubbo.application.name=demo-provider dubbo.registry.address=zookeeper://$DOCKER_HOST:2181 dubbo.protocol.name=dubbo dubbo.protocol.port=20880
因爲ZooKeeper運行在Docker容器中,須要注意的是:
四、服務端:啓動服務
在 main 方法中經過啓動一個Spring Context來對外提供Dubbo服務。
public class ProviderBootstrap { public static void main(String[] args) throws Exception { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class); context.start(); System.in.read(); }}
服務啓動端的的 main 方法,將會看到下面的輸出,表明服務端啓動成功,並在註冊中心(ZooKeeperRegistry)註冊上了 GreetingService 這個服務:
[03/08/18 10:50:33:033 CST] main INFO zookeeper.ZookeeperRegistry: [DUBBO] Register: dubbo://192.168.99.1:20880/com.alibaba.dubbo.samples.api.GreetingService?anyhost=true&application=demo-provider&dubbo=2.6.2&generic=false&interface=com.alibaba.dubbo.samples.api.GreetingService&methods=sayHello&pid=12938&side=provider×tamp=1533264631849, dubbo version: 2.6.2, current host: 192.168.99.1
經過ZooKeeper管理終端觀察服務提供方的註冊信息:
$ docker exec -it zookeeper bash bash-4.4# bin/zkCli.sh -server localhost:218 Connecting to localhost:2181 ... Welcome to ZooKeeper! JLine support is enabled ... [zk: localhost:2181(CONNECTED) 0] ls [dubbo%3A%2F%2F192.168.99.1%3A20880%2Fcom.alibaba.dubbo.samples.api.GreetingService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.samples.api.GreetingService%26methods%3DsayHello%26pid%3D12938%26side%3Dprovider%26timestamp%3D1533264631849]
能夠看到剛剛啓動的Dubbo的服務在providers 節點下注冊了本身的URL地址:dubbo://192.168.99.1:20880 /com.alibaba.dubbo.samples.api.GreetingService?anyhost = true&application = demo-provider&dubbo =2.6 0.2&通用=假接口=com.alibaba.dubbo.samples.api.GreetingService&方法= sayHello的&PID = 12938&側=提供商時間戳=1533264631849
五、客戶端:引用服務
經過 @Reference 來在客戶端聲明服務的引用,運行時將會經過該引用發起全程調用,而服務的目標地址將會從ZooKeeper的provider 節點下查詢。
@Component("annotatedConsumer")public class GreetingServiceConsumer { @Reference private GreetingService greetingService; public String doSayHello(String name) { return greetingService.sayHello(name); }}
六、客戶端:組裝
定義ConsumerConfiguration來組裝Dubbo服務。
@Configuration@EnableDubbo(scanBasePackages = "com.alibaba.dubbo.samples.action")@PropertySource("classpath:/spring/dubbo-consumer.properties")@ComponentScan(value = {"com.alibaba.dubbo.samples.action"})static class ConsumerConfiguration {}
dubbo-consumer.properties是在Spring應用中外置配置的方式,內容以下:
dubbo.application.name=demo-consumer dubbo.registry.address=zookeeper://$DOCKER_HOST:2181 dubbo.consumer.timeout=3000
與服務端:組裝相同,須要根據本身的運行環境來修改dubbo.registry.address中定義的$ DOCKER_HOST。請參閱步驟3的說明部分。
七、客戶端:發起遠程調用
運行 main 向已經啓動的服務提供方發起一次遠程調用。Dubbo會先向ZooKeeper訂閱服務地址,而後從返回的地址列表中選取一個,向對端發起調用:
public class ConsumerBootstrap { public static void main(String[] args) {public class ConsumerBootstrap { public static void main(String[] args) throws IOException { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class); context.start(); GreetingServiceConsumer greetingServiceConsumer = context.getBean(GreetingServiceConsumer.class); String hello = greetingServiceConsumer.doSayHello("zookeeper"); System.out.println("result: " + hello); System.in.read(); } }
運行結果以下:
[03/08/18 01:42:31:031 CST] main INFO zookeeper.ZookeeperRegistry: [DUBBO] Register: consumer://192.168.99.1/com.alibaba.dubbo.samples.api.GreetingService?application=demo-consumer&category=consumers&check=false&default.timeout=3000&dubbo=2.6.2&interface=com.alibaba.dubbo.samples.api.GreetingService&methods=sayHello&pid=82406&side=consumer×tamp=1533274951195, dubbo version: 2.6.2, current host: 192.168.99.1 #1[03/08/18 01:42:31:031 CST] main INFO zookeeper.ZookeeperRegistry: [DUBBO] Subscribe: consumer://192.168.99.1/com.alibaba.dubbo.samples.api.GreetingService?application=demo-consumer&category=providers,configurators,routers&default.timeout=3000&dubbo=2.6.2&interface=com.alibaba.dubbo.samples.api.GreetingService&methods=sayHello&pid=82406&side=consumer×tamp=1533274951195, dubbo version: 2.6.2, current host: 192.168.99.1 #2...
result: hello, zookeeper
經過ZooKeeper管理終端觀察服務提供方的註冊信息:
$ docker exec -it zookeeper bash bash-4.4# bin/zkCli.sh -server localhost:218 Connecting to localhost:2181 ... Welcome to ZooKeeper! JLine support is enabled ... [zk: localhost:2181(CONNECTED) 4] ls /dubbo/com.alibaba.dubbo.samples.api.GreetingService/consumers[consumer%3A%2F%2F192.168.99.1%2Fcom.alibaba.dubbo.samples.api.GreetingService%3Fapplication%3Ddemo-consumer%26category%3Dconsumers%26check%3Dfalse%26default.timeout%3D3000%26dubbo%3D2.6.2%26interface%3Dcom.alibaba.dubbo.samples.api.GreetingService%26methods%3DsayHello%26pid%3D82406%26side%3Dconsumer%26timestamp%3D1533274951195]
能夠看到Dubbo的服務消費者在 consumers 節點下注冊了本身的URL地址:
consumer://192.168.99.1/com.alibaba.dubbo.samples.api.GreetingService?application=demo-consumer&category=providers,configurators,routers&default.timeout=3000&dubbo=2.6.2&interface=com.alibaba.dubbo.samples.api.GreetingService&methods=sayHello&pid=82406&side=consumer×tamp=1533274951195
本文側重介紹瞭如何在Dubbo應用中使用Zookeeper作爲註冊中心,固然,本文也提到了Zookeeper在Dubbo的應用場景下還承擔了配置中心和服務治理的職責。本文中的Zookeeper是單節點,Standalone的模式,在生產環境中爲了高可用的訴求,每每會組件Zookeeper集羣,也就是Zookeeper ensemble模式。
經過本文的學習,讀者能夠掌握到:
固然,自從阿里巴巴開源 Nacos 後,Dubbo生態中又多了一項動態服務發現的選項,Nacos + Dubbo的組合正進一步釋放 Dubbo 在雲原生及ServiceMesh時代中,在大規模微服務治理、流量治理、服務集成與服務共享等服務平臺能力建設上的威力。
做者:中間件小哥