Dubbo設計與實現——Dubbo支持的協議的詳解

Dubbo支持dubbo、rmi、hessian、http、webservice、thrift、redis等多種協議,可是Dubbo官網是推薦咱們使用Dubbo協議的。下面咱們就針對Dubbo的每種協議詳解講解,以便咱們在實際應用中可以正確取捨。html

Dubbo協議

缺省協議,使用基於mina1.1.7+hessian3.2.1的tbremoting交互。 
鏈接個數:單鏈接 
鏈接方式:長鏈接 
傳輸協議:TCP 
傳輸方式:NIO異步傳輸 
序列化:Hessian二進制序列化 
適用範圍:傳入傳出參數數據包較小(建議小於100K),消費者比提供者個數多,單一消費者沒法壓滿提供者,儘可能不要用dubbo協議傳輸大文件或超大字符串。 
適用場景:常規遠程服務方法調用java

一、dubbo默認採用dubbo協議,dubbo協議採用單一長鏈接和NIO異步通信,適合於小數據量大併發的服務調用,以及服務消費者機器數遠大於服務提供者機器數的狀況 
二、他不適合傳送大數據量的服務,好比傳文件,傳視頻等,除非請求量很低。 
配置以下:web

<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」 />

三、Dubbo協議缺省每服務每提供者每消費者使用單一長鏈接,若是數據量較大,可使用多個鏈接。redis

<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」>表示該服務使用獨立兩條長鏈接。 
四、爲防止被大量鏈接撐掛,可在服務提供方限制大接收鏈接數,以實現服務提供方自我保護spring

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

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


爲何不能傳大包? 
    因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

RMI協議

Java標準的遠程調用協議。 
鏈接個數:多鏈接 
鏈接方式:短鏈接 
傳輸協議:TCP 
傳輸方式:同步傳輸 
序列化:Java標準二進制序列化 
適用範圍:傳入傳出參數數據包大小混合,消費者與提供者個數差很少,可傳文件。 
適用場景:常規遠程服務方法調用,與原生RMI服務互操做網絡

一、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" />

Hessian協議

基於Hessian的遠程調用協議。 
鏈接個數:多鏈接 
鏈接方式:短鏈接 
傳輸協議:HTTP 
傳輸方式:同步傳輸 
序列化:表單序列化 
適用範圍:傳入傳出參數數據包大小混合,提供者比消費者個數多,可用瀏覽器查看,可用表單或URL傳入參數,暫不支持傳文件。 
適用場景:需同時給應用程序和瀏覽器JS使用的服務。

一、Hessian協議用於集成Hessian的服務,Hessian底層採用Http通信,採用Servlet暴露服務,Dubbo缺省內嵌Jetty做爲服務器實現。 
二、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協議調用。 
約束 
一、參數及返回值需實現Serializable接口 
二、參數及返回值不能自定義實現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派發請求 
協議的端口<dubbo:protocol port="8080" />必須與servlet容器的端口相同, 
協議的上下文路徑<dubbo:protocol contextpath="foo" />必須與servlet應用的上下文路徑相同。

http協議

基於http表單的遠程調用協議。參見:[HTTP協議使用說明] 
鏈接個數:多鏈接 
鏈接方式:短鏈接 
傳輸協議:HTTP 
傳輸方式:同步傳輸 
序列化:表單序列化 
適用範圍:傳入傳出參數數據包大小混合,提供者比消費者個數多,可用瀏覽器查看,可用表單或URL傳入參數,暫不支持傳文件。 
適用場景:需同時給應用程序和瀏覽器JS使用的服務。

一、採用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應用的上下文路徑相同。

webservice協議

基於WebService的遠程調用協議。 
鏈接個數:多鏈接 
鏈接方式:短鏈接 
傳輸協議:HTTP 
傳輸方式:同步傳輸 
序列化:SOAP文本序列化 
適用場景:系統集成,跨語言調用

一、基於CXF的frontend-simple和transports-http實現。 
二、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派發請求: 
協議的端口<dubbo:protocol port="8080" />必須與servlet容器的端口相同, 
協議的上下文路徑<dubbo:protocol contextpath="foo" />必須與servlet應用的上下文路徑相同。

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

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" />

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" />
相關文章
相關標籤/搜索