第一部分:Dubbo的背景分析及工作原理
1. Dubbo是什么?
Dubbo是一個來自阿里巴巴的開源分布式服務框架,致力於提供高性能和透明化的RPC遠程服務調用方案,以及SOA服務治理方案。
簡單的說,dubbo就是個服務框架,如果沒有分布式的需求,其實是不需要用的,只有在分布式的時候,才有dubbo這樣的分布式服務框架的需求,並且本質上是個服務調用的東東,說白了就是個遠程服務調用的分布式框架
其核心部分包含:
1. 遠程通訊: 提供對多種基於長連接的NIO框架抽象封裝,包括多種線程模型,序列化,以及“請求-響應”模式的信息交換方式。
2. 集群容錯: 提供基於接口方法的透明遠程過程調用,包括多協議支持,以及軟負載均衡,失敗容錯,地址路由,動態配置等集群支持。
3. 自動發現: 基於注冊中心目錄服務,使服務消費方能動態的查找服務提供方,使地址透明,使服務提供方可以平滑增加或減少機器。
2. Dubbo能做什么?
1.透明化的遠程方法調用,就像調用本地方法一樣調用遠程方法,只需簡單配置,沒有任何API侵入。
2.軟負載均衡及容錯機制,可在內網替代F5等硬件負載均衡器,降低成本,減少單點。
3. 服務自動注冊與發現,不再需要寫死服務提供方地址,注冊中心基於接口名查詢服務提供者的IP地址,並且能夠平滑添加或刪除服務提供者。
Dubbo采用全Spring配置方式,透明化接入應用,對應用沒有任何API侵入,只需用Spring加載Dubbo的配置即可,Dubbo基於Spring的Schema擴展進行加載。
3、Dubbox是什么?
當當根據自身的需求,為Dubbo實現了一些新的功能,包括REST風格遠程調用、Kryo/FST序列化等等。並將其命名為Dubbox(即Dubbo eXtensions)。
Dubbox基於非常成熟的JBoss RestEasy框架,在dubbo中實現了REST風格(HTTP + JSON/XML)的遠程調用,以顯著簡化企業內部的跨語言交互,同時顯著簡化企業對外的Open API、無線API甚至AJAX服務端等等的開發。
事實上,這個REST調用也使得Dubbo可以對當今特別流行的“微服務”架構提供基礎性支持。
另外,REST調用也達到了比較高的性能,在基准測試下,HTTP + JSON與Dubbo 2.x默認的RPC協議(即TCP + Hessian2二進制序列化)之間只有1.5倍左右的差距。
4、Dubbox 實現原理
作為一個分布式的服務框架,有幾個核心要點,以dubbox為例:
- 調用方式(Rest/RPC)
- 服務注冊 (zookeeper)
- 服務發現 (zookeeper)
- 序列化/反序列化 (dubbo/hessian2/fastjson/Kryo/FST等)
- 負載均衡

第二部分:Dubbox的應用實踐
1、環境搭建
你可以把Dubbox理解成一個可插拔的服務組件,其依賴的中間件只有Zookeeper,它主要負責是服務注冊與服務發現,起到一個中間橋梁的作用,所以Dubbox環境搭建比較簡單,只需要一個zookeeper或zookeeper集群環境即可。
zookeeper不是本文的重點,所以不作詳細講解,關於zookeeper的安裝和集群配置,可參考:
官方網站:https://zookeeper.apache.org/
安裝示例:http://www.cnblogs.com/xxx0624/p/4168440.html
集群示例:http://www.cnblogs.com/huangxincheng/p/5654170.html
2、項目中的jar包依賴
生產者和消費者都需要引入dubbox相關依賴包,maven配置:
<!-- dubbo --> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>2.8.5-SNAPSHOT</version> </dependency> <!-- 序列化 --> <dependency> <groupId>com.esotericsoftware.kryo</groupId> <artifactId>kryo</artifactId> <version>2.24.0</version> </dependency>
<dependency> <groupId>de.javakaffee</groupId> <artifactId>kryo-serializers</artifactId> <version>0.26</version> </dependency> <!--zookeeper --> <dependency> <groupId>com.alibaba</groupId> <artifactId>zkclient</artifactId> <version>0.8.1</version> </dependency> <dependency> <groupId>net.dubboclub</groupId> <artifactId>netty4</artifactId> <version>0.0.6</version> </dependency> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.4.9</version> </dependency>
2、生產者(服務注冊)
服務注冊比較簡單,只需要一個dubbox的配置文件,在服務進程啟動的時候加載它,就可以完成服務注冊,以spring配置為例:
springDubbo.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="myapp" owner="myapp" /> <dubbo:protocol name="dubbo" port="20889" threads="500" accepts="500" keepalive="true" serialization="kryo" /> <!-- 使用zookeeper注冊中心暴露服務地址 --> <dubbo:registry protocol="zookeeper" address="zookeeper://192.168.15.5:2189?backup=192.168.15.6:2189,192.168.15.9:2189" check="true" subscribe="false" register="true" /> <dubbo:provider group="corp" /> <dubbo:service interface="com.yhouse.modules.services.ITestService" ref="testServiceImpl" id="testService" timeout="1200000" protocol="dubbo" /> </beans>
配置文件中的標簽作一個簡要的說明:
- dubbo:application 應用配置,用於配置當前應用信息,不管該應用是提供者還是消費者
- dubbo:protocol 協議配置,用於配置提供服務的協議信息,協議由提供方指定,消費方被動接受,這里我們采用dubbo協議,當然也可以修改成其他如rmi、hessian等。
- dubbo:registry 注冊中心配置,用於配置連接注冊中心相關信息。
- dubbo:provider 提供方的缺省值,當ProtocolConfig和ServiceConfig某屬性沒有配置時,采用此缺省值,可選,在一些特定的場景很有用,如多個環境服務重疊時,可使用它來區分是哪個環境的服務,簡單的可以理解為分組的概念。
- dubbo:service 服務配置,用於暴露一個服務,定義服務的元信息,一個服務可以用多個協議暴露,一個服務也可以注冊到多個注冊中心。
從上面的配置文件可以看出,我們注冊了一個服務:testService。
定義接口與實現類:

package com.yhouse.modules.services; public interface ITestService { public String getName(); }
package com.yhouse.modules.services; import org.springframework.stereotype.Service; @Service("testServiceImpl") public class TestServiceImpl implements ITestService { @Override public String getName() { // TODO Auto-generated method stub return "hello"; } }
啟動進程(jetty),終端打印信息:
從上面顏色標深的三行,可以看出服務已經注冊成功。
服務示例使用的環境: java web / jetty容器 / spring4 ,根據你的實際情況選擇,如jetty換成tomcat,或者使用dubbo提供的單獨啟動進程的方式:
public static void main(String[] args) {
com.alibaba.dubbo.container.Main.main(args);
}
3、消費者(服務發現和調用)
從上面的生產者示例開發,消費端其實也很簡單,過程一樣,只不過角色換成了服務調用方,首先來看配置文件,看有哪些變化:

<?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="consumerTest" owner="consumerTest" /> <!-- 加入監控 --> <dubbo:monitor protocol="registry" /> <!-- 使用zookeeper注冊中心暴露服務地址 --> <dubbo:registry protocol="zookeeper" address="zookeeper://192.168.15.5:2189?backup=192.168.15.6:2189,192.168.15.9:2189" check="true" subscribe="true" register="true" /> <dubbo:reference check="false" interface="com.yhouse.modules.services.ITestService" id="testService" group="corp"/> </beans>
- dubbo:application 應用配置,用於配置當前應用信息,不管該應用是提供者還是消費者
- dubbo:registry 注冊中心配置,用於配置連接注冊中心相關信息。
- dubbo:reference 引用服務配置,用於創建一個遠程服務代理,一個引用可以指向多個注冊中心,后面有一個group,其實就是生產者的 <dubbo:provider group="corp" />
注意:生產者與消費者都引用了 com.yhouse.modules.services.ITestService 這個接口,在實際當中不可能在生產者定義一次,又到消費端來定義一次吧,具體的做法可以這樣:
把對外的接口、接口參數(如果是對象)、接口返回(如果是對象)抽出一個公共層,生產者與浪費者只需要引用這個公共層即可。
下面用一個簡單的測試用例調用dubbox服務:
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.yhouse.modules.services.ITestService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:/spring/dubbox.xml" }, inheritLocations = true) public class TestServiceCall { @Autowired private ITestService testService; @Test public void dubboCallTest(){ System.out.println(testService.getName()); } }
控制台輸出:
從結果看出,dubbox服務可以正常調通,是不是很簡單?
在實際應用當中,dubbox還有很多功能可以使用,可以適應不同的應用場景,本文僅作為一個入門級應用示范。
第三部分:配置中一些重要特性講解
1、服務調用超時設置
上圖中以timeout為例,顯示了配置的查找順序,其它retries, loadbalance, actives也類似。
方法級優先,接口級次之,全局配置再次之。如果級別一樣,則消費方優先,提供方次之。
其中,服務提供方配置,通過URL經由注冊中心傳遞給消費方。
建議由服務提供方設置超時,因為一個方法需要執行多長時間,服務提供方更清楚,如果一個消費方同時引用多個服務,就不需要關心每個服務的超時設置。
理論上ReferenceConfig的非服務標識配置,在ConsumerConfig,ServiceConfig, ProviderConfig均可以缺省配置。
2、啟動時檢查
Dubbo缺省會在啟動時檢查依賴的服務是否可用,不可用時會拋出異常,阻止Spring初始化完成,以便上線時,能及早發現問題,默認check=true。
如果你的Spring容器是懶加載的,或者通過API編程延遲引用服務,請關閉check,否則服務臨時不可用時,會拋出異常,拿到null引用,如果check=false,總是會返回引用,當服務恢復時,能自動連上。
可以通過check="false"關閉檢查,比如,測試時,有些服務不關心,或者出現了循環依賴,必須有一方先啟動。
<!-- 關閉某個服務的啟動時檢查:(沒有提供者時報錯) --> <dubbo:reference interface="com.foo.BarService" check="false" /> <!-- 關閉所有服務的啟動時檢查:(沒有提供者時報錯) 寫在定義服務消費者一方 --> <dubbo:consumer check="false" /> <!-- 關閉注冊中心啟動時檢查:(注冊訂閱失敗時報錯) --> <dubbo:registry check="false" />
引用缺省是延遲初始化的,只有引用被注入到其它Bean,或被getBean()獲取,才會初始化。
如果需要飢餓加載,即沒有人引用也立即生成動態代理,可以配置:
<dubbo:reference interface="com.foo.BarService" init="true" />
3、訂閱
問題:為方便開發測試,經常會在線下共用一個所有服務可用的注冊中心,這時,如果一個正在開發中的服務提供者注冊,可能會影響消費者不能正常運行。
解決文案:可以讓服務提供者開發方,只訂閱服務(開發的服務可能依賴其它服務),而不注冊正在開發的服務,通過直連測試正在開發的服務。
<!-- 禁用注冊配置:--> <dubbo:registry address="10.20.153.10:9090" register="false" /> <!-- 或者:--> <dubbo:registry address="10.20.153.10:9090?register=false" />
4、回聲測試(測試服務是否可用)
回聲測試用於檢測服務是否可用,回聲測試按照正常請求流程執行,能夠測試整個調用是否通暢,可用於監控。
所有服務自動實現EchoService接口,只需將任意服務引用強制轉型為EchoService,即可使用。
<dubbo:reference id="memberService" interface="com.xxx.MemberService" /> MemberService memberService = ctx.getBean("memberService"); // 遠程服務引用 EchoService echoService = (EchoService) memberService; // 強制轉型為EchoService String status = echoService.$echo("OK"); // 回聲測試可用性 assert(status.equals("OK"))
5、延遲連接
延遲連接,用於減少長連接數,當有調用發起時,再創建長連接。
只對使用長連接的dubbo協議生效。
<dubbo:protocol name="dubbo" lazy="true" />
6、令牌驗證
防止消費者繞過注冊中心訪問提供者,在注冊中心控制權限,以決定要不要下發令牌給消費者,注冊中心可靈活改變授權方式,而不需修改或升級提供者
<!-- 1、全局設置開啟令牌驗證:隨機token令牌,使用UUID生成 --> <dubbo:provider interface="com.foo.BarService" token="true" /> <!-- 固定token令牌,相當於密碼--> <dubbo:provider interface="com.foo.BarService" token="123456" />
<!-- 2、服務級別設置開啟令牌驗證: 隨機token令牌,使用UUID生成 --> <dubbo:service interface="com.foo.BarService" token="true" /> <!--固定token令牌,相當於密碼--> <dubbo:service interface="com.foo.BarService" token="123456" />
<!-- 3、協議級別設置開啟令牌驗證:隨機token令牌,使用UUID生成--> <dubbo:protocol name="dubbo" token="true" /> <!--固定token令牌,相當於密碼--> <dubbo:protocol name="dubbo" token="123456" />
7、日志適配
缺省自動查找:log4j、slf4j、jcl、jdk
可以通過以下方式配置日志輸出策略:<dubbo:application logger="log4j"/>
訪問日志:如果你想記錄每一次請求信息,可開啟訪問日志,類似於apache的訪問日志。此日志量比較大,請注意磁盤容量。
將訪問日志輸出到當前應用的log4j日志:
<dubbo:protocol accesslog="true" />
將訪問日志輸出到指定文件:
<dubbo:protocol accesslog="http://192.168.15.9/wiki/foo/bar.log" />
8、配置Dubbo緩存文件
配置方法如下:
<dubbo:registryfile=”${user.home}/output/dubbo.cache” />
注意:
文件的路徑,應用可以根據需要調整,保證這個文件不會在發布過程中被清除。如果有多個應用進程注意不要使用同一個文件,避免內容被覆蓋。
這個文件會緩存:
- 注冊中心的列表
- 服務提供者列表
有了這項配置后,當應用重啟過程中,Dubbo注冊中心不可用時則應用會從這個緩存文件讀取服務提供者列表的信息,進一步保證應用可靠性。
