啟動時檢查
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" /> |
也可以用dubbo.properties配置:
dubbo.reference.com.foo.BarService.check=false
|
也可以用-D參數:
java -Ddubbo.reference.com.foo.BarService.check=false java -Ddubbo.reference.check=false java -Ddubbo.consumer.check=false java -Ddubbo.registry.check=false |
注意區別
dubbo.reference.check=false,強制改變所有reference的check值,就算配置中有聲明,也會被覆蓋。
dubbo.consumer.check=false,是設置check的缺省值,如果配置中有顯式的聲明,如:<dubbo:reference check="true"/>,不會受影響。
dubbo.registry.check=false,前面兩個都是指訂閱成功,但提供者列表是否為空是否報錯,如果注冊訂閱失敗時,也允許啟動,需使用此選項,將在后台定時重試。
引用缺省是延遲初始化的,只有引用被注入到其它Bean,或被getBean()獲取,才會初始化。
如果需要飢餓加載,即沒有人引用也立即生成動態代理,可以配置:
<dubbo:reference interface="com.foo.BarService" init="true" /> |
集群容錯
在集群調用失敗時,Dubbo提供了多種容錯方案,缺省為failover重試。
各節點關系:
這里的Invoker是Provider的一個可調用Service的抽象,Invoker封裝了Provider地址及Service接口信息。
Directory代表多個Invoker,可以把它看成List<Invoker>,但與List不同的是,它的值可能是動態變化的,比如注冊中心推送變更。
Cluster將Directory中的多個Invoker偽裝成一個Invoker,對上層透明,偽裝過程包含了容錯邏輯,調用失敗后,重試另一個。
Router負責從多個Invoker中按路由規則選出子集,比如讀寫分離,應用隔離等。
LoadBalance負責從多個Invoker中選出具體的一個用於本次調用,選的過程包含了負載均衡算法,調用失敗后,需要重選。
集群容錯模式:
Failover Cluster
失敗自動切換,當出現失敗,重試其它服務器。(缺省)
通常用於讀操作,但重試會帶來更長延遲。
可通過retries="2"來設置重試次數(不含第一次)。
Failfast Cluster
快速失敗,只發起一次調用,失敗立即報錯。
通常用於非冪等性的寫操作,比如新增記錄。
Failsafe Cluster
失敗安全,出現異常時,直接忽略。
通常用於寫入審計日志等操作。
Failback Cluster
失敗自動恢復,后台記錄失敗請求,定時重發。
通常用於消息通知操作。
Forking Cluster
並行調用多個服務器,只要一個成功即返回。
通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。
可通過forks="2"來設置最大並行數。
Broadcast Cluster
廣播調用所有提供者,逐個調用,任意一台報錯則報錯。(2.1.0開始支持)
通常用於通知所有提供者更新緩存或日志等本地資源信息。
重試次數配置如:(failover集群模式生效)
retries="2" /> |
或:
<dubbo:reference retries="2" /> |
或:
name="findFoo" retries="2" /> |
集群模式配置如:
<dubbo:service cluster="failsafe" /> |
或:
<dubbo:reference cluster="failsafe" /> |
負載均衡
在集群負載均衡時,Dubbo提供了多種均衡策略,缺省為random隨機調用。
Random LoadBalance
隨機,按權重設置隨機概率。
在一個截面上碰撞的概率高,但調用量越大分布越均勻,而且按概率使用權重后也比較均勻,有利於動態調整提供者權重。
RoundRobin LoadBalance
輪循,按公約后的權重設置輪循比率。
存在慢的提供者累積請求問題,比如:第二台機器很慢,但沒掛,當請求調到第二台時就卡在那,久而久之,所有請求都卡在調到第二台上。
LeastActive LoadBalance
最少活躍調用數,相同活躍數的隨機,活躍數指調用前后計數差。
使慢的提供者收到更少請求,因為越慢的提供者的調用前后計數差會越大。
ConsistentHash LoadBalance
一致性Hash,相同參數的請求總是發到同一提供者。
當某一台提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。
算法參見:http://en.wikipedia.org/wiki/Consistent_hashing。
缺省只對第一個參數Hash,如果要修改,請配置<dubbo:parameter key="hash.arguments" value="0,1" />
缺省用160份虛擬節點,如果要修改,請配置<dubbo:parameter key="hash.nodes" value="320" />
配置如:
<dubbo:service interface="..." loadbalance="roundrobin" /> |
或:
<dubbo:reference interface="..." loadbalance="roundrobin" /> |
或:
<dubbo:service interface="...">
name="..." loadbalance="roundrobin"/>
|
或:
<dubbo:reference interface="...">
name="..." loadbalance="roundrobin"/>
|
線程模型
件處理線程說明
如果事件處理的邏輯能迅速完成,並且不會發起新的IO請求,比如只是在內存中記個標識,則直接在IO線程上處理更快,因為減少了線程池調度。
但如果事件處理邏輯較慢,或者需要發起新的IO請求,比如需要查詢數據庫,則必須派發到線程池,否則IO線程阻塞,將導致不能接收其它請求。
如果用IO線程處理事件,又在事件處理過程中發起新的IO請求,比如在連接事件中發起登錄請求,會報“可能引發死鎖”異常,但不會真死鎖。
Dispatcher
all 所有消息都派發到線程池,包括請求,響應,連接事件,斷開事件,心跳等。
direct 所有消息都不派發到線程池,全部在IO線程上直接執行。
message 只有請求響應消息派發到線程池,其它連接斷開事件,心跳等消息,直接在IO線程上執行。
execution 只請求消息派發到線程池,不含響應,響應和其它連接斷開事件,心跳等消息,直接在IO線程上執行。
connection 在IO線程上,將連接斷開事件放入隊列,有序逐個執行,其它消息派發到線程池。
ThreadPool
fixed 固定大小線程池,啟動時建立線程,不關閉,一直持有。(缺省)
cached 緩存線程池,空閑一分鍾自動刪除,需要時重建。
limited 可伸縮線程池,但池中的線程數只會增長不會收縮。(為避免收縮時突然來了大流量引起的性能問題)。
配置如:
<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" /> |
直連提供者
在開發及測試環境下,經常需要繞過注冊中心,只測試指定服務提供者,這時候可能需要點對點直連,點對點直聯方式,將以服務接口為單位,忽略注冊中心的提供者列表,A接口配置點對點,不影響B接口從注冊中心獲取列表。
(1) 如果是線上需求需要點對點,可在<dubbo:reference>中配置url指向提供者,將繞過注冊中心,多個地址用分號隔開,配置如下:(1.0.6及以上版本支持)
<dubbo:reference interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" /> |
(2) 在JVM啟動參數中加入-D參數映射服務地址,如:
(key為服務名,value為服務提供者url,此配置優先級最高,1.0.15及以上版本支持)
java |
注意
為了避免復雜化線上環境,不要在線上使用這個功能,只應在測試階段使用。
(3) 如果服務比較多,也可以用文件映射,如:
(用-Ddubbo.resolve.file指定映射文件路徑,此配置優先級高於<dubbo:reference>中的配置,1.0.15及以上版本支持)
(2.0以上版本自動加載${user.home}/dubbo-resolve.properties文件,不需要配置)
java |
然后在映射文件xxx.properties中加入:(key為服務名,value為服務提供者url)
com.alibaba.xxx.XxxService=dubbo://localhost:20890 |
注意
為了避免復雜化線上環境,不要在線上使用這個功能,只應在測試階段使用。
只訂閱
問題
為方便開發測試,經常會在線下共用一個所有服務可用的注冊中心,這時,如果一個正在開發中的服務提供者注冊,可能會影響消費者不能正常運行。
解決方案
可以讓服務提供者開發方,只訂閱服務(開發的服務可能依賴其它服務),而不注冊正在開發的服務,通過直連測試正在開發的服務。
禁用注冊配置:
<dubbo:registry address="10.20.153.10:9090" register="false" /> |
或者:
<dubbo:registry address="10.20.153.10:9090?register=false" /> |
只注冊
問題
如果有兩個鏡像環境,兩個注冊中心,有一個服務只在其中一個注冊中心有部署,另一個注冊中心還沒來得及部署,而兩個注冊中心的其它應用都需要依賴此服務,所以需要將服務同時注冊到兩個注冊中心,但卻不能讓此服務同時依賴兩個注冊中心的其它服務。
解決方案
可以讓服務提供者方,只注冊服務到另一注冊中心,而不從另一注冊中心訂閱服務。
禁用訂閱配置:
<dubbo:registry id="hzRegistry" address="10.20.153.10:9090" /> id="qdRegistry" address="10.20.141.150:9090" subscribe="false" /> |
或者:
<dubbo:registry id="hzRegistry" address="10.20.153.10:9090" /> id="qdRegistry" address="10.20.141.150:9090?subscribe=false" /> |
靜態服務
有時候希望人工管理服務提供者的上線和下線,此時需將注冊中心標識為非動態管理模式。
<dubbo:registry address="10.20.141.150:9090" dynamic="false" /> |
或者:
<dubbo:registry address="10.20.141.150:9090?dynamic=false" /> |
服務提供者初次注冊時為禁用狀態,需人工啟用,斷線時,將不會被自動刪除,需人工禁用。
如果是一個第三方獨立提供者,比如memcached等,可以直接向注冊中心寫入提供者地址信息,消費者正常使用:(通常由腳本監控中心頁面等調用)
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension(); Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181")); registry.register(URL.valueOf ("memcached://10.20.153.11/com.foo.BarService?category=providers&dynamic=false&application=foo")); |
多協議
(1) 不同服務不同協議
比如:不同服務在性能上適用不同協議進行傳輸,比如大數據用短連接協議,小數據大並發用長連接協議。
<?xml version="1.0" encoding="UTF-8"?> |
(2) 多協議暴露服務
比如:需要與http客戶端互操作
consumer.xml
<?xml version="1.0" encoding="UTF-8"?> |
多注冊中心
(1) 多注冊中心注冊
比如:中文站有些服務來不及在青島部署,只在杭州部署,而青島的其它應用需要引用此服務,就可以將服務同時注冊到兩個注冊中心。
consumer.xml
<?xml version="1.0" encoding="UTF-8"?> |
(2) 不同服務使用不同注冊中心
比如:CRM有些服務是專門為國際站設計的,有些服務是專門為中文站設計的。
<?xml version="1.0" encoding="UTF-8"?> |
(3) 多注冊中心引用
比如:CRM需同時調用中文站和國際站的PC2服務,PC2在中文站和國際站均有部署,接口及版本號都一樣,但連的數據庫不一樣。
consumer.xml
<?xml version="1.0" encoding="UTF-8"?> |
如果只是測試環境臨時需要連接兩個不同注冊中心,使用豎號分隔多個不同注冊中心地址:
<?xml version="1.0" encoding="UTF-8"?> |
服務分組
當一個接口有多種實現時,可以用group區分。
<dubbo:service group="feedback" interface="com.xxx.IndexService" /> group="member" interface="com.xxx.IndexService" /> |
任意組:(2.2.0以上版本支持,總是只調一個可用組的實現)
<dubbo:reference id="feedbackIndexService" group="feedback" interface="com.xxx.IndexService" /> id="memberIndexService" group="member" interface="com.xxx.IndexService" /> |
多版本
當一個接口實現,出現不兼容升級時,可以用版本號過渡,版本號不同的服務相互間不引用。
在低壓力時間段,先升級一半提供者為新版本
再將所有消費者升級為新版本
然后將剩下的一半提供者升級為新版本
<dubbo:service interface="com.foo.BarService" version="1.0.0" /> |
<dubbo:service interface="com.foo.BarService" version="2.0.0" /> |
<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" /> |
<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" /> |
不區分版本:(2.2.0以上版本支持)
<dubbo:reference id="barService" interface="com.foo.BarService" version="*" /> |
分組聚合
按組合並返回結果,比如菜單服務,接口一樣,但有多種實現,用group區分,現在消費方需從每種group中調用一次返回結果,合並結果返回,這樣就可以實現聚合菜單項。
從2.1.0版本開始支持
代碼參見:https://github.com/alibaba/dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/merge
配置如:(搜索所有分組)
<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true" /> |
或:(合並指定分組)
<dubbo:reference interface="com.xxx.MenuService" group="aaa,bbb" merger="true" /> |
或:(指定方法合並結果,其它未指定的方法,將只調用一個Group)
<dubbo:reference interface="com.xxx.MenuService" group="*"> name="getMenuItems" merger="true" /> |
或:(某個方法不合並結果,其它都合並結果)
<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true"> name="getMenuItems" merger="false" /> |
或:(指定合並策略,缺省根據返回值類型自動匹配,如果同一類型有兩個合並器時,需指定合並器的名稱)
參見:[合並結果擴展]
<dubbo:reference interface="com.xxx.MenuService" group="*"> name="getMenuItems" merger="mymerge" /> |
或:(指定合並方法,將調用返回結果的指定方法進行合並,合並方法的參數類型必須是返回結果類型本身)
<dubbo:reference interface="com.xxx.MenuService" group="*"> name="getMenuItems" merger=".addAll" /> |
參數驗證
參數驗證功能是基於JSR303實現的,用戶只需標識JSR303標准的驗證Annotation,並通過聲明filter來實現驗證。
2.1.0以上版本支持
完整示例代碼參見:https://github.com/alibaba/dubbo/tree/master/dubbo-test/dubbo-test-examples/src/main/java/com/alibaba/dubbo/examples/validation
驗證方式可擴展,參見:Validation擴展點
參數標注示例:
import java.io.Serializable; java.util.Date; javax.validation.constraints.Future; javax.validation.constraints.Max; javax.validation.constraints.Min; javax.validation.constraints.NotNull; javax.validation.constraints.Past; javax.validation.constraints.Pattern; javax.validation.constraints.Size; class Serializable { static // 不允許為空 String name; String email; int // 必須為一個過去的時間 Date loginDate; // 必須為一個未來的時間 Date expiryDate; String getName() { name; void String getEmail() { email; void
int age; void age) { Date getLoginDate() { loginDate; void Date getExpiryDate() { expiryDate; void 分組驗證示例: public interface Save{} // save(ValidationParameter parameter); update(ValidationParameter parameter); |
分組驗證示例:
import javax.validation.GroupSequence; public interface @GroupSequence(Update.class) @interface Save{}
save(ValidationParameter parameter); @interface Update{}
update(ValidationParameter parameter); } |
參數驗證示例:
import javax.validation.constraints.Min; javax.validation.constraints.NotNull; interface save(@NotNull ValidationParameter parameter); // delete(@Min(1) id); // |
在客戶端驗證參數:
<dubbo:reference id="validationService" interface="com.alibaba.dubbo.examples.validation.api.ValidationService" validation="true" /> |
在服務器端驗證參數:
<dubbo:service interface="com.alibaba.dubbo.examples.validation.api.ValidationService" ref="validationService" validation="true" /> |
驗證異常信息:
import javax.validation.ConstraintViolationException; javax.validation.ConstraintViolationException; org.springframework.context.support.ClassPathXmlApplicationContext; com.alibaba.dubbo.examples.validation.api.ValidationParameter; com.alibaba.dubbo.examples.validation.api.ValidationService; com.alibaba.dubbo.rpc.RpcException; class static Exception {
ClassPathXmlApplicationContext(config);
{ ValidationParameter(); (RpcException e) { // |
需要加入依賴:
<dependency> <groupId>javax.validation</groupId> |