Dubbo使用總結
- 2019 年 12 月 31 日
- 筆記
文章目錄
Dubbo常見問題
官方文檔
啟動檢查
Dubbo 缺省會在啟動時檢查依賴的服務是否可用,不可用時會拋出異常,阻止 Spring 初始化完成,以便上線時,能及早發現問題,默認
check="true"
可以通過check="false"
關閉檢查,比如,測試時,有些服務不關心,或者出現了循環依賴,必須有一方先啟動。 另外,如果你的 Spring 容器是懶加載的,或者通過 API 編程延遲引用服務,請關閉 check,否則服務臨時不可用時,會拋出異常,拿到 null 引用,如果check="false"
,總是會返回引用,當服務恢復時,能自動連上 如果在服務提供者沒有上線的情況下,我們需要提前將消費者上線,那麼就可以關閉啟動檢查,這樣當消費者啟動但是不調用服務的情況下不會報錯,保證正常啟動
配置方式
關閉某個服務的檢查
<dubbo:reference id="helloService" interface="cn.tedu.service.IHelloService" check="false"/>
:關閉某個服務的啟動時檢查
這個只會關閉當前的服務器的檢查,還是會檢查其他的服務
在沒有對應服務提供者的情況下如果調用這個服務那麼將會報錯
在消費者中配置
public class TestDubbo { public static void main(String[] args) { ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("appliactionContext.xml"); IHelloService helloService=context.getBean("helloService",IHelloService.class); //現在沒有調用服務的情況下不會報錯,但是如果調用了HelloService中的方法,那麼將會報錯 // helloService.sayHello(); context.close(); } }
- 如果沒有關閉檢查,那麼會出現如下的錯誤
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'helloService': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: Failed to check the status of the service cn.tedu.service.IHelloService. No provider available for the service cn.tedu.service.IHelloService from the url zookeeper://39.105.123.197:2181/com.alibaba.dubbo.registry.RegistryService?application=dubbo-consum01&dubbo=2.5.3&interface=cn.tedu.service.IHelloService&methods=sayHello&pid=10974&revision=0.0.1&side=consumer×tamp=1529667926718 to the consumer 10.18.236.4 use dubbo version 2.5.3 at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:175) at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:103) at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1634) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:254) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1086) at TestDubbo.main(TestDubbo.java:9) Caused by: java.lang.IllegalStateException: Failed to check the status of the service cn.tedu.service.IHelloService. No provider available for the service cn.tedu.service.IHelloService from the url zookeeper://39.105.123.197:2181/com.alibaba.dubbo.registry.RegistryService?application=dubbo-consum01&dubbo=2.5.3&interface=cn.tedu.service.IHelloService&methods=sayHello&pid=10974&revision=0.0.1&side=consumer×tamp=1529667926718 to the consumer 10.18.236.4 use dubbo version 2.5.3 at com.alibaba.dubbo.config.ReferenceConfig.createProxy(ReferenceConfig.java:420) at com.alibaba.dubbo.config.ReferenceConfig.init(ReferenceConfig.java:300) at com.alibaba.dubbo.config.ReferenceConfig.get(ReferenceConfig.java:138) at com.alibaba.dubbo.config.spring.ReferenceBean.getObject(ReferenceBean.java:65) at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:168) ... 6 more
關閉所有服務的檢查
關閉所有服務的檢查 在消費者的配置文件中配置即可
<dubbo:consumer check="false" />
關閉註冊中心啟動時檢查
註冊訂閱失敗的時候報錯
<dubbo:registry check="false" />
dubbo.properties
- 在
src/main/resource
文件夾下新建dubbo.properties
- 在其中添加如下內容
- dubbo.reference.com.foo.BarService.check=false dubbo.reference.check=false dubbo.consumer.check=false dubbo.registry.check=false
- 只需要添加這個文件即可,會自動設置
負載均衡策略
- 這個在集群部署的時候會用到,比如多台機器提供的是同一個服務,那麼當瀏覽器請求服務的時候到底該調用哪台機器上的服務才會更好,此時就需要用到負載均衡策略
- 優秀博文
多協議
- 在dubbo中存在8中不同的協議,這些協議的作用都是不同的,此時我們需要根據服務的功能來使用不同的協議,比如我們需要上傳文件,那麼就需要能夠傳輸大文件的協議
- 默認是dubbo協議,也是用的最多的協議
不同服務不同協議
<!-- 多協議配置 --> <dubbo:protocol name="dubbo" port="20880" /> <dubbo:protocol name="rmi" port="1099" /> <!-- 使用dubbo協議暴露服務,直接使用protocol關鍵詞引用上面配置的協議 --> <dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" protocol="dubbo" /> <!-- 使用rmi協議暴露服務 --> <dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" protocol="rmi" />
多協議暴露服務
<!-- 多協議配置 --> <dubbo:protocol name="dubbo" port="20880" /> <dubbo:protocol name="hessian" port="8080" /> <!-- 使用多個協議暴露服務 --> <dubbo:service id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" protocol="dubbo,hessian" />
多版本
- 當一個接口實現,出現不兼容升級時,可以用版本號過渡,版本號不同的服務相互間不引用。 可以按照以下的步驟進行版本遷移:
- 在低壓力時間段,先升級一半提供者為新版本
- 再將所有消費者升級為新版本
- 然後將剩下的一半提供者升級為新版本
- 直接使用
version
指定版本即可
實現
- 我們有一個
HelloService
接口,但是有兩個實現類,分別為HelloServiceImpl1
,HelloServiceImpl2
public class HelloServiceImpl1 implements IHelloService { public void sayHello() { System.out.println("say helloService1"); } } public class HelloServiceImpl2 implements IHelloService { public void sayHello() { System.out.println("say helloservice2"); } }
- 配置服務提供者,需要根據版本的不同提供服務
<!-- 配置應用名字,用來標識每一個應用,這裡的name最好和工程名字一樣 --> <dubbo:application name="dubbo-provider01"></dubbo:application> <!-- 使用zookeeper註冊中心暴露服務 --> <dubbo:registry address="zookeeper://39.105.123.197:2181" /> <!-- 配置服務的接口實現類,這樣當提供服務調用的接口的時候才能找到對應的實現類 --> <bean id="helloService1" class="cn.tedu.servivceImpl.HelloServiceImpl1"></bean> <!-- 配置服務的接口實現類,這樣當提供服務調用的接口的時候才能找到對應的實現類 --> <bean id="helloService2" class="cn.tedu.servivceImpl.HelloServiceImpl2"></bean> <!-- 配置服務提供者,版本為1.0,使用的是helloService1實現類 --> <dubbo:service interface="cn.tedu.service.IHelloService" ref="helloService1" version="1.0"></dubbo:service> <!-- 配置服務提供者,版本為2.0,使用的是helloService2實現類 --> <dubbo:service interface="cn.tedu.service.IHelloService" ref="helloService2" version="2.0"></dubbo:service>
- 配置服務消費者,需要指定版本號區分調用的服務
- 根據版本號區分調用哪個服務
<!--調用2.0版本的服務--> <dubbo:reference id="helloService2" interface="cn.tedu.service.IHelloService" version="2.0"/> <!--調用1.0版本的服務--> <dubbo:reference id="helloService1" interface="cn.tedu.service.IHelloService" version="1.0"/>
服務分組
- 當一個接口有多種實現的時候,我們可以使用分組區分調用的服務功能
- 假設一個支付的接口
PayService
,其中實現的類有微信支付WeChatPayServiceImpl
和支付寶支付AliPayServiceImpl
,那麼我們可以使用分組進行區分兩種服務
服務
- 在服務提供者的配置文件中定義
<bean id="aliPayServiceImpl" class="cn.tedu.serviceImpl.AliPayServiceImpl"></bean> <bean id="weChatPayServiceImpl" class="cn.tedu.serviceImpl.WeChatPayServiceImpl"></bean> <!--使用group區分不同的服務功能 --> <dubbo:service group="alipay" interface="cn.tedu.service.PayService" ref="aliPayServiceImpl" /> <dubbo:service group="weChatPay" interface="cn.tedu.service.PayService" ref="weChatPayServiceImpl" />
引用
- 在消費者的配置文件中配置即可,使用
group
指定需要引用的服務
<dubbo:reference id="alipayService" interface="cn.tedu.service.PayService" group="alipay"/> <dubbo:reference id="weChatPayService" interface="cn.tedu.service.PayService" group="weChatPay"/>
令牌驗證
通過令牌驗證在註冊中心控制權限,以決定要不要下發令牌給消費者,可以防止消費者繞過註冊中心訪問提供者,另外通過註冊中心可靈活改變授權方式,而不需修改或升級提供者
可以全局設置開啟令牌驗證:
<!--隨機token令牌,使用UUID生成--> <dubbo:provider interface="com.foo.BarService" token="true" />
或
<!--固定token令牌,相當於密碼--> <dubbo:provider interface="com.foo.BarService" token="123456" />
也可在服務級別設置:
<!--隨機token令牌,使用UUID生成--> <dubbo:service interface="com.foo.BarService" token="true" />
或
<!--固定token令牌,相當於密碼--> <dubbo:service interface="com.foo.BarService" token="123456" />
還可在協議級別設置:
<!--隨機token令牌,使用UUID生成--> <dubbo:protocol name="dubbo" token="true" />
或
<!--固定token令牌,相當於密碼--> <dubbo:protocol name="dubbo" token="123456" />
dubbo控制台的安裝部署
- 下載dubbo-admin.war : 鏈接:https://pan.baidu.com/s/1ggeIIHX 密碼:ck4h
- 優秀博文
線程模型
如果事件處理的邏輯能迅速完成,並且不會發起新的 IO 請求,比如只是在內存中記個標識,則直接在 IO 線程上處理更快,因為減少了線程池調度。
但如果事件處理邏輯較慢,或者需要發起新的 IO 請求,比如需要查詢數據庫,則必須派發到線程池,否則 IO 線程阻塞,將導致不能接收其它請求。
如果用 IO 線程處理事件,又在事件處理過程中發起新的 IO 請求,比如在連接事件中發起登錄請求,會報「可能引發死鎖」異常,但不會真死鎖。
因此,需要通過不同的派發策略和不同的線程池配置的組合來應對不同的場景:
<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />
Dispatcher
all
所有消息都派發到線程池,包括請求,響應,連接事件,斷開事件,心跳等。direct
所有消息都不派發到線程池,全部在 IO 線程上直接執行。message
只有請求響應消息派發到線程池,其它連接斷開事件,心跳等消息,直接在 IO 線程上執行。execution
只請求消息派發到線程池,不含響應,響應和其它連接斷開事件,心跳等消息,直接在 IO 線程上執行。connection
在 IO 線程上,將連接斷開事件放入隊列,有序逐個執行,其它消息派發到線程池。
ThreadPool
fixed
固定大小線程池,啟動時建立線程,不關閉,一直持有。(缺省)cached
緩存線程池,空閑一分鐘自動刪除,需要時重建。limited
可伸縮線程池,但池中的線程數只會增長不會收縮。只增長不收縮的目的是為了避免收縮時突然來了大流量引起的性能問題。eager
優先創建Worker
線程池。在任務數量大於corePoolSize
但是小於maximumPoolSize
時,優先創建Worker
來處理任務。當任務數量大於maximumPoolSize
時,將任務放入阻塞隊列中。阻塞隊列充滿時拋出RejectedExecutionException
。(相比於cached
:cached
在任務數量超過maximumPoolSize
時直接拋出異常而不是將任務放入阻塞隊列)
多註冊中心
Dubbo 支持同一服務向多註冊中心同時註冊,或者不同服務分別註冊到不同的註冊中心上去,甚至可以同時引用註冊在不同註冊中心上的同名服務。另外,註冊中心是支持自定義擴展的 1。
多註冊中心註冊
比如:中文站有些服務來不及在青島部署,只在杭州部署,而青島的其它應用需要引用此服務,就可以將服務同時註冊到兩個註冊中心。
<?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://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="world" /> <!-- 多註冊中心配置 --> <dubbo:registry id="hangzhouRegistry" address="10.20.141.150:9090" /> <dubbo:registry id="qingdaoRegistry" address="10.20.141.151:9010" default="false" /> <!-- 向多個註冊中心註冊 --> <dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="hangzhouRegistry,qingdaoRegistry" /> </beans>
不同服務使用不同註冊中心
比如:CRM 有些服務是專門為國際站設計的,有些服務是專門為中文站設計的。
<?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://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="world" /> <!-- 多註冊中心配置 --> <dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" /> <dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" /> <!-- 向中文站註冊中心註冊 --> <dubbo:service interface="com.alibaba.hello.api.HelloService" version="1.0.0" ref="helloService" registry="chinaRegistry" /> <!-- 向國際站註冊中心註冊 --> <dubbo:service interface="com.alibaba.hello.api.DemoService" version="1.0.0" ref="demoService" registry="intlRegistry" /> </beans>
多註冊中心引用
比如:CRM 需同時調用中文站和國際站的 PC2 服務,PC2 在中文站和國際站均有部署,接口及版本號都一樣,但連的數據庫不一樣。
<?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://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="world" /> <!-- 多註冊中心配置 --> <dubbo:registry id="chinaRegistry" address="10.20.141.150:9090" /> <dubbo:registry id="intlRegistry" address="10.20.154.177:9010" default="false" /> <!-- 引用中文站服務 --> <dubbo:reference id="chinaHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="chinaRegistry" /> <!-- 引用國際站站服務 --> <dubbo:reference id="intlHelloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" registry="intlRegistry" /> </beans>
如果只是測試環境臨時需要連接兩個不同註冊中心,使用豎號分隔多個不同註冊中心地址:
<?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://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="world" /> <!-- 多註冊中心配置,豎號分隔表示同時連接多個不同註冊中心,同一註冊中心的多個集群地址用逗號分隔 --> <dubbo:registry address="10.20.141.150:9090|10.20.154.177:9010" /> <!-- 引用服務 --> <dubbo:reference id="helloService" interface="com.alibaba.hello.api.HelloService" version="1.0.0" /> </beans>
分組聚合
按組合併返回結果 1,比如菜單服務,接口一樣,但有多種實現,用group區分,現在消費方需從每種group中調用一次返回結果,合併結果返回,這樣就可以實現聚合菜單項。
配置
搜索所有分組
<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="*"> <dubbo:method name="getMenuItems" merger="true" /> </dubbo:service>
某個方法不合併結果,其它都合併結果
<dubbo:reference interface="com.xxx.MenuService" group="*" merger="true"> <dubbo:method name="getMenuItems" merger="false" /> </dubbo:service>
指定合併策略,缺省根據返回值類型自動匹配,如果同一類型有兩個合併器時,需指定合併器的名稱 2
<dubbo:reference interface="com.xxx.MenuService" group="*"> <dubbo:method name="getMenuItems" merger="mymerge" /> </dubbo:service>
指定合併方法,將調用返回結果的指定方法進行合併,合併方法的參數類型必須是返回結果類型本身
<dubbo:reference interface="com.xxx.MenuService" group="*"> <dubbo:method name="getMenuItems" merger=".addAll" /> </dubbo:service>
配置文件覆蓋策略
- 消費者中的相同的配置將會覆蓋提供者的配置
回聲測試
- 回聲測試用於檢測服務是否可用,回聲測試按照正常請求流程執行,能夠測試整個調用是否通暢,可用於監控。所有服務自動實現
EchoService
接口,只需將任意服務引用強制轉型為EchoService
,即可使用。 - Spring 配置:
<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"));
上下文信息
RpcContext
是一個ThreadLocal
的臨時狀態記錄器,當接收到 RPC 請求,或發起 RPC 請求時,RpcContext 的狀態都會變化。比如:A 調 B,B 再調 C,則 B 機器上,在 B 調 C 之前,RpcContext 記錄的是 A 調 B 的信息,在 B 調 C 之後,RpcContext 記錄的是 B 調 C 的信息。- RpcContext的狀態是隨時變化的,只會存儲最近調用的信息
服務消費方
public class TestDubbo { public static void main(String[] args) { ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("appliactionContext.xml"); IHelloService helloService=context.getBean("helloService",IHelloService.class); //執行服務,只有執行服務才會認定為消費端 helloService.sayHello(); //測試是否是消費者 Boolean flag=RpcContext.getContext().isConsumerSide(); System.out.println(flag); //獲取提供者的ip地址 String ip=RpcContext.getContext().getRemoteHost(); System.out.println(ip); //獲取調用者的名稱,這裡的application是<dubbo:application>標籤中的名字 String parameters=RpcContext.getContext().getUrl().getParameter("application"); System.out.println(parameters); context.close(); } }
服務提供方
public class XxxServiceImpl implements XxxService { public void xxx() { // 本端是否為提供端,這裡會返回true boolean isProviderSide = RpcContext.getContext().isProviderSide(); // 獲取調用方IP地址 String clientIP = RpcContext.getContext().getRemoteHost(); // 獲取當前服務配置信息,所有配置信息都將轉換為URL的參數 String application = RpcContext.getContext().getUrl().getParameter("application"); // 注意:每發起RPC調用,上下文狀態會變化 yyyService.yyy(); // 此時本端變成消費端,這裡會返回false boolean isProviderSide = RpcContext.getContext().isProviderSide(); } }
隱式參數
可以通過
RpcContext
上的setAttachment
和getAttachment
在服務消費方和提供方之間進行參數的隱式傳遞。 1
在服務消費方端設置隱式參數
setAttachment
設置的 KV 對,在完成下面一次遠程調用會被清空,即多次遠程調用要多次設置。
RpcContext.getContext().setAttachment("index", "1"); // 隱式傳參,後面的遠程調用都會隱式將這些參數發送到服務器端,類似cookie,用於框架集成,不建議常規業務使用 xxxService.xxx(); // 遠程調用 // ...
在服務提供方端獲取隱式參數
public class XxxServiceImpl implements XxxService { public void xxx() { // 獲取客戶端隱式傳入的參數,用於框架集成,不建議常規業務使用 String index = RpcContext.getContext().getAttachment("index"); } }
- 注意:path, group, version, dubbo, token, timeout 幾個 key 是保留字段,請使用其它值。 ↩
異步調用
基於 NIO 的非阻塞實現並行調用,客戶端不需要啟動多線程即可完成並行調用多個遠程服務,相對多線程開銷較小。 1
在 consumer.xml 中配置:
<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService"> <dubbo:method name="findFoo" async="true" /> </dubbo:reference> <dubbo:reference id="barService" interface="com.alibaba.bar.BarService"> <dubbo:method name="findBar" async="true" /> </dubbo:reference>
調用代碼:
// 此調用會立即返回null fooService.findFoo(fooId); // 拿到調用的Future引用,當結果返回後,會被通知和設置到此Future Future<Foo> fooFuture = RpcContext.getContext().getFuture(); // 此調用會立即返回null barService.findBar(barId); // 拿到調用的Future引用,當結果返回後,會被通知和設置到此Future Future<Bar> barFuture = RpcContext.getContext().getFuture(); // 此時findFoo和findBar的請求同時在執行,客戶端不需要啟動多線程來支持並行,而是藉助NIO的非阻塞完成 // 如果foo已返回,直接拿到返回值,否則線程wait住,等待foo返回後,線程會被notify喚醒 Foo foo = fooFuture.get(); // 同理等待bar返回 Bar bar = barFuture.get(); // 如果foo需要5秒返回,bar需要6秒返回,實際只需等6秒,即可獲取到foo和bar,進行接下來的處理。
你也可以設置是否等待消息發出: 2
sent="true"
等待消息發出,消息發送失敗將拋出異常。sent="false"
不等待消息發出,將消息放入 IO 隊列,即刻返回。
<dubbo:method name="findFoo" async="true" sent="true" />
如果你只是想異步,完全忽略返回值,可以配置 return="false"
,以減少 Future 對象的創建和管理成本:
<dubbo:method name="findFoo" async="true" return="false" />
本地存根
- dubbo的本地存根的原理是:遠程服務後,客戶端通常只剩下接口,而實現全在服務器端,但提供方有些時候想在客戶端也執行部分邏輯,那麼就在服務消費者這一端提供了一個Stub類,然後當消費者調用provider方提供的dubbo服務時,客戶端生成 Proxy 實例,這個Proxy實例就是我們正常調用dubbo遠程服務要生成的代理實例,然後消費者這方會把 Proxy 通過構造函數傳給 消費者方的Stub ,然後把 Stub 暴露給用戶,Stub 可以決定要不要去調 Proxy。會通過代理類去完成這個調用,這樣在Stub類中,就可以做一些額外的事,來對服務的調用過程進行優化或者容錯的處理。附圖:
- 總結:如果消費者想用在調用遠程服務的同時還想在之前或者之後實現自己的部分邏輯,那麼就需要在消費者端定義一個代理類,其實在消費者調用服務的時候,實際上是調用的代理類。不過其中代理類返回的數據是可以傳遞給服務提供者的
實現步驟
1. 定義一個服務接口和服務實現類
public interface UserInterface { public User getUserById(Integer id) ; }
public class UserService implements UserInterface { public User getUserById(Integer id) { User user = new User() ; user.setId(id); user.setName("hu"); return user; } }
- 服務分佈配置
<dubbo:service interface="org.huxin.dubbo.test.user.service.UserInterface" ref="userService" protocol="dubbo" retries="0"/> <bean id="userService" class="org.huxin.dubbo.test.user.service.impl.UserService" />
3.服務消費者的Stub類
public class UserServiceStub implements UserInterface { //必須定義這個接口,以便接收dubbo在調用遠程服務生成的服務代理類 private UserInterface userLocalService ; //這個構造函數必須要提供,dubbo框架會在消費者這一方調用這個方法 public UserServiceStub(UserInterface userLocalService ) { this.userLocalService = userLocalService ; } public User getUserById(Integer id) { User user = null ; try { if (id == 1) { user = this.userLocalService.getUserById(id) ; }else { user = new User(); user.setName("系統用戶"); } }catch(Exception e) { user = new User(); user.setName("異常用戶"); } return user ; } }
- 服務消費方的配置
<dubbo:reference id="userService" interface="org.huxin.dubbo.test.user.service.UserInterface" stub="org.huxin.dubbo.test.UserServiceStub" protocol="dubbo"/>
5.測試代碼
@Test public void testGetUserById(){ Integer id = 2 ; UserInterface userService = context.getBean(UserInterface.class) ; User user = userService.getUserById( id) ; System.out.println(user.getName()); }
總結
- 實際上調用
getUserById(id)
這個方法是代理類UserServiceStub
的方法,返回的User對象也是這個這個方法返回的
本地偽裝
延遲暴露
並發控制
連接控制
- 限制連接個數
- 連接控制
延遲連接
粘滯連接
粘滯連接用於有狀態服務,儘可能讓客戶端總是向同一提供者發起調用,除非該提供者掛了,再連另一台。
粘滯連接將自動開啟延遲連接,以減少長連接數。
<dubbo:protocol name="dubbo" sticky="true" />