天天看點

Dubbo API 筆記——Dubbo協定&最佳實踐服務化最佳實踐

Dubbo協定

dubbo://

Dubbo預設協定采用單一長連接配接和NIO異步通訊,适合于小資料量大并發的服務調用,以及服務消費者機器遠大于服務提供者機器數的情況

反之,Dubbo 預設協定不适合傳送大資料量的服務,比如傳檔案,傳視訊等,除非請求量很低

Dubbo API 筆記——Dubbo協定&最佳實踐服務化最佳實踐

特性

預設協定,使用基于 mina 1.1.7 和 hessian 3.2.1 的 tbremoting 互動

  • 連接配接個數:單連接配接
  • 連接配接方式:長連接配接
  • 傳輸協定:TCP
  • 傳輸方式:NIO 異步傳輸
  • 序列化:Hessian 二進制序列化
  • 适用範圍:傳入傳出參數資料包較小(建議小于100K),消費者比提供者個數多,單一消費者無法壓滿提供者,盡量不要用 dubbo 協定傳輸大檔案或超大字元串
  • 适用場景:正常遠端服務方法調用

限制

  • 參數及傳回值需實作 Serializable 接口
  • 參數及傳回值不能自定義實作 List, Map, Number, Date, Calendar 等接口,隻能用 JDK 自帶的實作,因為 hessian 會做特殊處理,自定義實作類中的屬性值都會丢失
  • Hessian 序列化,隻傳成員屬性值和值的類型,不傳方法或靜态變量,相容情況

資料通訊

情況

A->B

類A多一種屬性(或者說類B少一種屬性)

A->B

枚舉A多一種枚舉(或者說B少一種枚舉),A使用多出來的枚舉進行傳輸

A->B

枚舉A多一種枚舉(或者說B少一種枚舉),A不使用多出來的枚舉進行傳輸

A->B

A和B的屬性名相同,但類型不相同

A->B

serialId不相同

配置

配置協定

<dubbo:protocol name="dubbo" port="20880" />
           

配置協定選項

<dubbo:protocol name="dubbo" port="9090" server="netty" client="netty" codec="dubbo" serialization="hessian2" charset="UTF-8" threadpool="fixed" threads="100" queues="0" iothreads="9" buffer="8192" accepts="1000" payload="8388608" />
           

多連接配接配置

Dubbo 協定預設每服務每提供者每消費者使用單一長連接配接,如果資料量較大,可以使用多個連接配接

<dubbo:protocol name="dubbo" connections="2" />
           

為防止被大量連接配接撐挂,可在服務提供方限制大接收連接配接數,以實作服務提供方自我保護

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

rmi://

RMI 協定采用 JDK 标準的 java.rmi.* 實作,采用阻塞式短連接配接和 JDK 标準序列化方式

特性

  • 連接配接個數:多連接配接
  • 連接配接方式:短連接配接
  • 傳輸協定:TCP
  • 傳輸方式:同步傳輸
  • 序列化:Java 标準二進制序列化
  • 适用範圍:傳入傳出參數資料包大小混合,消費者與提供者個數差不多,可傳檔案
  • 适用場景:正常遠端服務方法調用,與原生RMI服務互操作

限制

  • 參數及傳回值需實作 Serializable 接口 dubbo 配置中的逾時時間對 RMI 無效,需使用 java 啟動參數設定:
    Dsun.rmi.transport.tcp.responseTimeout=3000 
               

配置

定義RMI協定

<dubbo:protocol name="rmi" port="1099" />
           

多端口

<dubbo:protocol id="rmi1" name="rmi" port="1099" /> 
<dubbo:protocol id="rmi2" name="rmi" port="2099" />

<dubbo:service  protocol="rmi1" />
           

hessian://

Hessian 協定用于內建 Hessian 的服務,Hessian 底層采用 Http 通訊,采用 Servlet 暴露服務,Dubbo 預設内嵌 Jetty 作為伺服器實作

特性

  • 連接配接個數:多連接配接
  • 連接配接方式:短連接配接
  • 傳輸協定:HTTP
  • 傳輸方式:同步傳輸
  • 序列化:Hessian二進制序列化
  • 适用範圍:傳入傳出參數資料包較大,提供者比消費者個數多,提供者壓力較大,可傳檔案
  • 适用場景:頁面傳輸,檔案傳輸,或與原生hessian服務互操作

依賴

<dependency>                
    <groupId>com.caucho</groupId>           
    <artifactId>hessian</artifactId>                
    <version>4.0.7</version> 
</dependency>
           

限制

  • 參數及傳回值需實作 Serializable 接口參數及傳回值不能自定義實作 List, Map, Number, Date, Calendar等接口,隻能用 JDK 自帶的實作,因為 hessian 會做特殊處理,自定義實作類中的屬性值都會丢失

配置

定義 hessian 協定

<dubbo:protocol name="hessian" port="8080" server="jetty" />
           

多端口

<dubbo:protocol id="hessian1" name="hessian" port="8080" />
<dubbo:protocol id="hessian2" name="hessian" port="8081" />
           

直連

<dubbo:reference id="helloService" inter url="hessian://10.20.153.10:8080/helloWorld" />
           

zookeeper注冊中心

Zookeeper 是 Apacahe Hadoop 的子項目,是一個樹型的目錄服務,支援變更推送,适合作為 Dubbo 服務的注冊中心,工業強度較高,可用于生産環境,并推薦使用

Dubbo API 筆記——Dubbo協定&amp;最佳實踐服務化最佳實踐

流程說明

  • 服務提供者啟動時: 向 /dubbo/com.foo.BarService/providers 目錄下寫入自己的 URL 位址
  • 服務消費者啟動時: 訂閱 /dubbo/com.foo.BarService/providers 目錄下的提供者 URL 位址。并向 /dubbo/com.foo.BarService/consumers 目錄下寫入自己的 URL 位址
  • 監控中心啟動時: 訂閱 /dubbo/com.foo.BarService 目錄下的所有提供者和消費者 URL 位址

支援以下功能

  • 當提供者出現斷電等異常停機時,注冊中心能自動删除提供者資訊
  • 當注冊中心重新開機時,能自動恢複注冊資料,以及訂閱請求
  • 當會話過期時,能自動恢複注冊資料,以及訂閱請求
  • 當設定 <dubbo:registry check=”false” /> 時,記錄失敗注冊和訂閱請求,背景定時重試
  • 可通過 <dubbo:registry username=”admin” password=”1234” /> 設定 zookeeper 登入資訊
  • 可通過 <dubbo:registry group=”dubbo” /> 設定 zookeeper 的根節點,不設定将使用無根樹
  • 支援 * 号通配符 <dubbo:reference group=”” version=”” />,可訂閱服務的所有分組和所有版本的提供者

使用

在 provider 和 consumer 中增加 zookeeper 用戶端 jar 包依賴

<dependency>                
    <groupId>org.apache.zookeeper</groupId>         
    <artifactId>zookeeper</artifactId>      
    <version>3.3.3</version> 
</dependency>
           

使用zkclient用戶端實作

預設配置

<dubbo:registry ... client="zkclient" />
           

需依賴

<dependency>                
    <groupId>com.github.sgroschupf</groupId>                
    <artifactId>zkclient</artifactId>               
    <version>0.1</version> 
</dependency>
           

Zookeeper 單機配置

<dubbo:registry protocol="zookeeper" address="10.20.153.10:2181" />
           

Zookeeper 叢集配置

<dubbo:registry protocol="zookeeper" address="10.20.153.10:2181,10.20.153.11:2181,10.2 0.153.12:2181" />
           

同一 Zookeeper,分成多組注冊中心

<dubbo:registry id="chinaRegistry" protocol="zookeeper" address="10.20.153.10:2181" group="china" /> 
<dubbo:registry id="intlRegistry" protocol="zookeeper" address="10.20.153.10:2181" group="intl" />
           

服務化最佳實踐

分包

建議将服務接口,服務模型,服務異常等均放在 API 包中,因為服務模型及異常也是 API 的一部分,同時,這樣做也符合分包原則:重用釋出等價原則(REP),共同重用原則(CRP)

如果需要,也可以考慮在 API 包中放置一份 spring 的引用配置,這樣使用方,隻需在 spring 加載過程中引用此配置即可,配置建議放在子產品的包目錄下,以免沖突, 如: com/alibaba/china/xxx/dubbo-reference.xml

粒度

服務接口盡可能大粒度,每個服務方法應代表一個功能,而不是某功能的一個步驟,否則将面臨分布式事務問題,Dubbo 暫未提供分布式事務支援

服務接口建議以業務場景為機關劃分,并對相近業務做抽象,防止接口數量爆炸

不建議使用過于抽象的通用接口,如: Map query(Map) ,這樣的接口沒有明确語義,會給後 期維護帶來不便

版本

每個接口都應定義版本号,為後續不相容更新提供可能,如:<dubbo:service interface=”com.xxx.XxxService” version=”1.0” />

建議使用兩位版本号,因為第三位版本号通常表示相容更新,隻有不相容時才需要變更服務版本

當不相容時,先更新一半提供者為新版本,再将消費者全部升為新版本,然後将剩下的一半提供者升為新版本

相容性

服務接口增加方法,或服務模型增加字段,可向後相容,删除方法或删除字段,将不相容,枚舉類型新增字段也不相容,需通過變更版本号更新

枚舉值

如果是完備集,可以用 Enum,比如: ENABLE, DISABLE

如果是業務種類,以後明顯會有類型增加,不建議用 Enum,可以用 String 代替

如果是在傳回值中用了 Enum,并新增了 Enum 值,建議先更新服務消費方,這樣服務提供方不會傳回新值

如果是在傳入參數中用了 Enum,并新增了 Enum 值,建議先更新服務提供方,這樣服務消費方不會傳入新值

序列化

服務參數及傳回值建議使用 POJO 對象,即通過 setter, getter 方法表示屬性的對象

服務參數及傳回值不建議使用接口,因為資料模型抽象的意義不大,并且序列化需要接口實作類的元資訊,并不能起到隐藏實作的意圖

服務參數及傳回值都必需是 byValue 的,而不能是 byReference 的,消費方和提供方的參數或傳回值引用并不是同一個,隻是值相同,Dubbo 不支援引用遠端對象

異常

建議使用異常彙報錯誤,而不是傳回錯誤碼,異常資訊能攜帶更多資訊,以及語義更友好

如果擔心性能問題,在必要時,可以通過 override 掉異常類的 fillInStackTrace() 方法為空方法,使其不拷貝棧資訊

查詢方法不建議抛出 checked 異常,否則調用方在查詢時将過多的 try…catch,并且不能進行有效處理

服務提供方不應将 DAO 或 SQL 等異常抛給消費方,應在服務實作中對消費方不關心的異常進行包裝,否則可能出現消費方無法反序列化相應異常

調用

不要隻是因為是 Dubbo 調用,而把調用 try…catch 起來。 try…catch 應該加上合适的 復原邊界上

對于輸入參數的校驗邏輯在 Provider 端要有。如有性能上的考慮,服務實作者可以考慮在 API 包上加上服務 Stub 類來完成檢驗

推薦用法

在Provider上盡量多配置Consumer端屬性

原因

  • 作服務的提供者,比服務使用方更清楚服務性能參數,如調用的逾時時間,合理的重試次數,等等
  • 在 Provider 配置後,Consumer 不配置則會使用 Provider 的配置值,即 Provider 配置可以作為 Consumer 的預設值。否則,Consumer 會使用 Consumer 端的全局設定,這對于 Provider 不可控的,并且往往是不合理的

示例

<dubbo:service inter version="1.0.0" ref="hel loService" timeout="300" retry="2" loadbalance="random" actives="0" />

<dubbo:service inter version="1.0.0" ref="helloService" timeout="300" retry="2" loadbalance="random" actives="0" >
    <dubbo:method name="findAllPerson" timeout="10000" retries="9" loadbalance="leastactive" actives="5" /> 
<dubbo:service/>
           

可在Provider上配置的Consumer屬性有

  • timeout 方法調用逾時
  • retries 失敗重試次數,預設是 2
  • loadbalance 負載均衡算法,預設是随機 random。還可以有輪詢 roundrobin、最不活躍優先 leastactive
  • actives 消費者端,最大并發調用限制,即當 Consumer 對一個服務的并發調用到上限後,新調用會 Wait 直到逾時,在方法上配置 dubbo:method 則并發限制針對方法,在接口上配置 dubbo:service ,則并發限制針對服務

Provider 上配置合理的 Provider 端屬性

<dubbo:protocol threads="200" />    
<dubbo:service inter version="1.0.0" ref="helloService" executes="200">                    
    <dubbo:method name="findAllPerson" executes="50" /> 
</dubbo:service>
           

Provider 上可以配置的 Provider 端屬性有

  • threads 服務線程池大小
  • executes 一個服務提供者并行執行請求上限,即當 Provider 對一個服務的并發調用到上限後,新調用會 Wait,這個時候Consumer可能會逾時。在方法上配置 dubbo:method 則并發限制針對方法,在接口上配置 dubbo:service,則并發限制針對服務

配置Dubbo緩存檔案

提供者清單緩存檔案

<dubbo:registry file="${user.home}/output/dubbo.cache" />
           

這個檔案會緩存注冊中心的清單和服務提供者清單。有了這項配置後,當應用重新開機過程中,Dubbo 注冊中心不可用時則應用會從這個緩存檔案讀取服務提供者清單的資訊,進一步保證應用可靠性