很多時候,其實我們使用這個技術的時候,可能都是因為項目需要,所以,我們就用了,但是,至於為什么我們需要用到這個技術,可能自身並不是很了解的,但是,其實了解技術的來由及背景知識,對於理解一項技術還是有幫助的,那么,dubbo是怎么被提上日程的呢?
在互聯網的發展過程中,在以前,我們只需要一個服務器,將程序全部打包好就可以,但是,隨着流量的增大,常規的垂直應用架構已無法應對,所以,架構就發生了演變。
1 單一應用架構
2 應用和數據庫單獨部署
3 應用和數據庫集群部署
4 數據庫壓力變大,讀寫分離
5 使用緩存技術加快速度
6 數據庫分庫分表
7 應用分為不同的類型拆分
發展到這個階段的時候,我們發現,應用與應用之間的關系已經十分的復雜了,就會出現以下幾個問題(以下摘錄於官網):
① 當服務越來越多時,服務 URL 配置管理變得非常困難,F5 硬件負載均衡器的單點壓力也越來越大。
② 當進一步發展,服務間依賴關系變得錯蹤復雜,甚至分不清哪個應用要在哪個應用之前啟動,架構師都不能完整的描述應用的架構關系。
③ 接着,服務的調用量越來越大,服務的容量問題就暴露出來,這個服務需要多少機器支撐?什么時候該加機器?
為了解決這由於架構的演變所產生的問題幾個問題,於是,dubbo 產生了。當然,解決這個問題的技術不止 dubbo 。
從上面 Dubbo 的服務治理圖我們就可以看到,Duboo 很好了解決了上面所出現的一些問題。
所以,當你的系統架構發展到了這種階段的時候,就需要考慮使用 Dubbo 了。
二 Dubbo 技術架構
我們已經非常清楚的知道為什么在我們的系統中需要 Dubbo 這項技術了,下面,我們接着嘮叨嘮叨 Dubbo 的架構。
首先,上一張圖(摘自官網)。
看到圖之后,可能你對上面的幾個概念還是一臉懵逼,無從下手,下面,帶你看看這幾個角色到底是什么意思?
節點角色說明
看了這幾個概念后似乎發現,其實 Dubbo 的架構也是很簡單的(其實現細節是復雜的),為什么這么說呢,有沒有發現,其實很像 生產者-消費者 模型。只是在這種模型上,加上了 注冊中心和監控中心 ,用於管理提供方提供的 url ,以及管理整個過程。
那么,整個發布-訂閱的過程就非常的簡單了。
- 啟動容器,加載, 運行服務提供者 。
- 服務提供者在啟動時,在注冊中心 發布注冊 自己提供的 服務 。
- 服務消費者在啟動時,在注冊中心 訂閱 自己所需的 服務 。
如果考慮 失敗或變更 的情況,就需要考慮下面的過程。
- 注冊中心返回服務提供者地址列表給消費者,如果有變更,注冊中心將基於長連接推送變更數據給消費者。
- 服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一台提供者進行調用,如果調用失敗,再選另一台調用。
- 服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鍾發送一次統計數據到監控中心。
通過這番講解,我相信 Dubbo 的架構我們也輕車熟路了,那就直接入手,開車吧。
三 Dubbo 開始入門
終於走到這一步了,寫到這里停了大概一周的時間,主要原因還是最近項目太忙,趕着交差呢,今天希望能一鼓作氣,完完整整的寫完 dubbo 的基礎篇!
3.1 服務端
首先,我們先把服務端的接口寫好,因為其實 dubbo 的作用簡單來說就是給消費端提供接口。
接口定義
/** * xml方式服務提供者接口 */ public interface ProviderService { String SayHello(String word); }
這個接口非常簡單,只是包含一個 SayHello 的方法。
接着,定義它的實現類。
/** * xml方式服務提供者實現類 */ public class ProviderServiceImpl implements ProviderService{ public String SayHello(String word) { return word; } }
這樣我們就把我們的接口寫好了,那么我們應該怎么將我們的服務暴露出去呢?
導入 maven 依賴
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ouyangsihai</groupId> <artifactId>dubbo-provider</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/dubbo --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.6</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.10</version> </dependency> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.5</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.32.Final</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.8.0</version> </dependency> </dependencies> </project>
這里使用的 dubbo 的版本是 2.6.6
,需要注意的是,如果你只導入 dubbo 的包的時候是 會報錯 的, 找不到 netty 和 curator 的依賴 ,所以,在這里我們需要把這兩個的依賴加上,就不會報錯了。
另外,這里我們使用 zookeeper 作為注冊中心。
到目前為止,dubbo 需要的環境就已經可以了,下面,我們就把上面剛剛定義的接口暴露出去。
暴露接口(xml 配置方法)
首先,我們在我們項目的 resource 目錄下 創建 META-INF.spring 包 ,然后再創建 provider.xml 文件,名字可以任取哦,如下圖。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!--當前項目在整個分布式架構里面的唯一名稱,計算依賴關系的標簽--> <dubbo:application name="provider" owner="sihai"> <dubbo:parameter key="qos.enable" value="true"/> <dubbo:parameter key="qos.accept.foreign.ip" value="false"/> <dubbo:parameter key="qos.port" value="55555"/> </dubbo:application> <dubbo:monitor protocol="registry"/> <!--dubbo這個服務所要暴露的服務地址所對應的注冊中心--> <!--<dubbo:registry address="N/A"/>--> <dubbo:registry address="N/A" /> <!--當前服務發布所依賴的協議;webserovice、Thrift、Hessain、http--> <dubbo:protocol name="dubbo" port="20880"/> <!--服務發布的配置,需要暴露的服務接口--> <dubbo:service interface="com.sihai.dubbo.provider.service.ProviderService" ref="providerService"/> <!--Bean bean定義--> <bean id="providerService" class="com.sihai.dubbo.provider.service.ProviderServiceImpl"/> </beans>
① 上面的文件其實就是類似 spring 的配置文件,而且,dubbo 底層就是 spring。
② 節點:dubbo:application 就是整個項目在分布式架構中的唯一名稱,可以在 name
屬性中配置,另外還可以配置 owner
字段,表示屬於誰。 下面的參數是可以不配置的,這里配置是因為出現了端口的沖突,所以配置。
③ 節點:dubbo:monitor 監控中心配置, 用於配置連接監控中心相關信息,可以不配置,不是必須的參數。
④ 節點:dubbo:registry 配置注冊中心的信息,比如,這里我們可以配置 zookeeper 作為我們的注冊中心。 address
是注冊中心的地址,這里我們配置的是 N/A
表示由 dubbo 自動分配地址。或者說是一種直連的方式,不通過注冊中心。
⑤ 節點:dubbo:protocol 服務發布的時候 dubbo 依賴什么協議,可以配置 dubbo、webserovice、Thrift、Hessain、http等協議。
⑥ 節點:dubbo:service 這個節點就是我們的重點了,當我們服務發布的時候,我們就是通過這個配置將我們的服務發布出去的。 interface
是接口的包路徑, ref
是第 ⑦ 點配置的接口的 bean。
⑦ 最后,我們需要像配置 spring 的接口一樣,配置接口的 bean。
到這一步,關於服務端的配置就完成了,下面我們通過 main 方法
將接口發布出去。
發布接口
package com.sihai.dubbo.provider; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ProtocolConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.config.ServiceConfig; import com.alibaba.dubbo.container.Main; import com.sihai.dubbo.provider.service.ProviderService; import com.sihai.dubbo.provider.service.ProviderServiceImpl; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.io.IOException; /** * xml方式啟動 * */ public class App { public static void main( String[] args ) throws IOException { //加載xml配置文件啟動 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/spring/provider.xml"); context.start(); System.in.read(); // 按任意鍵退出 } }
發布接口非常簡單,因為 dubbo 底層就是依賴 spring 的,所以,我們只需要通過 ClassPathXmlApplicationContext
拿到我們剛剛配置好的 xml ,然后調用 context.start()
方法就啟動了。
看到下面的截圖,就算是啟動成功了,接口也就發布出去了。
你以為到這里就結束了了,並不是的,我們拿到 dubbo 暴露出去的 url 分析分析。
dubbo 暴露的 url
dubbo://192.168.234.1:20880/com.sihai.dubbo.provider.service.ProviderService?anyhost=true&application=provider&bean.name=com.sihai.dubbo.provider.service.ProviderService&bind.ip=192.168.234.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=com.sihai.dubbo.provider.service.ProviderService&methods=SayHello&owner=sihai&pid=8412&qos.accept.foreign.ip=false&qos.enable=true&qos.port=55555&side=provider×tamp=1562077289380
分析
① 首先,在形式上我們發現,其實這么牛逼的 dubbo 也是用 類似於 http 的協議 發布自己的服務的,只是這里我們用的是 dubbo 協議 。
② dubbo://192.168.234.1:20880/com.sihai.dubbo.provider.service.ProviderService
上面這段鏈接就是 ?
之前的鏈接,構成: 協議://ip:端口/接口 。發現是不是也沒有什么神秘的。
③ anyhost=true&application=provider&bean.name=com.sihai.dubbo.provider.service.ProviderService&bind.ip=192.168.234.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=com.sihai.dubbo.provider.service.ProviderService&methods=SayHello&owner=sihai&pid=8412&qos.accept.foreign.ip=false&qos.enable=true&qos.port=55555&side=provider×tamp=1562077289380
?
之后的字符串,分析后你發現,這些都是剛剛在 provider.xml
中配置的字段,然后通過 &
拼接而成的,聞到了 http
的香味了嗎?
終於,dubbo 服務端入門了。下面我們看看拿到了 url 后,怎么消費呢?
3.2 消費端
上面提到,我們在服務端提供的只是點對點的方式提供服務,並沒有使用注冊中心,所以,下面的配置也是會有一些不一樣的。
消費端環境配置
首先,我們在消費端的 resource 下建立配置文件 consumer.xml
。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!--當前項目在整個分布式架構里面的唯一名稱,計算依賴關系的標簽--> <dubbo:application name="consumer" owner="sihai"/> <!--dubbo這個服務所要暴露的服務地址所對應的注冊中心--> <!--點對點的方式--> <dubbo:registry address="N/A" /> <!--<dubbo:registry address="zookeeper://localhost:2181" check="false"/>--> <!--生成一個遠程服務的調用代理--> <!--點對點方式--> <dubbo:reference id="providerService" interface="com.sihai.dubbo.provider.service.ProviderService" url="dubbo://192.168.234.1:20880/com.sihai.dubbo.provider.service.ProviderService"/> <!--<dubbo:reference id="providerService" interface="com.sihai.dubbo.provider.service.ProviderService"/>--> </beans>
分析
① 發現這里的 dubbo:application
和 dubbo:registry
是一致的。
② dubbo:reference
:我們這里采用 點對點 的方式,所以,需要配置在服務端暴露的 url 。
maven 依賴
和服務端一樣
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ouyangsihai</groupId> <artifactId>dubbo-consumer</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>com.ouyangsihai</groupId> <artifactId>dubbo-provider</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/dubbo --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.6.6</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.10</version> </dependency> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.5</version> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.32.Final</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.8.0</version> </dependency> </dependencies> </project>
調用服務
package com.sihai.dubbo.consumer; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.sihai.dubbo.provider.service.ProviderService; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.io.IOException; /** * xml的方式調用 * */ public class App { public static void main( String[] args ) throws IOException { ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("consumer.xml"); context.start(); ProviderService providerService = (ProviderService) context.getBean("providerService"); String str = providerService.SayHello("hello"); System.out.println(str); System.in.read(); } }
這里和服務端的發布如出一轍。
如此,我們就成功調用接口了。
文章有不當之處,歡迎指正,如果喜歡微信閱讀,你也可以關注我的 微信公眾號 :
Java技術zhai
,獲取優質學習資源。免費Java高級資料需要自己領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高並發分布式等教程,一共40G。
傳送門: https://mp.weixin.qq.com/s/DHCu6AkF4nz4hNOv9c0IZg