“沒有最好的技術(shù),只有最合適的技術(shù)?!蔽蚁脒@句話也同樣適用于微服務(wù)領(lǐng)域,沒有最好的服務(wù)框架,只有最適合自己的服務(wù)改造。在 Dubbo 的未來規(guī)劃中,除了保持自身技術(shù)上的領(lǐng)先性,關(guān)注性能、大流量、大規(guī)模集群領(lǐng)域的挑戰(zhàn)外,圍繞 Dubbo 核心來發(fā)展生態(tài),將 Dubbo 打造成一個(gè)服務(wù)化改造的整體方案也是重點(diǎn)之一。從本期開始,我們將推出“服務(wù)化改造”系列文章,通過在一些外圍系統(tǒng)和服務(wù)化基礎(chǔ)組件上的開發(fā)實(shí)踐,分享Dubbo 生態(tài)下的服務(wù)化改造收獲和總結(jié)。
? 8月26日,Aliware Open Source 將首次在成都舉辦 Apache Dubbo 的meetup活動(dòng),Dubbo 、Sentinel和 Nacos 的小哥哥和小姐姐都會(huì)在現(xiàn)場(chǎng)進(jìn)行技術(shù)分享,歡迎成都的朋友報(bào)名參加我們的活動(dòng)喔,搜索“阿里巴巴中間件”公眾號(hào),后臺(tái)發(fā)送“成都meetup”,獲取報(bào)名鏈接。
一、改造背景
在現(xiàn)代的分布式應(yīng)用中,往往會(huì)出現(xiàn)節(jié)點(diǎn)和節(jié)點(diǎn)之間的協(xié)調(diào)問題,其中就包括了:選主,集群管理,分布式鎖,分布式配置管理,統(tǒng)一命名服務(wù),狀態(tài)同步等訴求。Apache ZooKeeper,正如它的名字所暗示的那樣,動(dòng)物園管理員,就是為了解決這些訴求的一個(gè)分布式協(xié)調(diào)服務(wù)框架。
為了保證系統(tǒng)的高可用,ZooKeeper本身也可以部署成集群模式,稱之為ZooKeeper Ensemble。ZooKeeper集群中始終確保其中的一臺(tái)為leader的角色,并通過ZAB(Zookeeper Atomic Broadcast Protocol)[1] 協(xié)議確保所有節(jié)點(diǎn)上的信息的一致。客戶端可以訪問集群中的任何一臺(tái)進(jìn)行讀寫操作,而不用擔(dān)心數(shù)據(jù)出現(xiàn)不一致的現(xiàn)象。
O'Reilly的ebook-Zookeeper-Distributed Process Coordination
ZooKeeper中的數(shù)據(jù)存儲(chǔ)方式與傳統(tǒng)的UNIX文件系統(tǒng)相似,節(jié)點(diǎn)按照樹狀結(jié)構(gòu)來組織,其中,節(jié)點(diǎn)被稱之為znodes(ZooKeeper數(shù)據(jù)節(jié)點(diǎn))
O'Reilly的ebook-Zookeeper-Distributed Process Coordination
二、基本用法
可以通過直接下載的方式[2] 安裝并運(yùn)行ZooKeeper,在Mac上也可以通過Homebrew [3] brew install zookeeper 來安裝,考慮到通用性,本文采用docker的方式來運(yùn)行ZooKeeper。如果沒有安裝docker,請(qǐng)先準(zhǔn)備好docker環(huán)境 [4]。
1、啟動(dòng) ZooKeeper
執(zhí)行命令將 ZooKeeper,運(yùn)行在docker容器中。
docker?run?--rm?--name?zookeeper?-p?2181:2181?zookeeper
2、進(jìn)入 Zookeeper 容器
docker?exec?-it?zookeeper?bash
在bin 目錄下有啟動(dòng) ZooKeeper 的命令 zkServer 以及管理控制臺(tái) ?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``
? ?3、通過zkCli進(jìn)入Zookeeper管理界面
由于是通過Docker啟動(dòng),ZooKeeper 進(jìn)程已經(jīng)啟動(dòng),并通過2181端口對(duì)外提供服務(wù)。
bash-4.4#?psPID???USER?????TIME??COMMAND1?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 的控制臺(tái)來進(jìn)行管理。
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????historyredo?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上的一些基本操作
創(chuàng)建 ?/hello-zone 節(jié)點(diǎn):
[zk:?127.0.0.1:2181(CONNECTED)?19]?create?/hello-zone?'world'Created?/hello-zone
列出 / 下的子節(jié)點(diǎn),確認(rèn) hello-zone 被創(chuàng)建:
[zk:?127.0.0.1:2181(CONNECTED)?19]?create?/hello-zone?'world'Created?/hello-zone
列出 /hello-zone的子節(jié)點(diǎn),確認(rèn)為空:
[zk:?127.0.0.1:2181(CONNECTED)?21]?ls?/hello-zone[]
獲取存儲(chǔ)在 ?/hello-zone節(jié)點(diǎn)上的數(shù)據(jù):
[zk:?127.0.0.1:2181(CONNECTED)?22]?get?/hello-zone world
三、在 Dubbo 中使用ZooKeeper
Dubbo使用 ZooKeeper 用于服務(wù)的注冊(cè)發(fā)現(xiàn)和配置管理,在ZooKeeper中數(shù)據(jù)的組織由下圖所示:
首先,所有Dubbo相關(guān)的數(shù)據(jù)都組織在 /duboo 的根節(jié)點(diǎn)下。
二級(jí)目錄是服務(wù)名,如 ?com.foo.BarService。
三級(jí)目錄有兩個(gè)子節(jié)點(diǎn),分別 providers 和 consumers,表示該服務(wù)的提供者和消費(fèi)者。
四級(jí)目錄記錄了與該服務(wù)相關(guān)的每一個(gè)應(yīng)用實(shí)例的URL信息,在 providers 下的表示該服務(wù)的所有提供者,而在 consumers 下的表示該服務(wù)的所有消費(fèi)者。舉例說明,com.foo.BarService 的服務(wù)提供者在啟動(dòng)時(shí)將自己的URL信息注冊(cè)到 ? /dubbo/com.foo.BarService/providers 下;同樣的,服務(wù)消費(fèi)者將自己的信息注冊(cè)到相應(yīng)的 consumers 下,同時(shí),服務(wù)消費(fèi)者會(huì)訂閱其所對(duì)應(yīng)的 providers 節(jié)點(diǎn),以便能夠感知到服務(wù)提供方地址列表的變化。
四、準(zhǔn)備示例代碼
本文代碼可以在以下鏈接中找到。
1、接口定義
一個(gè)定義簡(jiǎn)單的 GreetingService 接口,只有里面一個(gè)簡(jiǎn)單的方法 sayHello 向調(diào)用者問好。
public?interface?GreetingService?{String?sayHello(String?name);}
2、服務(wù)端:服務(wù)實(shí)現(xiàn)
實(shí)現(xiàn) ?GreetingService接口,并通過 @Service 來標(biāo)注其為Dubbo的一個(gè)服務(wù)。
@Servicepublic?class?AnnotatedGreetingService?implements?GreetingService?{public?String?sayHello(String?name)?{????return?"hello,?"?+?name; }}
3、服務(wù)端:組裝
定義 ProviderConfiguration 來組裝Dubbo服務(wù)。
@Configuration@EnableDubbo(scanBasePackages?=?"com.alibaba.dubbo.samples.impl")@PropertySource("classpath:/spring/dubbo-provider.properties")static?class?ProviderConfiguration?{}
dubbo-provider.properties是在Spring應(yīng)用中外置配置的方式,內(nèi)容如下:
dubbo.application.name=demo-provider dubbo.registry.address=zookeeper://$DOCKER_HOST:2181 dubbo.protocol.name=dubbo dubbo.protocol.port=20880
由于ZooKeeper運(yùn)行在Docker容器中,需要注意的是:
本文假定Dubbo應(yīng)用運(yùn)行在宿主機(jī)上,也就是Docker容器外,需要將ZooKeeper的地址替換成環(huán)境變量${DOCKER_HOST}所指定的IP地址,相關(guān)信息請(qǐng)查閱Docker官方文檔;
如果Dubbo應(yīng)用也是Docker化的應(yīng)用,只需要用ZooKeeper的容器名,在本文中容器名是ZooKeeper;
當(dāng)然,如果不用容器方式啟動(dòng)ZooKeeper,只需要簡(jiǎn)單的將這里的$ DOCKER_HOST換成localhost即可。
4、服務(wù)端:?jiǎn)?dòng)服務(wù)
在 main 方法中通過啟動(dòng)一個(gè)Spring Context來對(duì)外提供Dubbo服務(wù)。
public?class?ProviderBootstrap?{public?static?void?main(String[]?args)?throws?Exception?{ ????AnnotationConfigApplicationContext?context?=?new?AnnotationConfigApplicationContext(ProviderConfiguration.class); ????context.start(); ????System.in.read(); }}
服務(wù)啟動(dòng)端的的 main 方法,將會(huì)看到下面的輸出,代表服務(wù)端啟動(dòng)成功,并在注冊(cè)中心(ZooKeeperRegistry)注冊(cè)上了 GreetingService 這個(gè)服務(wù):
[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管理終端觀察服務(wù)提供方的注冊(cè)信息:
$?docker?exec?-it?zookeeper?bash bash-4.4#?bin/zkCli.sh?-server?localhost:218Connecting?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]
可以看到剛剛啟動(dòng)的Dubbo的服務(wù)在providers 節(jié)點(diǎn)下注冊(cè)了自己的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&側(cè)=提供商時(shí)間戳=1533264631849
5、客戶端:引用服務(wù)
通過 @Reference 來在客戶端聲明服務(wù)的引用,運(yùn)行時(shí)將會(huì)通過該引用發(fā)起全程調(diào)用,而服務(wù)的目標(biāo)地址將會(huì)從ZooKeeper的provider 節(jié)點(diǎn)下查詢。
@Component("annotatedConsumer")public?class?GreetingServiceConsumer?{@Referenceprivate?GreetingService?greetingService;public?String?doSayHello(String?name)?{????return?greetingService.sayHello(name); }}
? ?6、客戶端:組裝
定義ConsumerConfiguration來組裝Dubbo服務(wù)。
@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應(yīng)用中外置配置的方式,內(nèi)容如下:
dubbo.application.name=demo-consumer dubbo.registry.address=zookeeper://$DOCKER_HOST:2181 dubbo.consumer.timeout=3000
與服務(wù)端:組裝相同,需要根據(jù)自己的運(yùn)行環(huán)境來修改dubbo.registry.address中定義的$ DOCKER_HOST。請(qǐng)參閱步驟3的說明部分。
7、客戶端:發(fā)起遠(yuǎn)程調(diào)用
運(yùn)行 main 向已經(jīng)啟動(dòng)的服務(wù)提供方發(fā)起一次遠(yuǎn)程調(diào)用。Dubbo會(huì)先向ZooKeeper訂閱服務(wù)地址,然后從返回的地址列表中選取一個(gè),向?qū)Χ税l(fā)起調(diào)用:
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(); } }
運(yùn)行結(jié)果如下:
[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
說明:
注冊(cè):消費(fèi)者://192.168.99.1/...& category= consumers&:消費(fèi)者向ZooKeeper注冊(cè)自己的信息,并放在 consumers 節(jié)點(diǎn)下
訂閱:消費(fèi)者://192.168.99.1/...& 類別=提供商,配置器,路由器:消費(fèi)者同時(shí)向動(dòng)物園管理員訂閱了providers、configurators、routers 節(jié)點(diǎn),其中 configurations ?與多寶配置相關(guān),routers 與路由規(guī)則相關(guān),值得注意的英文 providers 節(jié)點(diǎn)的訂閱,當(dāng)有新的服務(wù)提供方加入后,由于訂閱的關(guān)系,新的地址列表會(huì)推送給訂閱方,服務(wù)的消費(fèi)者也因此動(dòng)態(tài)感知到了地址列表的變化。
通過ZooKeeper管理終端觀察服務(wù)提供方的注冊(cè)信息:
$?docker?exec?-it?zookeeper?bash bash-4.4#?bin/zkCli.sh?-server?localhost:218Connecting?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的服務(wù)消費(fèi)者在 consumers 節(jié)點(diǎn)下注冊(cè)了自己的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
五、總結(jié)
本文側(cè)重介紹了如何在Dubbo應(yīng)用中使用Zookeeper做為注冊(cè)中心,當(dāng)然,本文也提到了Zookeeper在Dubbo的應(yīng)用場(chǎng)景下還承擔(dān)了配置中心和服務(wù)治理的職責(zé)。本文中的Zookeeper是單節(jié)點(diǎn),Standalone的模式,在生產(chǎn)環(huán)境中為了高可用的訴求,往往會(huì)組件Zookeeper集群,也就是Zookeeper ensemble模式。
通過本文的學(xué)習(xí),讀者可以掌握到:
ZooKeeper的基本概念和基本用法
ZooKeeper在Dubbo應(yīng)用中的作用
通過實(shí)戰(zhàn)了解ZooKeeper與Dubbo的交互
Dubbo在ZooKeeper中服務(wù)注冊(cè),消費(fèi)信息的存儲(chǔ)方式
當(dāng)然,自從阿里巴巴開源 Nacos 后,Dubbo生態(tài)中又多了一項(xiàng)動(dòng)態(tài)服務(wù)發(fā)現(xiàn)的選項(xiàng),Nacos + Dubbo的組合正進(jìn)一步釋放 Dubbo 在云原生及ServiceMesh時(shí)代中,在大規(guī)模微服務(wù)治理、流量治理、服務(wù)集成與服務(wù)共享等服務(wù)平臺(tái)能力建設(shè)上的威力。- 鏈接
作者:中間件小哥 ? ? ? ?
評(píng)論
查看更多