1. 程式人生 > >dubbo 支援的7種協議

dubbo 支援的7種協議

Dubbo支援dubbo、rmi、hessian、http、webservice、thrift、redis等多種協議,但是Dubbo官網是推薦我們使用Dubbo協議的。

下面我們就針對Dubbo的每種協議詳解講解,以便我們在實際應用中能夠正確取捨。

1、dubbo 協議 (預設)

預設協議,使用基於mina1.1.7+hessian3.2.1的tbremoting互動。

  • 連線個數:單連線
  • 連線方式:長連線
  • 傳輸協議:TCP
  • 傳輸方式:NIO非同步傳輸
  • 序列化:Hessian 二進位制序列化
  • 適用範圍:傳入傳出引數資料包較小(建議小於100K),消費者比提供者個數多,單一消費者無法壓滿提供者,儘量不要用dubbo協議傳輸大檔案或超大字串。
  • 適用場景:常規遠端服務方法呼叫

1、dubbo預設採用dubbo協議,dubbo協議採用單一長連線和NIO非同步通訊,適合於小資料量大併發的服務呼叫,以及服務消費者機器數遠大於服務提供者機器數的情況
2、他不適合傳送大資料量的服務,比如傳檔案,傳視訊等,除非請求量很低。

配置如下:

<dubbo:protocol name="dubbo" port="20880" />
<!-- Set default protocol: -->
<dubbo:provider protocol="dubbo" />
<~-- Set service protocol
-->
<dubbo:service protocol="dubbo" /> <!-- Multi port --> <dubbo:protocol id="dubbo1" name="dubbo" port="20880" /> <dubbo:protocol id="dubbo2" name="dubbo" port="20881" />. <!-- Dubbo protocol options: --> <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” />

3、Dubbo協議預設每服務每提供者每消費者使用單一長連線,如果資料量較大,可以使用多個連線。

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

<dubbo:service connections=”0”><dubbo:reference connections=”0”>表示該服務使用JVM共享長連線。(預設)
<dubbo:service connections=”1”><dubbo:reference connections=”1”>表示該服務使用獨立長連線。
<dubbo:service connections=”2”><dubbo:reference connections=”2”>表示該服務使用獨立兩條長連線。

4、為防止被大量連線撐掛,可在服務提供方限制大接收連線數,以實現服務提供方自我保護

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

為什麼要消費者比提供者個數多?
因dubbo協議採用單一長連線,假設網路為千兆網絡卡(1024Mbit=128MByte),根據測試經驗資料每條連線最多隻能壓滿7MByte(不同的環境可能不一樣,供參考),理論上1個服務提供者需要20個服務消費者才能壓滿網絡卡

為什麼不能傳大包?
因dubbo協議採用單一長連線,如果每次請求的資料包大小為500KByte,假設網路為千兆網絡卡(1024Mbit=128MByte),每條連線最大7MByte(不同的環境可能不一樣,供參考),單個服務提供者的TPS(每秒處理事務數)最大為:128MByte / 500KByte = 262。單個消費者呼叫單個服務提供者的TPS(每秒處理事務數)最大為:7MByte / 500KByte = 14。如果能接受,可以考慮使用,否則網路將成為瓶頸。

為什麼採用非同步單一長連線?
因為服務的現狀大都是服務提供者少,通常只有幾臺機器,而服務的消費者多,可能整個網站都在訪問該服務,比如Morgan的提供者只有6臺提供者,卻有上百臺消費者,每天有1.5億次呼叫,如果採用常規的hessian服務,服務提供者很容易就被壓跨,通過單一連線,保證單一消費者不會壓死提供者,長連線,減少連線握手驗證等,並使用非同步IO,複用執行緒池,防止C10K問題。

介面增加方法,對客戶端無影響,如果該方法不是客戶端需要的,客戶端不需要重新部署;
輸入引數和結果集中增加屬性,對客戶端無影響,如果客戶端並不需要新屬性,不用重新
部署;

輸入引數和結果集屬性名變化,對客戶端序列化無影響,但是如果客戶端不重新部署,不管輸入還是輸出,屬性名變化的屬性值是獲取不到的。

總結:伺服器端 和 客戶端 對 領域物件 並不需要完全一致,而是按照最大匹配原則。

如果不是整合Spring,單獨配置如下:

dubbo.service.protocol=dubbo

2、rmi 協議

Java標準的遠端呼叫協議。

  • 連線個數:多連線
  • 連線方式:短連線
  • 傳輸協議:TCP
  • 傳輸方式:同步傳輸
  • 序列化:Java標準二進位制序列化
  • 適用範圍:傳入傳出引數資料包大小混合,消費者與提供者個數差不多,可傳檔案。
  • 適用場景:常規遠端服務方法呼叫,與原生RMI服務互操作

1、RMI協議採用JDK標準的java.rmi.*實現,採用阻塞式短連線JDK標準序列化方式
注:
如果正在使用RMI提供服務給外部訪問(公司內網環境應該不會有攻擊風險),同時應用裡依賴了老的common-collections包(dubbo不會依賴這個包,請排查自己的應用有沒有使用)的情況下,存在反序列化安全風險。
請檢查應用:
將commons-collections3 請升級到3.2.2版本:
https://commons.apache.org/proper/commons-collections/release_3_2_2.html
將commons-collections4 請升級到4.1版本:https://commons.apache.org/proper/commons-collections/release_4_1.html
新版本的commons-collections解決了該問題

如果服務介面繼承了java.rmi.Remote介面,可以和原生RMI互操作,即:
提供者用Dubbo的RMI協議暴露服務,消費者直接用標準RMI介面呼叫,或者提供方用標準RMI暴露服務,消費方用Dubbo的RMI協議呼叫。

如果服務介面沒有繼承java.rmi.Remote介面,預設Dubbo將自動生成一個com.xxx.XxxService$Remote的介面,並繼承java.rmi.Remote介面,並以此介面暴露服務,

但如果設定了<dubbo:protocol name="rmi" codec="spring" />,將不生成$Remote介面,而使用Spring的RmiInvocationHandler介面暴露服務,和Spring相容。

<!-- Define rmi protocol -->
<dubbo:protocol name="rmi" port="1099" />.
<!-- Set default protocol: -->
<dubbo:provider protocol="rmi" />
<!-- Set service protocol: -->
<dubbo:service protocol="rmi" />
<!-- Multi port -->
<dubbo:protocol id="rmi1" name="rmi" port="1099" />
<dubbo:protocol id="rmi2" name="rmi" port="2099" />
<dubbo:service protocol="rmi1" />
<!-- Spring compatible: -->
<dubbo:protocol name="rmi" codec="spring" />

3、hessian 協議

基於Hessian的遠端呼叫協議。

  • 連線個數:多連線
  • 連線方式:短連線
  • 傳輸協議:HTTP
  • 傳輸方式:同步傳輸
  • 序列化:表單序列化
  • 適用範圍:傳入傳出引數資料包大小混合,提供者比消費者個數多,可用瀏覽器檢視,可用表單或URL傳入引數,暫不支援傳檔案。
  • 適用場景:需同時給應用程式和瀏覽器JS使用的服務。

1、Hessian協議用於整合Hessian的服務,Hessian底層採用Http通訊,採用Servlet暴露服務,Dubbo預設內嵌Jetty作為伺服器實現。
2、Hessian是Caucho開源的一個RPC框架:http://hessian.caucho.com,其通訊效率高於WebService和Java自帶的序列化。
依賴:

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

可以和原生Hessian服務互操作,即:
提供者用Dubbo的Hessian協議暴露服務,消費者直接用標準Hessian介面呼叫,或者提供方用標準Hessian暴露服務,消費方用Dubbo的Hessian協議呼叫。

約束
1、引數及返回值需實現Serializable介面
2、引數及返回值不能自定義實現List, Map, Number, Date, Calendar等介面,只能用JDK自帶的實現,因為hessian會做特殊處理,自定義實現類中的屬性值都會丟失。

<!-- Define hessian protocol: -->
<dubbo:protocol name="hessian" port="8080" server="jetty" />
<!-- Set default protocol: -->
<dubbo:provider protocol="hessian" />
<!-- Set service protocol: -->
<dubbo:service protocol="hessian" />
<!-- Multi port: -->
<dubbo:protocol id="hessian1" name="hessian" port="8080" />
<dubbo:protocol id="hessian2" name="hessian" port="8081" />
<!-- Directly provider: -->
<dubbo:reference id="helloService" interface="HelloWorld" url="hessian://10.20.153.10:8080/helloWorld" />
<!-- Jetty Server -->
<dubbo:protocol ... server="jetty" />
<!-- Servlet Bridge Server -->
<dubbo:protocol ... server="servlet" />

web.xml配置:

<servlet>
     <servlet-name>dubbo</servlet-name>
     <servlet-class>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</servlet-class>
     <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
     <servlet-name>dubbo</servlet-name>
     <url-pattern>/*</url-pattern>
</servlet-mapping>

注意:如果使用servlet派發請求
協議的埠必須與servlet容器的埠相同,
協議的上下文路徑必須與servlet應用的上下文路徑相同。

4、http 協議

基於http表單的遠端呼叫協議。參見:[HTTP協議使用說明]

  • 連線個數:多連線
  • 連線方式:短連線
  • 傳輸協議:HTTP
  • 傳輸方式:同步傳輸
  • 序列化:表單序列化
  • 適用範圍:傳入傳出引數資料包大小混合,提供者比消費者個數多,可用瀏覽器檢視,可用表單或URL傳入引數,暫不支援傳檔案。
  • 適用場景:需同時給應用程式和瀏覽器JS使用的服務。

1、採用Spring的 HttpInvoker 實現
配置

<dubbo:protocol name="http" port="8080" />
<!-- Jetty Server -->
<dubbo:protocol ... server="jetty" />
<!-- Servlet Bridge Server -->
<dubbo:protocol ... server="servlet" />

web.xml配置

<servlet>
     <servlet-name>dubbo</servlet-name>
     <servlet-class>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</servlet-class>
     <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
     <servlet-name>dubbo</servlet-name>
     <url-pattern>/*</url-pattern>
</servlet-mapping>

注意:如果使用 servlet 派發請求
協議的埠<dubbo:protocol port="8080" />必須與servlet容器的埠相同,
協議的上下文路徑<dubbo:protocol contextpath="foo" />必須與servlet應用的上下文路徑相同。

5、webservice 協議

基於WebService的遠端呼叫協議

  • 連線個數:多連線
  • 連線方式:短連線
  • 傳輸協議:HTTP
  • 傳輸方式:同步傳輸
  • 序列化:SOAP文字序列化
  • 適用場景:系統整合,跨語言呼叫

1、基於CXF的 frontend-simple 和 transports-http 實現。
2、CXF是Apache開源的一個RPC框架:http://cxf.apache.org,由Xfire和Celtix合併而來 。

依賴

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-simple</artifactId>
    <version>2.6.1</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>2.6.1</version>
</dependency>

可以和原生WebService服務互操作,即:
提供者用Dubbo的WebService協議暴露服務,消費者直接用標準WebService介面呼叫,或者提供方用標準WebService暴露服務,消費方用Dubbo的WebService協議呼叫。

約束:
引數及返回值需實現Serializable介面
引數儘量使用基本型別和POJO。

<!-- Define webservice protocol -->
<dubbo:protocol name="webservice" port="8080" server="jetty" />
<!-- Set default protocol -->
<dubbo:provider protocol="webservice" />
<!-- Set service protocol -->
<dubbo:service protocol="webservice" />
<!-- Multi port -->
<dubbo:protocol id="webservice1" name="webservice" port="8080" />
<dubbo:protocol id="webservice2" name="webservice" port="8081" />
<!-- Directly provider -->
<dubbo:reference id="helloService" interface="HelloWorld" url="webservice://10.20.153.10:8080/com.foo.HelloWorld" />
<!-- WSDL -->
http://10.20.153.10:8080/com.foo.HelloWorld?wsdl
<!-- Jetty Server: (default) -->
<dubbo:protocol ... server="jetty" />
<!-- Servlet Bridge Server: (recommend) -->
<dubbo:protocol ... server="servlet" />

web.xml

<servlet>
     <servlet-name>dubbo</servlet-name>
     <servlet-class>com.alibaba.dubbo.remoting.http.servlet.DispatcherServlet</servlet-class>
     <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
     <servlet-name>dubbo</servlet-name>
     <url-pattern>/*</url-pattern>
</servlet-mapping>

注意:如果使用servlet派發請求:
協議的埠必須與servlet容器的埠相同,
協議的上下文路徑必須與servlet應用的上下文路徑相同。

6、thrift 協議

當前 dubbo 支援的 thrift 協議是對 thrift 原生協議的擴充套件,在原生協議的基礎上添加了一些額外的頭資訊,比如service name,magic number等。使用dubbo thrift協議同樣需要使用thrift的idl compiler編譯生成相應的java程式碼,後續版本中會在這方面做一些增強
依賴

<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.8.0</version>
</dependency>

配置:

<dubbo:protocol name="thrift" port="3030" />

Thrift不支援null值,不能在協議中傳null

7、memcached 協議

可以通過指令碼或監控中心手工填寫表單註冊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&group=member&loadbalance=consistenthash"));

然後在客戶端使用時,不需要感知Memcached的地址:

<dubbo:reference id="cache" 
            interface="http://10.20.160.198/wiki/display/dubbo/java.util.Map" group="member" />

或者點對點直連

<dubbo:reference id="cache" 
            interface="http://10.20.160.198/wiki/display/dubbo/java.util.Map" 
            url="memcached://10.20.153.10:11211" />

自定義介面

<dubbo:reference id="cache" 
            interface="com.foo.CacheService" url="memcached://10.20.153.10:11211" />

方法名建議和memcached的標準方法名相同,即:get(key), set(key, value), delete(key)。
如果方法名和memcached的標準方法名不相同,則需要配置對映關係:(其中”p:xxx”為spring的標準p標籤)

<dubbo:reference id="cache" interface="com.foo.CacheService" 
                url="memcached://10.20.153.10:11211" p:set="putFoo" 
                p:get="getFoo" p:delete="removeFoo" />

8、redis 協議

可以通過指令碼或監控中心手工填寫表單註冊redis服務的地址:

RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("redis://10.20.153.11/com.foo.BarService?category=providers&dynamic=false&application=foo&group=member&loadbalance=consistenthash"));

然後在客戶端使用時,不需要感知Redis的地址:

<dubbo:reference id="store" 
            interface="http://10.20.160.198/wiki/display/dubbo/java.util.Map" group="member" />

點對點直連

<dubbo:reference id="store" 
            interface="http://10.20.160.198/wiki/display/dubbo/java.util.Map" 
            url="redis://10.20.153.10:6379" />

自定義介面

<dubbo:reference id="store" interface="com.foo.StoreService" url="redis://10.20.153.10:6379" />

方法名建議和redis的標準方法名相同,即:get(key), set(key, value), delete(key)。
如果方法名和redis的標準方法名不相同,則需要配置對映關係:(其中”p:xxx”為spring的標準p標籤)

<dubbo:reference id="cache" interface="com.foo.CacheService" 
            url="memcached://10.20.153.10:11211"
             p:set="putFoo" p:get="getFoo" p:delete="removeFoo" />

面試題:

1、dubbo 預設使用什麼序列化框架,你知道的還有哪些?
預設使用Hessian序列化。還有Duddo、FastJson、Java自帶序列化。

2、dubbo推薦用什麼協議?
預設使用dubbo協議。