14.dubbo本地存根、本地偽裝、延遲暴露、併發控制、連線控制
1.本地存根
消費者進行一次遠端呼叫之後可以把這一次呼叫生成的遠端物件的proxy進行快取,再次呼叫時使用這個快取的proxy來呼叫。
寫一個存根類,用來傳入proxy快取物件。存根類和遠端呼叫的類實現同一個介面。當遠端呼叫類調用出錯時還可以用存根類返回一個容錯資料
(1)提供方
暴露的介面實現
暴露的介面實現的proxy存根類package com.tyf.d_zk_provider; //暴露介面例項 public class modelServiceImpl implements modelService { //暴露的方法 public String serviceTest(String data) { System.out.println("提供方呼叫"); return "提供方計算結果"; } }
配置檔案在釋出服務時指定服務的存根類package com.tyf.d_zk_provider; //存根類,需要傳入一個暴露方法的proxy,這個proxy是消費者上一次呼叫生成的遠端物件的proxy //這個存根類會儲存在消費者端 public class sub_modelServiceImpl implements modelService { //傳入proxy private final modelService service; public sub_modelServiceImpl(modelService service) { this.service = service; } //和暴露例項相同的方法 public String serviceTest(String data) { //先嚐試用上次呼叫生成的proxy來執行 try { return service.serviceTest(data); } catch (Exception e) { System.out.println("使用proxy存根呼叫失敗,返回容錯資料"); return "容錯資料"; } } }
<!-- 暴露服務、開啟存根 -->
<dubbo:service interface="com.tyf.d_zk_provider.modelService"
ref="modelService"
stub="com.tyf.d_zk_provider.sub_modelServiceImpl"/>
<bean id="modelService" class="com.tyf.d_zk_provider.modelServiceImpl" />
(2)消費者
不做任何改變直接多次呼叫即可
2.本地偽裝
dubbo本地偽裝通常用於服務降級。通常服務提供方暴露的介面可能在被消費者呼叫的時候會因為一些網路或者其他因素而呼叫失敗而丟擲RpcException異常。丟擲異常後可能會影響整個呼叫鏈的後續呼叫這裡可以給暴露的介面在消費者端開啟mock。分兩種情況
假如只是想忽略異常:在引用服務的時候設定mock為return null。即呼叫發生異常時直接忽略異常呼叫結果直接返回null值。
假如想忽略異常的同時返回容錯資料:在引用服務的時候mock設定為一個bean,這個bean實現了暴露的介面方法來返回一個容錯資料
下面展示第二種
(1)提供者工程關閉下測試消費者本地呼叫
消費者
在引用服務的時候設定介面的mock具體實現。關閉服務檢查,這裡不開啟服務提供者,消費者會自動走mock的實現
配置檔案
<!-- 引用提供者介面,同時設定本地偽裝 -->
<dubbo:reference id="modelService"
interface="com.tyf.d_zk_provider.modelService"
mock="com.tyf.d_zk_provider.mock_modelService"
check="false"
/>
呼叫本地偽裝服務結果
// 獲取遠端服務代理
modelService service = (modelService)context.getBean("modelService");
// 遠端呼叫
service.serviceTest("data");
(2)提供者被呼叫時丟擲RpcException異常時觸發消費者本地呼叫
提供者
暴露的介面方法返回一個person物件
//暴露介面例項
public class modelServiceImpl implements modelService {
//暴露的方法
public person serviceTest(String data) {
System.out.println("提供方呼叫");
return new person("name", "age");
}
}
person類。在不實現序列化介面情況下上面的呼叫會丟擲RpcException異常
消費者
mock實現
//服務提供方介面的本地實現,在服務提供方呼叫丟擲異常之後呼叫本地
public class mock_modelService implements modelService {
//返回容錯資料
public person serviceTest(String data) {
//System.out.println("提供者的本地偽裝呼叫");
return new person("容錯資料", "容錯資料");
}
}
配置檔案
<!-- 引用提供者介面,同時設定本地偽裝 -->
<dubbo:reference id="modelService"
interface="com.tyf.d_zk_provider.modelService"
mock="com.tyf.d_zk_provider.mock_modelService"
check="false"
/>
呼叫結果 // 獲取遠端服務代理
modelService service = (modelService)context.getBean("modelService");
// 遠端呼叫
person p = service.serviceTest("data");
System.out.println(p.getName()+p.getAge());
3.延遲暴露
一方面假如應用需要預載入快取或者其他預熱活動。另一方面防止spring死鎖。延遲到spring載入完畢之後暴露服務<dubbo:service deplay="-1">
延遲5秒暴露服務<dubbo:service deplay="5000">
applicationContext.getBean():1.同步singletonObject判斷bean是否存在2.如果bean不存在就同步beanDefinitionMap將bean初始化3.再次同步singletonObjects將bean寫入bean例項快取。
spring初始化執行緒:1.同步beanDefinitionMap初始化bean2.同步singletonObjects將bean寫入bean例項快取。
按照spring初始化過程,當解析到dubbo:service標籤是時就已經在zk上面暴露了服務。這個時候假如有一個請求呼叫這個方法,且方法中有applicationContext.getBean()時就會和spring的初始化執行緒對singletonObjects和beanDefinitionMap造成死鎖。
解決辦法:
1.不使用getBean全部使用ioc注入的方式獲取bean
2.如果一個暴露的服務需要呼叫getBean可以dubbo配置放在spring最後載入
3.大量使用getBean的話可以將dubbo服務單獨隔離容器
4.延遲暴露服務<dubbo:service deplay="-1">
4.併發控制
提供方併發控制:<!-- 暴露服務,提供方呼叫時執行緒池容量不超過10 -->
<dubbo:service interface="com.tyf.d_zk_provider.modelService" ref="modelService" >
<dubbo:method name="serviceTest" executes="10" />
</dubbo:service>
消費者端併發控制:
<!-- 引用提供者介面,消費者端呼叫時執行緒池容量不超過10 -->
<dubbo:reference id="modelService" interface="com.tyf.d_zk_provider.modelService" >
<dubbo:method name="serviceTest" actives="10" />
</dubbo:reference>
如果 <dubbo:service>
和 <dubbo:reference>
都配了actives那麼<dubbo:reference>
優先如果同時設定了負載均衡策略為leastactives那麼每次呼叫時選擇併發數最小的那個提供者來呼叫
<!-- 引用提供者介面,消費者端呼叫時執行緒池容量不超過10 -->
<dubbo:reference id="modelService" interface="com.tyf.d_zk_provider.modelService" loadbalance="leastactive">
<dubbo:method name="serviceTest" actives="10" />
</dubbo:reference>
5.連線控制
提供者連線控制: <!-- 使用dubbo協議暴露服務的提供者接收的連線數不超過10個 -->
<dubbo:protocol name="dubbo" port="20880" accepts="10"/>
消費者連線控制:
<!-- 引用提供者介面,消費者端連線數不超過10 -->
<dubbo:reference id="modelService" interface="com.tyf.d_zk_provider.modelService" connections="10">
</dubbo:reference>