在前面的文章我們分析了一下 dubbo 遠(yuǎn)程服務(wù)暴露過(guò)程中通過(guò) Netty 進(jìn)行 Socket 服務(wù)暴露。使得遠(yuǎn)程客戶端可以訪問(wèn)這個(gè)暴露的服務(wù),這個(gè)只是解決了訪問(wèn)之前點(diǎn)到點(diǎn)的服務(wù)調(diào)用。對(duì)于分步式環(huán)境當(dāng)中,越來(lái)越多的服務(wù)我們?nèi)绾喂芾聿⑶抑卫磉@些服務(wù)是一個(gè)問(wèn)題。因此 dubbo 引入了注冊(cè)中心這個(gè)概念,把服務(wù)暴露、服務(wù)調(diào)用的信息保存到注冊(cè)中心上面。并且還可以訂閱注冊(cè)中心,實(shí)現(xiàn)服務(wù)自動(dòng)發(fā)現(xiàn)。因?yàn)?dubbo 遠(yuǎn)程暴露里面的過(guò)程還是比較復(fù)雜的,所以我就分為三個(gè)文章來(lái)講解 dubbo 的遠(yuǎn)程暴露:
- dubbo 遠(yuǎn)程暴露 – Netty 暴露服務(wù)
- dubbo 遠(yuǎn)程暴露 – Zookeeper 連接
- dubbo 遠(yuǎn)程暴露 – Zookeeper 注冊(cè) & 訂閱
在上篇文章中我們分析 dubbo 是如何創(chuàng)建 zookeeper 這個(gè)注冊(cè)中心的。下面我們就來(lái)分析一下 dubbo 究竟使用 zookeeper 這個(gè)組件來(lái)做了哪些事。
1、RegistryProtocol#export
下面就是 dubbo 暴露的核心步驟的代碼,可能由于版本的原因(下面的代碼基于 2.6.1)代碼會(huì)有所差異但是核心思想不變。
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
//export invoker
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
URL registryUrl = getRegistryUrl(originInvoker);
//registry provider
final Registry registry = getRegistry(originInvoker);
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//to judge to delay publish whether or not
boolean register = registedProviderUrl.getParameter("register", true);
ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registedProviderUrl);
if (register) {
register(registryUrl, registedProviderUrl);
ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
}
// Subscribe the override data
// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call the same service. Because the subscribed is cached key with the name of the service, it causes the subscription information to cover.
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
//Ensure that a new exporter instance is returned every time export
return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registedProviderUrl);
}
在之前的文章中分析了 dubbo 通過(guò) netty 暴露服務(wù),然后獲取到 zookeeper 注冊(cè)中心 --- ZookeeperRegistry
。下面就是把 Provider (服務(wù)提供者)的信息注冊(cè)到注冊(cè)中心上。這樣 Consumer (服務(wù)消費(fèi)者)就可以通過(guò) Registry (注冊(cè)中心)獲取到 Provider 的信息。這樣就解耦了 Provider 與 Consumer,并且可以通過(guò) Registry 來(lái)修改路由規(guī)則、權(quán)重等控制。這樣就達(dá)到的服務(wù)的治理。下面我們就來(lái)講解一下,服務(wù)的注冊(cè)與訂閱。
2、ZookeeperRegistry#register
這個(gè)就是把服務(wù)信息注冊(cè)到 Zookeeper 上面去。
1、AbstractRegistry#register
添加注冊(cè)Set<URL> registered
(已注冊(cè)的URL),用于失敗重試。
2、ZookeeperRegistry#doRegister
通過(guò)上篇 blog 講解的獲取到的 ZookeeperClient 把服務(wù)信息注冊(cè)到 Zookeeper 上。此時(shí)的 URL 為:
dubbo://192.168.75.1:20880/com.alibaba.dubbo.demo.DemoService?
anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&
interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2076&
side=provider×tamp=1522237459900
dubbo 會(huì)基于這個(gè) URL 生成一個(gè)新的URL,生成規(guī)則為:
"/dubbo/" + url里面的interface的值 + "/providers/" + URL.encode(url)
生成后的值 :
/dubbo/com.alibaba.dubbo.demo.DemoService/providers/
dubbo%3A%2F%2F192.168.75.1%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D2076%26side%3Dprovider%26timestamp%3D1522237459900
然后在 Zookeeper 上面把 /dubbo/com.alibaba.dubbo.demo.DemoService/providers/
創(chuàng)建成持久化節(jié)點(diǎn),而后面部分的 URL 就會(huì)創(chuàng)建成臨時(shí)節(jié)點(diǎn)。
把服務(wù)提供者信息創(chuàng)建成臨時(shí)節(jié)點(diǎn)的好處就是如果當(dāng)前服務(wù)掛掉,這個(gè)節(jié)點(diǎn)就會(huì)自動(dòng)刪除。這樣失效服務(wù)就可以自動(dòng)剔除。
Zookeeper 持久化節(jié)點(diǎn) 和臨時(shí)節(jié)點(diǎn)有什么區(qū)別?
持久化節(jié)點(diǎn):一旦被創(chuàng)建,觸發(fā)主動(dòng)刪除掉,否則就一直存儲(chǔ)在ZK里面。
臨時(shí)節(jié)點(diǎn):與客戶端會(huì)話綁定,一旦客戶端會(huì)話失效,這個(gè)客戶端端所創(chuàng)建的所有臨時(shí)節(jié)點(diǎn)都會(huì)被刪除。
3、ZookeeperRegistry#subscribe
通過(guò)訂閱 Zookeeper 上面的的節(jié)點(diǎn)信息變更, 可以通過(guò) dubbo-admin
來(lái)修改服務(wù)路由規(guī)則、權(quán)重等。
1、創(chuàng)建一個(gè) NotifyListener
實(shí)例 OverrideListener
, 當(dāng)收到服務(wù)變更通知時(shí)觸發(fā)。
2、在 Zookeeper 注冊(cè)中心創(chuàng)建持久化節(jié)點(diǎn)/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
,用于接收 dubbo-admin
這個(gè)客戶端上對(duì)于集群的服務(wù)治理。
ZookeeperRegistry#doSubscribe {
zkClient.create(path, false)
}
3、啟動(dòng)加入訂閱/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
,如果該節(jié)點(diǎn)信息發(fā)生改變,就會(huì)交給FailbackRegistry.notify
處理。
ZookeeperRegistry#doSubscribe {
zkClient.addChildListener(path, zkListener)
}
4、FailbackRegistry#notify
1、把服務(wù)端的注冊(cè) url 信息更新到本地緩存(AbstractRegistry#saveProperties
),以Window為例:
C:\Users\Carl\.dubbo\dubbo-registry-10.65.209.12.cache
2、調(diào)用傳入的OverrideListener#notify
,如果修改了 URL 信息,調(diào)用RegistryProtocol.this.doChangeLocalExport(originInvoker, newUrl)
重新暴露當(dāng)前服務(wù)。
前面二篇加上這一篇就是 dubbo 遠(yuǎn)程服務(wù)暴露的整個(gè)過(guò)程。下面這張圖片就是 dubbo 進(jìn)行服務(wù)暴露、服務(wù)遠(yuǎn)程調(diào)用添加 dubbo monitor 后 Zookeeper 上面 dubbo 節(jié)點(diǎn)的結(jié)構(gòu)圖。
關(guān)于zookeeper在Dubbo中扮演了一個(gè)什么角色,起到了什么作用啊?,大家可以看一下這篇知乎上面的問(wèn)答。