啟動時檢查
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> |