Dubbo開發優化注意事項

啓動時檢查

    Dubbo 缺省會在啓動時檢查依賴的服務是否可用,不可用時會拋出異常,阻止 Spring 初始化完成,以便上線時,能及早發現問題,默認 check="true"。另外,若是你的 Spring 容器是懶加載的,或者經過 API 編程延遲引用服務,請關閉 check,不然服務臨時不可用時,會拋出異常,拿到 null 引用,若是 check="false",老是會返回引用,當服務恢復時,能自動連上。html

集羣容錯模式

Failover Cluster 失敗自動切換,當出現失敗,重試其它服務器 。一般用於讀操做,但重試會帶來更長延遲。可經過 retries="2" 來設置重試次數(不含第一次)。java

Failfast Cluster 快速失敗,只發起一次調用,失敗當即報錯。用於非冪等性的寫操做,好比新增記錄。node

Failsafe Cluster失敗安全,出現異常時,直接忽略。一般用於寫入審計日誌等操做。算法

Failback Cluster失敗自動恢復,後臺記錄失敗請求,定時重發。一般用於消息通知操做。spring

Forking Cluster並行調用多個服務器,只要一個成功即返回。一般用於實時性要求較高的讀操做,但須要浪費更多服務資源。可經過 forks="2" 來設置最大並行數。apache

Broadcast Cluster廣播調用全部提供者,逐個調用,任意一臺報錯則報錯 2。一般用於通知全部提供者更新緩存或日誌等本地資源信息。編程

負載均衡

在集羣負載均衡時,Dubbo 提供了多種均衡策略,缺省爲random隨機調用。api

Random LoadBalance緩存

  • 隨機,按權重設置隨機機率。
  • 在一個截面上碰撞的機率高,但調用量越大分佈越均勻,並且按機率使用權重後也比較均勻,有利於動態調整提供者權重。

RoundRobin LoadBalancetomcat

  • 輪循,按公約後的權重設置輪循比率。
  • 存在慢的提供者累積請求的問題,好比:第二臺機器很慢,但沒掛,當請求調到第二臺時就卡在那,長此以往,全部請求都卡在調到第二臺上。

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: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時直接拋出異常而不是將任務放入阻塞隊列)

直連提供者

    在開發及測試環境下,常常須要繞過註冊中心,只測試指定服務提供者,這時候可能須要點對點直連,點對點直聯方式,將以服務接口爲單位,忽略註冊中心的提供者列表,A 接口配置點對點,不影響 B 接口從註冊中心獲取列表。

經過 XML 配置

<dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" />

 經過 -D 參數指定

在 JVM 啓動參數中加入-D參數映射服務地址 ,如:

java -Dcom.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" />
<dubbo:registry id="qdRegistry" address="10.20.141.150:9090" subscribe="false" />

或者

<dubbo:registry id="hzRegistry" address="10.20.153.10:9090" />
<dubbo:registry id="qdRegistry" address="10.20.141.150:9090?subscribe=false" />

 

多協議

Dubbo 容許配置多協議,在不一樣服務上支持不一樣協議或者同一服務上同時支持多種協議。

不一樣服務在性能上適用不一樣協議進行傳輸,好比大數據用短鏈接協議,小數據大併發用長鏈接協議。

<!-- 多協議配置 -->
    <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" />
</beans>

 

多註冊中心

    Dubbo 支持同一服務向多註冊中心同時註冊,或者不一樣服務分別註冊到不一樣的註冊中心上去,甚至能夠同時引用註冊在不一樣註冊中心上的同名服務。另外,註冊中心是支持自定義擴展的 。

<!-- 多註冊中心配置 -->
<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" />

 

服務分組

當一個接口有多種實現時,能夠用 group 區分。

服務

<dubbo:service group="feedback" interface="com.xxx.IndexService" />
<dubbo:service group="member" interface="com.xxx.IndexService" />

引用

<dubbo:reference id="feedbackIndexService" group="feedback" interface="com.xxx.IndexService" />
<dubbo:reference id="memberIndexService" group="member" interface="com.xxx.IndexService" />

任意組 1

<dubbo:reference id="barService" interface="com.foo.BarService" group="*" />

 

多版本

當一個接口實現,出現不兼容升級時,能夠用版本號過渡,版本號不一樣的服務相互間不引用。

能夠按照如下的步驟進行版本遷移:

  1. 在低壓力時間段,先升級一半提供者爲新版本
  2. 再將全部消費者升級爲新版本
  3. 而後將剩下的一半提供者升級爲新版本

version控制

分組聚合

    按組合並返回結果,好比菜單服務,接口同樣,但有多種實現,用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>

結果緩存

    結果緩存,用於加速熱門數據的訪問速度,Dubbo 提供聲明式緩存,以減小用戶加緩存的工做量。

 緩存類型

  • lru 基於最近最少使用原則刪除多餘緩存,保持最熱的數據被緩存。
  • threadlocal 當前線程緩存,好比一個頁面渲染,用到不少 portal,每一個 portal 都要去查用戶信息,經過線程緩存,能夠減小這種多餘訪問。
  • jcache 與 JSR107 集成,能夠橋接各類緩存實現。

配置

<dubbo:reference interface="com.foo.BarService" cache="lru" />

或:

<dubbo:reference interface="com.foo.BarService">
    <dubbo:method name="findBar" cache="lru" />
</dubbo:reference>

 

隱式參數

    能夠經過 RpcContext 上的 setAttachment 和 getAttachment 在服務消費方和提供方之間進行參數的隱式傳遞。

/user-guide/images/context.png

異步調用

    基於 NIO 的非阻塞實現並行調用,客戶端不須要啓動多線程便可完成並行調用多個遠程服務,相對多線程開銷較小。

/user-guide/images/future.jpg

在 consumer.xml 中配置:

<dubbo:reference id="fooService" interface="com.alibaba.foo.FooService">
      <dubbo:method name="findFoo" async="true" />
</dubbo:reference>

設置是否等待消息發出: 

  • sent="true" 等待消息發出,消息發送失敗將拋出異常。
  • sent="false" 不等待消息發出,將消息放入 IO 隊列,即刻返回。
<dubbo:method name="findFoo" async="true" sent="true" />

若是隻是想異步,徹底忽略返回值,能夠配置 return="false",以減小 Future 對象的建立和管理成本:

<dubbo:method name="findFoo" async="true" return="false" />

本地調用

    本地調用使用了 injvm 協議,是一個僞協議,它不開啓端口,不發起遠程調用,只在 JVM 內直接關聯,但執行 Dubbo 的 Filter 鏈。

配置

定義 injvm 協議

<dubbo:protocol name="injvm" />

設置默認協議

<dubbo:provider protocol="injvm" />

設置服務協議

<dubbo:service protocol="injvm" />

優先使用 injvm

<dubbo:consumer injvm="true" .../>
<dubbo:provider injvm="true" .../>

<dubbo:reference injvm="true" .../>
<dubbo:service injvm="true" .../>

注意:服務暴露與服務引用都須要聲明 injvm="true"

 

事件通知

    在調用以前、調用以後、出現異常時,會觸發 oninvokeonreturnonthrow 三個事件,能夠配置當事件發生時,通知哪一個類的哪一個方法 。

服務消費者 Callback 配置

<bean id ="demoCallback" class = "com.alibaba.dubbo.callback.implicit.NofifyImpl" />
<dubbo:reference id="demoService" interface="com.alibaba.dubbo.callback.implicit.IDemoService" version="1.0.0" group="cn" >
      <dubbo:method name="get" async="true" onreturn = "demoCallback.onreturn" onthrow="demoCallback.onthrow" />
</dubbo:reference>

callback 與 async 功能正交分解,async=true 表示結果是否立刻返回,onreturn 表示是否須要回調。

二者疊加存在如下幾種組合狀況 :

  • 異步回調模式:async=true onreturn="xxx"
  • 同步回調模式:async=false onreturn="xxx"
  • 異步無回調 :async=true
  • 同步無回調 :async=false

本地存根

    遠程服務後,客戶端一般只剩下接口,而實現全在服務器端,但提供方有些時候想在客戶端也執行部分邏輯,好比:作 ThreadLocal 緩存,提早驗證參數,調用失敗後僞造容錯數據等等,此時就須要在 API 中帶上 Stub,客戶端生成 Proxy 實例,會把 Proxy 經過構造函數傳給 Stub 1,而後把 Stub 暴露給用戶,Stub 能夠決定要不要去調 Proxy。

/user-guide/images/stub.jpg

在 spring 配置文件中按如下方式配置:

<dubbo:service interface="com.foo.BarService" stub="true" />

<dubbo:service interface="com.foo.BarService" stub="com.foo.BarServiceStub" />

Demo

package com.foo;
public class BarServiceStub implements BarService { 
    private final BarService barService;

    // 構造函數傳入真正的遠程代理對象
    public (BarService barService) {
        this.barService = barService;
    }

    public String sayHello(String name) {
        // 此代碼在客戶端執行, 你能夠在客戶端作ThreadLocal本地緩存,或預先驗證參數是否合法,等等
        try {
            return barService.sayHello(name);
        } catch (Exception e) {
            // 你能夠容錯,能夠作任何AOP攔截事項
            return "容錯數據";
        }
    }
}

 

本地假裝

本地假裝 1 一般用於服務降級,好比某驗權服務,當服務提供方所有掛掉後,客戶端不拋出異常,而是經過 Mock 數據返回受權失敗。

在 spring 配置文件中按如下方式配置:

<dubbo:reference interface="com.foo.BarService" mock="true" />

<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" />

Demo 在工程中提供 Mock 實現 :

package com.foo;
public class BarServiceMock implements BarService {
    public String sayHello(String name) {
        // 你能夠僞造容錯數據,此方法只在出現RpcException時被執行
        return "容錯數據";
    }
}

 

延遲暴露

若是你的服務須要預熱時間,好比初始化緩存,等待相關資源就位等,可使用 delay 進行延遲暴露。

延遲 5 秒暴露服務

<dubbo:service delay="5000" />

延遲到 Spring 初始化完成後,再暴露服務 1

<dubbo:service delay="-1" />

 

併發控制

務器端併發執行(或佔用線程池線程數)不能超過 10 個:

<dubbo:service interface="com.foo.BarService" executes="10" />

指定方法服務器端併發執行(或佔用線程池線程數)不能超過 10 個:

<dubbo:service interface="com.foo.BarService">
    <dubbo:method name="sayHello" executes="10" />
</dubbo:service>

客戶端併發執行(或佔用鏈接的請求數)不能超過 10 個:

<dubbo:service interface="com.foo.BarService" actives="10" />
<dubbo:reference interface="com.foo.BarService" actives="10" />

Load Balance 均衡

配置服務的客戶端的 loadbalance 屬性爲 leastactive,此 Loadbalance 會調用併發數最小的 Provider(Consumer端併發數)。

<dubbo:reference interface="com.foo.BarService" loadbalance="leastactive" />
<dubbo:service interface="com.foo.BarService" loadbalance="leastactive" />

 

鏈接控制

服務端鏈接控制

限制服務器端接受的鏈接不能超過 10 個 1

<dubbo:provider protocol="dubbo" accepts="10" />

<dubbo:protocol name="dubbo" accepts="10" />

客戶端鏈接控制

限制客戶端服務使用鏈接不能超過 10 個 2

<dubbo:reference interface="com.foo.BarService" connections="10" />

<dubbo:service interface="com.foo.BarService" connections="10" />

若是 <dubbo:service> 和 <dubbo:reference> 都配了 connections,<dubbo:reference> 優先

 

延遲鏈接

延遲鏈接用於減小長鏈接數。當有調用發起時,再建立長鏈接。1

<dubbo:protocol name="dubbo" lazy="true" />
1. 注意:該配置只對使用長鏈接的 dubbo 協議生效

 

粘滯鏈接

粘滯鏈接用於有狀態服務,儘量讓客戶端老是向同一提供者發起調用,除非該提供者掛了,再連另外一臺。

粘滯鏈接將自動開啓延遲鏈接,以減小長鏈接數。

<dubbo:protocol name="dubbo" sticky="true" />

 

令牌驗證

    經過令牌驗證在註冊中心控制權限,以決定要不要下發令牌給消費者,能夠防止消費者繞過註冊中心訪問提供者,另外經過註冊中心可靈活改變受權方式,而不需修改或升級提供者

/user-guide/images/dubbo-token.jpg

能夠全局設置開啓令牌驗證:

<!--隨機token令牌,使用UUID生成-->
<dubbo:provider interface="com.foo.BarService" token="true" />

<!--固定token令牌,至關於密碼-->
<dubbo:provider interface="com.foo.BarService" token="123456" />

還可在服務級別、協議級別設置:

優雅停機

    Dubbo 是經過 JDK 的 ShutdownHook 來完成優雅停機的,因此若是用戶使用 kill -9 PID 等強制關閉指令,是不會執行優雅停機的,只有經過 kill PID 時,纔會執行。

原理

服務提供方

  • 中止時,先標記爲不接收新請求,新請求過來時直接報錯,讓客戶端重試其它機器。
  • 而後,檢測線程池中的線程是否正在運行,若是有,等待全部線程執行完成,除非超時,則強制關閉。

服務消費方

  • 中止時,再也不發起新的調用請求,全部新的調用在客戶端即報錯。
  • 而後,檢測有沒有請求的響應尚未返回,等待響應返回,除非超時,則強制關閉。

設置方式

設置優雅停機超時時間,缺省超時時間是 10 秒,若是超時則強制關閉。

# dubbo.properties
dubbo.service.shutdown.wait=15000

若是 ShutdownHook 不能生效,能夠自行調用,使用tomcat等容器部署的場景,建議經過擴展ContextListener等自行調用如下代碼實現優雅停機

ProtocolConfig.destroyAll();

 

訪問日誌

若是你想記錄每一次請求信息,可開啓訪問日誌,相似於apache的訪問日誌。注意:此日誌量比較大,請注意磁盤容量。

將訪問日誌輸出到當前應用的log4j日誌:

<dubbo:protocol accesslog="true" />

將訪問日誌輸出到指定文件:

<dubbo:protocol accesslog="http://10.20.160.198/wiki/display/dubbo/foo/bar.log" />

 

服務容器

    服務容器是一個 standalone 的啓動程序,由於後臺服務不須要 Tomcat 或 JBoss 等 Web 容器的功能,若是硬要用 Web 容器去加載服務提供方,增長複雜性,也浪費資源。

    服務容器只是一個簡單的 Main 方法,並加載一個簡單的 Spring 容器,用於暴露服務。

    服務容器的加載內容能夠擴展,內置了 spring, jetty, log4j 等加載,可經過容器擴展點進行擴展。配置配在 java 命令的 -D 參數或者 dubbo.properties 中。

容器類型

Spring Container

  • 自動加載 META-INF/spring 目錄下的全部 Spring 配置。
  • 配置 spring 配置加載位置:

    dubbo.spring.config=classpath*:META-INF/spring/*.xml

Jetty Container

  • 啓動一個內嵌 Jetty,用於彙報狀態。
  • 配置:
    • dubbo.jetty.port=8080:配置 jetty 啓動端口
    • dubbo.jetty.directory=/foo/bar:配置可經過 jetty 直接訪問的目錄,用於存放靜態文件
    • dubbo.jetty.page=log,status,system:配置顯示的頁面,缺省加載全部頁面

Log4j Container

  • 自動配置 log4j 的配置,在多進程啓動時,自動給日誌文件按進程分目錄。
  • 配置:
    • dubbo.log4j.file=/foo/bar.log:配置日誌文件路徑
    • dubbo.log4j.level=WARN:配置日誌級別
    • dubbo.log4j.subdirectory=20880:配置日誌子目錄,用於多進程啓動,避免衝突

容器啓動

缺省只加載 spring

java com.alibaba.dubbo.container.Main

經過 main 函數參數傳入要加載的容器

java com.alibaba.dubbo.container.Main spring jetty log4j

經過 JVM 啓動參數傳入要加載的容器

java com.alibaba.dubbo.container.Main -Ddubbo.container=spring,jetty,log4j

經過 classpath 下的 dubbo.properties 配置傳入要加載的容器

dubbo.container=spring,jetty,log4j
相關文章
相關標籤/搜索