DUBBO功能使用說明
1 DUBBO概述
DUBBO是阿里巴巴公司的一個分布式服務框架,致力於提供高性能和透明化的RPC遠程服務調用方案,以及SOA服務治理方案。
相比於其他服務框架,DUBBO有如下優勢:
v 透明化的遠程方法調用,就像調用本地方法一樣調用遠程方法,只需簡單配置,沒有任何API侵入;
v 軟負載均衡及容錯機制,可在內網替代F5等硬件負載均衡器,降低成本,減少單點;
v 服務自動注冊與發現,注冊中心基於接口名查詢服務提供者的IP地址,並且能夠平滑添加或刪除服務提供者。
2 DUBBO功能說明
2.1 服務間相互調用
2.1.1 功能說明
一個完整的業務流程往往涉及到多個服務,通過多個服務的組合完成特定的業務邏輯。
2.1.2 應用場景
服務的組合實現有兩種方式,一種為在web的controller層實現,一種是通過服務間調用來實現。
Web層多次調用
服務間調用
以上圖為例,存在兩個service,顧客service和商品service,如果這兩個服務分布在不同的物理機上,則上面兩種方式的網絡消耗是一致的。但是如果兩個服務在同一台物理機上,則服務間調用則比web層多次調用少了一次遠程調用。
2.1.3 操作步驟
1) 消費方項目引入服務提供方的api jar包
2)在dubbo服務引用配置文件中添加對應的標簽
<dubbo:reference
id="accountService" interface="com.ibm.netbank.store.service.IAccountService">
</dubbo:reference>
3)在CustomerService實現類中添加成員變量,並添加@AutoWired注解讓spring自動完成該service的注入。
@Autowired
public IAccountService accountService;
4)代碼中調用
accountService.getAccount(name);
2.1.4 注意事項
1、核心服務下沉,形成各個獨立的服務中心,盡量避免服務中心間的調用。
2、服務中心的形成需要業務治理輔助
3、需要建立需要依賴專門的服務治理工具,完成以下功能
a) 管理服務間的依賴關系,避免互相依賴及環狀調用的情況
b) 計算服務的關鍵路徑,確定服務的啟動順序
建議規范:
1、引入服務間調用后,服務提供方也湊多了服務消費者的角色,建議配置服務提供與服務引用的配置文件分離,分別命名為服務名-provider.xml及服務名-consumer.xml。
2.2 服務異步調用
2.2.1 功能說明
非阻塞實現並行調用,客戶端不需要啟動多線程即可完成並行調用多個遠程服務。
2.2.2 應用場景
1、一個業務邏輯中包含多個並行耗時服務調用
2、一個業務邏輯中存在操作時間較長,但后續步驟不依賴於該步驟完成的步驟(如客戶下單購買產品后,需要更新產品的點擊排名以及客戶的使用習慣追蹤等)
2.2.3 操作步驟
1)在AccountService中模擬兩個耗時操作,分別計算用戶美元賬戶與人民幣賬戶的收益
public double calcuDollarBenifit() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 6000;
}
public double calcuRmbBenifit() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1000;
}
2)spring-multiservices-provider.xml中暴露AccountService服務
<dubbo:service interface="com.ibm.netbank.multiservices.service.IAccountService" ref="accountService"/>
3)消費者方在consumer.xml中引用Account服務,針對兩個耗時調用聲明為異步調用,並根據自身需求設置timeout時間。
<dubbo:reference
id="accountService" interface="com.ibm.netbank.multiservices.service.IAccountService" url="dubbo://127.0.0.1:20888">
<dubbo:method name="calcuDollarBenifit" async="true" timeout="10000"/>
<dubbo:method name="calcuRmbBenifit" async="true" timeout="10000"/>
</dubbo:reference>
4)消費者通過調用實現業務邏輯,本例中美元賬戶收益計算耗時5s,人民幣賬戶收益計算耗時2s,異步並發調用后,5s即可返回結果。
public double getBenifitAsyn() {
accountService.calcuDollarBenifit();
Future<Double> dollarFuture = RpcContext.getContext().getFuture();
accountService.calcuRmbBenifit();
Future<Double> rmbFuture = RpcContext.getContext().getFuture();
double total = 0.0;
try {
double dollar = dollarFuture.get();
double rmb = rmbFuture.get();
total = dollar+rmb;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return total;
}
2.2.4 注意事項
1、需要根據實際業務情況設置超時,否則會拋出timeoutException
2、異步的調用具有傳遞性,如下圖,A服務聲明通過異步方式調用B服務,B服務聲明通過同步方式調用C服務,但實際調用過程中B調用C服務時也會立刻返回Null。
3、如在統計等場景下,完全忽略返回值,可以配置return="false",以減少Future對象的創建和管理成本
<dubbo:method name="findFoo" async="true" return="false" />
2.3 集群容錯配置
2.3.1 功能說明
當集群調用失敗時,可以選擇不同的容錯方案,缺省值為failover
2.3.2 應用場景
參數應用說明:
1.failover:失敗自動切換,當出現失敗,重試其它服務器。
通常用於讀操作,但重試會帶來更長延遲。
2.failfast:快速失敗,只發起一次調用,失敗立即報錯。
通常用於非冪等性的寫操作,比如新增記錄。
3.failsafe:失敗安全,出現異常時,直接忽略。
通常用於寫入審計日志等操作。
4.failback:失敗自動恢復,后台記錄失敗請求,定時重發。
通常用於消息通知操作。
4.forking:並行調用多個服務器,只要一個成功即返回。
通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。
可通過forks="2"來設置最大並行數。
4.broadcast:廣播調用所有提供者,逐個調用,任意一台報錯則報錯。
通常用於通知所有提供者更新緩存或日志等本地資源信息。
如果沒有特殊需求,推薦使用failover
2.3.3 操作步驟
容錯方式可以在Consumer或者Provider端進行配置,Consumer端配置優先於Provider端,
推薦在Provider端進行配置,因為服務提供者比服務消費者更清楚服務的性能參數
Provider端配置示例:
<dubbo:service interface="xxx" cluster="failover" />
Consumer端配置示例:
<dubbo:reference interface="xxx" cluster="failover" />
2.3.4 注意事項
1.其他與集群容錯相關的參數說明:
1)timeout: 方法調用超時時間,缺省為1000
2)retries: 失敗重試次數,缺省為2(表示加上第一次調用,會調用3次)
3)actives: Consumer端最大並發調用限制,當Consumer對一個服務的並發調用到上限后,新調用會Wait直到超時
2.failfast及failsafe都只發起一次調用,failfast失敗會在Consumer端拋出異常,failsafe失敗會被直接忽略,並在Consumer端返回null值
2.4 負載均衡配置
2.4.1 功能說明
在調用服務時,Dubbo提供了多種軟負載均衡策略,缺省為random隨機調用
2.4.2 應用場景
參數應用說明:
1.Random LoadBalance:
隨機,按權重設置隨機概率。
在一個截面上碰撞的概率高,但調用量越大分布越均勻,而且按概率使用權重后也比較均勻,有利於動態調整提供者權重。
2.RoundRobin LoadBalance:
輪循,按公約后的權重設置輪循比率。
存在慢的提供者累積請求問題,比如:第二台機器很慢,但沒掛,當請求調到第二台時就卡在那,久而久之,所有請求都卡在調到第二台上。
3.LeastActive LoadBalance:
最少活躍調用數,相同活躍數的隨機,活躍數指調用前后計數差。
使慢的提供者收到更少請求,因為越慢的提供者的調用前后計數差會越大。
4.ConsistentHash LoadBalance:
一致性Hash,相同參數的請求總是發到同一提供者。
當某一台提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。
如果沒有特殊需求,推薦使用random隨機調用
2.4.3 操作步驟
負載均衡策略,可以在Consumer或者Provider端進行配置,並可以選擇針對接口或者方法進行配置,配置的覆蓋規則:
1) 方法級配置別優於接口級別 2) Consumer端配置優於Provider配置
推薦在Provider端進行配置,因為服務提供者比服務消費者更清楚服務的性能參數
針對服務配置示例:
Provider端: <dubbo:service interface="xxx" loadbalance="random" />
Consumer端: <dubbo:reference interface="xxx" loadbalance="random" />
針對方法配置示例:
Provider端: <dubbo:service interface="xxx" >
<dubbo:method name=”xxx” loadbalance=”random”/>
</dubbo:service>
Consumer端: <dubbo:reference interface="xxx" >
<dubbo:method name=”xxx” loadbalance=”random”/>
</dubbo:reference>
2.4.4 注意事項
如果選擇random或者roundrobin,可以通過weight值設置服務權重,默認為100,此參數通常用於容量評估,配置示例:
<dubbo:service interface="xxx" weight="1000" />
2.5 服務分組
2.5.1 功能說明
主要是通過在服務提供端和服務消費端的服務聲明配置文件中分別聲明group的屬性,從而達到對於同一個接口具有不同實現的情況下,對不同實現進行區分調用的目的。
2.5.2 應用場景
服務分組的功能主要是應用於對同一個接口存在多種不同的實現的場景下。
2.5.3 操作步驟
1. 在服務提供端服務聲明的配置文件中(配置文件所在位置按照當前的目錄結構應為src/main/resources/META-INF/spring/spring-xx.xml文件)增加group配置選項,如下所示:
<dubbo:service interface="com.ibm.netbank.demo.service.IPersonService" ref="personService" version="2.0" group="zh" />
<dubbo:service interface="com.ibm.netbank.demo.service.IPersonService" ref="personServiceNew" version="2.0" group="en" />
2. 在服務消費端接口聲明的配置文件中(配置文件所在位置按照當前的目錄結構應為src/main/resources/dubboContext.xml)增加group配置選項,並通過group配置選項指定要調用的接口實現組,如下所示:
<dubbo:reference id="personService" interface="com.ibm.netbank.demo.service.IPersonService" version="2.0" group="en" />
2.5.4 注意事項
服務進行分組后,可以通過DUBBO自帶的telnet工具(注意DUBBO提供的端口為20880)查看服務的運行情況,如上例中配置了兩個group的服務實現,則通過DUBBO的ls -l命令可以看到實現了同一個接口的這兩個服務的情況,如下所示:
com.ibm.netbank.demo.service.IPersonService -> dubbo://10.232.98.185:20880/c
om.ibm.netbank.demo.service.IPersonService?anyhost=true&application=netbank-
sample-service&dubbo=2.5.3&group=zh&interface=com.ibm.netbank.demo.service.I
PersonService&methods=deletePerson,checkName,getAllPeople,seachPeopleByName,getP
ersonById,addPerson,updatePerson&pid=2644&revision=2.0&side=provider×tamp=1
381819869792&version=2.0
com.ibm.netbank.demo.service.IPersonService -> dubbo://10.232.98.185:20880/c
om.ibm.netbank.demo.service.IPersonService2?anyhost=true&application=netbank
-sample-service&dubbo=2.5.3&group=en&interface=com.ibm.netbank.demo.service.
IPersonService&methods=deletePerson,checkName,getAllPeople,seachPeopleByName,get
PersonById,addPerson,updatePerson&pid=2644&revision=2.0&side=provider×tamp=
1381819870822&version=2.0
2.6 服務多版本
2.6.1 功能說明
主要是通過在服務提供端和服務消費端的服務聲明配置文件中分別聲明version的屬性,從而達到可以調用不同版本服務的目的。
2.6.2 應用場景
服務多版本的功能可以應用於如下場景:
1. 在本地測試中,為避免使用組播造成在修改同一個服務時的交叉調用,可以使用版本號進行區分;
2. 在針對同一個接口出現不兼容的升級時,可以采用版本號的方式實現服務的按批次升級。
2.6.3 操作步驟
3. 在服務提供端服務聲明的配置文件中(配置文件所在位置按照當前的目錄結構應為src/main/resources/META-INF/spring/spring-xx.xml文件)增加version配置選項,如下所示:
<dubbo:service interface="com.ibm.netbank.demo.service.IPersonService" ref="personService" version="2.0" />
4. 在服務消費端接口聲明的配置文件中(配置文件所在位置按照當前的目錄結構應為src/main/resources/dubboContext.xml)增加version配置選項,如下所示:
<dubbo:reference id="personService" interface="com.ibm.netbank.demo.service.IPersonService" version="2.0" />
2.6.4 注意事項
1. 若提供端和消費端版本不一致,則會如下版本不一致錯誤:
Failed to check the status of the service com.ibm.netbank.demo.service.IPersonService. No provider available for the service com.ibm.netbank.demo.service.IPersonService:2.0.1 from the url multicast://224.5.6.11:1234/com.alibaba.dubbo.registry.RegistryService?application=netbank-sample-web&dubbo=2.5.3&interface=com.ibm.netbank.demo.service.IPersonService&methods=deletePerson,checkName,getAllPeople,seachPeopleByName,getPersonById,addPerson,updatePerson&pid=6840&revision=0.0.1-SNAPSHOT&side=consumer×tamp=1381540308102&version=2.0.1 to the consumer 10.232.98.185 use dubbo version 2.5.3
當出現上述問題時,可以通過DUBBO自帶的telnet工具(注意DUBBO提供的端口為20880),並通過ls -l命令來查看當前服務的詳細信息,其中包括服務的版本信息,如下所示:
com.ibm.netbank.demo.service.IPersonService -> dubbo://10.232.98.185:20880/c
om.ibm.netbank.demo.service.IPersonService?anyhost=true&application=netbank
sample-service&dubbo=2.5.3&interface=com.ibm.netbank.demo.service.IPersonSer
vice&methods=deletePerson,checkName,getAllPeople,seachPeopleByName,getPersonById
,addPerson,updatePerson&pid=7672&revision=2.0.0&side=provider×tamp=13815399
77658&version=2.0.0
2. 針對服務的版本號,建議使用兩位編碼樣式,如1.0、2.1,在生產環境中,通常在出現版本不兼容的情況下才使用序列號,另外需要注意的是,在不使用version屬性時,系統會將服務版本缺省賦予0.0.0的版本號。
2.7 結果緩存
2.7.1 功能說明
結果緩存是指方法的結果被存儲到緩存,當下次相同參數的方法調用時返回緩存的結果。
當目標方法執行時,架構會檢查指定參數的方便是否已經被執行過,如果沒有則執行,並緩存結果返回;否則直接返回緩存結果並不執行方法。
Dubbo提供lru, threadlocal, jcache三種類型的緩存。
根據http://code.alibabatech.com/jira/browse/DUBBO-241 ,http://www.99jianzhu.com 曾經還存在其他可擴展緩存類型:lru, threadlocal, jcache, ehcache, oscache等。並能夠通過RpcContext.getContext().clear();清除本次調用的緩存。
但在代碼中僅見lru, threadlocal, jcache三種緩存的代碼。jcache 與JSR107集成,可以橋接各種緩存實現,緩存類型可擴展。參見:CacheFactory擴展點。
2.7.2 應用場景
適用於參數相同時,方法執行結果不會變的方法。
LRU緩存如字面Least Recent Used所示,設計為在緩存滿時刪除最長時間未使用的緩存,保持最熱的數據被緩存。
Threadlocal緩存是當前線程緩存,通過ThreadLocal變量為各線程隔離分別存儲,用於同一線程內的變量緩存。Threadlocal緩存使用Map存儲,未設定最大緩存量值。
2.7.3 配置方法
與Spring相比,Dubbo未實現@Cacheable和@Caching等基於注解的聲明,而是使用XML聲明。
在聲明服務的配置文件中為所需緩存的服務或方法增加cache配置選項,如下所示:
標簽 |
屬性 |
類型 |
是否必填 |
作用 |
描述 |
兼容性 |
<dubbo:reference> |
cache |
string/boolean |
可選 |
服務治理 |
以調用參數為key,緩存返回結果,可選:lru, threadlocal, jcache等 |
Dubbo2.1.0及其以上版本支持 |
<dubbo:consumer> |
cache |
string/boolean |
可選 |
服務治理 |
以調用參數為key,緩存返回結果,可選:lru, threadlocal, jcache等 |
Dubbo2.1.0及其以上版本支持 |
在Dubbo服務注冊后,其中提供方和消費方均可對緩存進行配置。
如消費方可:
<dubbo:reference interface="com.foo.BarService" cache="lru" />
也可:
<dubbo:reference interface="com.foo.BarService">
<dubbo:method name="findBar" cache="lru" />
</dubbo:reference>
服務方也可:
<dubbo:service interface="com.foo.BarService" ref="BarService" cache="lru"/>
根據http://code.alibabatech.com/jira/browse/DUBBO-241
曾經可以使用 cache-size="100" 來配置LRU緩存大小,但看起來為了和其他接口統一,已經取消,大小配置已不可用。如確需修改緩存大小,可考慮修改common.Utils.LRUCache代碼。
使用方法可參照dubbo-master\dubbo-test\dubbo-test-examples的cache樣例。
2.7.4 注意事項
1、support.lru.LruCache默認緩存大小為1000,但僅提供是否過大的removeEldestEntry()函數,而未自我動態維護,也未存儲順序(用Map緩存)。實際使用的是common.Utils.LRUCache,它使用LinkedHashMap維護隊列。
2、cache="true"和cache="lru"有着相同的效果。
3、通過實驗發現,在客戶端和服務端只要有一端配置即可,當這兩個配置不同時,以cache-consumer端配置的為准。
4、Dubbo緩存文件
緩存文件配置如:<dubbo:registry file=”${user.hoome}/output/dubbo.cache”>
默認會在用戶文件夾\.dubbo\下根據廣播地址分文件注冊,如C:\Users\DELL\.dubbo\dubbo-registry-224.5.6.7.cache。
這個文件會緩存:注冊中心的列表、服務提供者列表。可供察看驗證。
應當注意:文件的路徑,應用可以根據需要調整,保證這個文件不會在發布過程中被清除。如果有多個應用進程注意不要使用同一個文件,避免內容被覆蓋。
有了這項配置后,當應用重啟過程中,Dubbo注冊中心不可用時,應用會從這個緩存文件讀取服務提供者列表的信息,進一步保證應用可靠性。
2.8 隱式傳參
2.8.1 功能說明
隱式傳參用於在服務方和消費方之間進行參數傳遞。在通過RPC調用服務時,通過Invoker將RpcContext的Attachment變量傳輸到被調用方,以供服務端使用。
2.8.2 應用場景
隱式傳參用於在服務方和消費方之間進行參數傳遞。特別是不宜明文傳輸參數的時候,或傳遞數量較大的時候。
2.8.3 操作步驟
1 一方在調用前使用setAttachment()設置參數,
2 調用以保證雙方在同一RPCContext下
3 另一方即可使用getAttachment()獲取參數。
{ RpcContext.getContext().setAttachment("index", "1"); // 隱式傳參,后面的遠程調用都會隱式將這些參數發送到服務器端,類似cookie,用於框架集成,不建議常規業務使用 xxxService.xxx(); // 遠程調用 } …… { String index = RpcContext.getContext().getAttachment("index"); // 獲取客戶端隱式傳入的參數 } |
2.8.4 注意事項
1、RpcContext狀態
RpcContext是一個ThreadLocal的臨時狀態記錄器,當接收到RPC請求,或發起RPC請求時,RpcContext的狀態都會變化。比如:A調B,B再調C,則B機器上,在B調C之前,RpcContext記錄的是A調B的信息,在B調C之后,RpcContext記錄的是B調C的信息。
2、禁用的特殊Key
path,group,version,dubbo,token,timeout幾個key有特殊處理,如在invoke時會清空、重設等。如果客戶端錯配了這些參數會影響服務的執行或無法取到希望的結果。
清空位置:com.alibaba.dubbo.rpc.filter.ContextFilter
如確需訪問這些,在直接獲取不到時可考慮如下不規范的方法獲取。
示例:
String token = RpcContext.getContext().getInvocation().getAttachements().get("token");
3、節約網絡負載
RPCContext的attachments存儲在HashMap<String, String> 中,setAttachment設置的KV,在完成下面一次遠程調用前會被清空。即多次遠程調用要多次設置。網絡傳輸負載較大,且客戶端可以任意設置參數,服務端無法控制數量。客戶端應自覺減少設置服務端不需要的參數。
清空位置: dubbo/dubbo-rpc/dubbo-rpc-api/src/main/java/com/alibaba/dubbo/rpc/filter/ConsumerContextFilter.java
AbstractInvoker拷貝attachments曾經很慢。
根據http://code.alibabatech.com/jira/browse/DUBBO-444 ,曾有一個版本,當並發很高時,有很多線程都在attachments拷貝。
現已改進。
4、在服務中僅設置局部變量
Attachment不能用於service的類變量、實例變量設置,僅能用於局部變量。否則在多個consumer利用同一service時,多線程設置同一實例的變量時會互相影響。
如:
Consumer不停將index設置為1-100,Consumer2不停將index設置為100-200。
Consumer:
AttachmentService attachmentService = (AttachmentService) context.getBean("AttachmentService"); while(true) { for(int i = 0; i < 100; i++) { RpcContext.getContext().setAttachment("index", (String.valueOf(i))); System.out.println("index is " + attachmentService.getAtt()); } } |
Service:
public class AttachmentServiceImpl implements AttachmentService{ String index = "origin"; //應改為局部變量 public String getAtt() { if(RpcContext.getContext().isProviderSide()) { if(RpcContext.getContext().getAttachment("index") != null) { index = (String)RpcContext.getContext().getAttachment("index"); System.out.println(" New index " + " from " + RpcContext. getContext(). getRemoteAddressString() + "is set to " + index); } return index; } return "not ProviderSide"; } } |
同時運行Consumer和Consumer2,由於index為實例變量,它們向service設置的Attachment會互相混淆。表現為consumer顯示1-100與100-200的數字交替出現。
2.9 事件通知
2.9.1 功能說明
在調用之前,調用之后,出現異常時,會觸發oninvoke, onreturn, onthrow三個事件,可以配置當事件發生時,通知哪個類的哪個方法。
2.9.2 應用場景
在消費者端實現,對服務方透明,且可與主業務邏輯分開,可用來實現一些非核心業務或者統計需求。
2.9.3 操作步驟
1、實現事件通知類
class DemoNofifyImpl {
public void onreturn(List<Person> msg) {
System.out.println("onreturn:" + msg);
}
public void onreturn2(Person person,Long id) {
System.out.println("onreturn2:" + person.getName());
}
public void onthrow(Throwable ex) {
System.out.println("onethrow"+ex.toString());
}
public void oninvoke(List<Person> msg) {
System.out.println("oninvoke");
}
}
2、服務方consumer.xml中增加事件通知類的bean標簽與需要事件通知的方法
<dubbo:reference
id="personService" interface="com.ibm.netbank.demo.service.IPersonService" >
<dubbo:method name="getAllPeople" async="false" onreturn ="demoCallback.onreturn" onthrow="demoCallback.onthrow"/>
<dubbo:method name="getPersonById" async="true" onreturn ="demoCallback.onreturn2" timeout="5000"/>
</dubbo:reference>
2.9.4 注意事項
1、onreturn onthrow 可用,oninvoke使用時spring報錯,無法完成注入
2、在定義回調處理方法時,需要按照以下規則匹配原service方法
onreturn(service返回值,service原參數1,service原參數2...)
onthrow(Throwable ex,service原參數1,service原參數2...)
2.10 日志設置
2.10.1 功能說明
如果需要記錄每一次請求信息,可開啟訪問日志,類似於apache的訪問日志。
2.10.2 應用場景
可用於測試,或線上開啟一台機器用於排查問題。
2.10.3 操作步驟
將訪問日志輸出到當前應用的log4j日志:
<dubbo:protocol accesslog="true"/>
將訪問日志輸出到指定文件:
<dubbo:protocol accesslog="foo/bar.log"/>
2.10.4 注意事項
此日志量比較大,請注意磁盤容量
為了對日志進行統一處理,建議采用全局日志框架,對各層的日志以及日志級別和開關進行統一考慮。