以前,不少小夥伴私信我:如何才能快速的掌握Dubbo的核心原理和源碼。因此,我寫了一篇《我是如何在短時間內快速掌握Dubbo的原理和源碼的(純乾貨)?》。對於Dubbo的源碼解析系列文章,我也在思考如何讓源碼解析的文章變得更加簡單易懂,因此,我調整了寫Dubbo源碼解析文章的策略,力求讓小夥伴們可以以更簡單、易懂的方式完全掌握Dubbo源碼。今天,咱們先說說Dubbo中的統一契約是如何實現的。java
文章已收錄到:git
https://github.com/sunshinelyz/technology-binghegithub
https://gitee.com/binghe001/technology-bingheapache
URL全稱爲統一資源定位符,它可以在互聯網中定位到惟一的一個網絡地址。URL的格式以下所示。api
protocol://username:password@host:port/path?key=value&key=value
其中,各個部分的簡要說明以下所示。bash
這就是咱們互聯網中的URL的簡單說明。微信
那麼,在Dubbo內部,大量的方法接收的參數都是以URL進行封裝的,那麼,URL在Dubbo內部到底起到了什麼做用呢?咱們繼續往下看。網絡
總的來講,在Dubbo內部,服務提供者Provider會將自身的相關信息封裝成URL註冊到Zookeeper或其餘註冊中心中,從而對外暴露本身提供的服務。而服務消費者Consumer也會經過URL的形式向Zookeeper或其餘註冊中心訂閱本身想要調用的服務。而在Dubbo的SPI實現中,URL又會參與擴展實現的邏輯處理。因此說,URL在Dubbo的實現中是很是重要的。也能夠這麼說,Dubbo中的URL就是Dubbo的統一契約。app
咱們先來看一下Dubbo中的URL具體長什麼樣吧,經過調試Dubbo自帶Provider的示例源碼,咱們能夠看到在Dubbo中的URL以下所示。ide
dubbo://192.168.175.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=15012&release=&side=provider×tamp=1610857629484
這也是Provider註冊到Zookeeper或者其餘註冊中心的信息。各個部分的說明以下所示。
既然Dubbo是向Zookeeper或其餘註冊中心註冊這些信息的,那Dubbo內部是如何對URL進行封裝的呢。
在dubbo-common模塊中,有一個URL類專門用於封裝URL,以下所示。
在URL類中,咱們來看一個核心構造函數,以下所示。
public URL(String protocol, String username, String password, String host, int port, String path, Map<String, String> parameters, Map<String, Map<String, String>> methodParameters) { if (StringUtils.isEmpty(username) && StringUtils.isNotEmpty(password)) { throw new IllegalArgumentException("Invalid url, password without username!"); } this.protocol = protocol; this.username = username; this.password = password; this.host = host; this.port = Math.max(port, 0); this.address = getAddress(this.host, this.port); // trim the beginning "/" while (path != null && path.startsWith("/")) { path = path.substring(1); } this.path = path; if (parameters == null) { parameters = new HashMap<>(); } else { parameters = new HashMap<>(parameters); } this.parameters = Collections.unmodifiableMap(parameters); this.methodParameters = Collections.unmodifiableMap(methodParameters); }
能夠看到,Dubbo對於URL的核心封裝,基本與互聯網中的URL封裝是一致的。
在Dubbo的dubbo-common模塊提供了處理URL的工具類:URLBuilder和URLStrParser。以下所示。
這兩個類的實現仍是比較簡單的,小夥伴們能夠自行閱讀Dubbo的源碼。
接下來,咱們一塊兒來看看在Dubbo內部,URL是如何實現統一契約的?
這裏,咱們主要經過三方面來簡單聊聊URL在Dubbo內部的實際應用:
稍微瞭解過Dubbo的小夥伴都知道,Dubbo具備高度的可擴展性,而這種擴展性是基於Dubbo自身的SPI來實現的。在Dubbo實現的SPI中,URL又起到了很是重要的做用。
在Dubbo SPI的實現中,一個典型的場景就是被@Adaptive註解修飾的接口方法,例如,在dubbo-registry-api 模塊中的RegistryFactory接口中的getRegistry()方法上被@Adaptive({"protocol"})註解修飾。以下所示。
說明RegistryFactory接口中的getRegistry()方法是一個適配器方法,Dubbo在運行的過程當中,會爲getRegistry()方法動態生成RegistryFactory$Adaptive
類型。例如,生成的RegistryFactory$Adaptive
類型以下所示。
public class RegistryFactory$Adaptive implements RegistryFactory { public Registry getRegistry(org.apache.dubbo.common.URL arg0) { if (arg0 == null) throw new IllegalArgumentException(""); org.apache.dubbo.common.URL url = arg0; String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException(""); RegistryFactory extension = (RegistryFactory) ExtensionLoader.getExtensionLoader(RegistryFactory.class).getExtension(extName); return extension.getRegistry(arg0); } }
這段代碼相對來講仍是比較容易理解的,生成的RegistryFactory$Adaptive會自動實現getRegistry()方法,在getRegistry()方法中,會獲取URL中的protocol參數來肯定URL的協議,若是獲取的protocol爲空,則使用默認的dubbo協議,有了這個協議,就可以經過SPI動態加載具體的擴展實現類。
咱們在Dubbo的dubbo-registry-api模塊中找到RegistryProtocol類,以下所示。
找到其中的getRegistry()方法並打上斷點,以下所示。
接下來,debug啓動Dubbo的Provider示例,以下所示。
能夠看到,此時使用的protocol協議爲zookeeper。有關Dubbo中SPI的實現,咱們後面再詳細剖析,今天,小夥伴們有個大體的瞭解便可。
在Dubbo中的服務註冊實現中,URL一樣起到了很是重要的做用。這裏,我使用的註冊中心是Zookeeper,因此,咱們在dubbo-registry-zookeeper模塊中找到ZookeeperRegistry類,以下所示。
找到其中的doRegister()方法,打上斷點,以下所示。
debug啓動Dubbo自帶的provider示例,以下所示。
能夠看到,在註冊到Zookeeper中的URL中,包含了protocol協議、host主機名、port端口號、path請求路徑,parameters參數等信息。
Dubbo中服務的消費者Consumer在啓動時,會向Zookeeper註冊中心訂閱自身須要調用的服務,那具體是如何經過URL訂閱的呢?咱們一樣在dubbo-registry-zookeeper模塊中的ZookeeperRegistry類中找到doSubscribe()方法。在doSubscribe()方法中打上斷點,以下所示。
啓動Dubbo自帶的Consumer示例,以下所示。
咱們能夠看到,Dubbo的Consumer會向Zookeeper傳入以下參數進行服務的訂閱操做。
consumer://192.168.175.1/org.apache.dubbo.demo.DemoService?application=dubbo-demo-annotation-consumer&category=providers,configurators,routers&dubbo=2.0.2&init=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=15184&side=consumer&sticky=false×tamp=1610860963037
其中的protocol爲consumer,表示訂閱協議。category表示要訂閱的分類,這裏是providers,configurators,routers三個分類。interface表示要訂閱的接口服務,這裏是org.apache.dubbo.demo.DemoService。methods表示要訂閱的方法,這裏是sayHello,sayHelloAsync。
還有一點須要注意的是:在服務註冊的過程當中,Dubbo會將URL轉化爲Zookeeper路徑將信息註冊到Zookeeper中;在服務發現的過程當中,Dubbo會將URL轉化爲Zookeeper路徑,從而監聽Zookeeper目錄的變化來訂閱相關的服務。
總之,在Dubbo內部經過URL實現了統一的契約。你學會了嗎?
好了,今天就到這兒吧,我是冰河,你們有啥問題能夠在下方留言,也能夠加我微信:sun_shine_lyz,一塊兒交流技術,一塊兒進階,一塊兒牛逼~~