在Dubbo中開發REST風格的遠程調用(RESTful Remoting)《續》

REST服務消費端詳解

這裏咱們用三種場景來分別討論:java

  1. 非dubbo的消費端調用dubbo的REST服務(non-dubbo --> dubbo)git

  2. dubbo消費端調用dubbo的REST服務 (dubbo --> dubbo)github

  3. dubbo的消費端調用非dubbo的REST服務 (dubbo --> non-dubbo)spring

場景1:非dubbo的消費端調用dubbo的REST服務

這種場景的客戶端與dubbo自己無關,直接選用相應語言和框架中合適的方式便可。json

若是是仍是java的客戶端(但沒用dubbo),能夠考慮直接使用標準的JAX-RS Client API或者特定REST實現的Client API來調用REST服務。下面是用JAX-RS Client API來訪問上述的UserService的registerUser():api

User user = new User();
user.setName("Larry");Client client = ClientBuilder.newClient();WebTarget target = client.target("http://localhost:8080/services/users/register.json");Response response = target.request().post(Entity.entity(user, MediaType.APPLICATION_JSON_TYPE));try {    if (response.getStatus() != 200) {        throw new RuntimeException("Failed with HTTP error code : " + response.getStatus());
    }    System.out.println("The generated id is " + response.readEntity(RegistrationResult.class).getId());
} finally {
    response.close();
    client.close(); // 在真正開發中不要每次關閉client,好比HTTP長鏈接是由client持有的}

上面代碼片斷中的User和RegistrationResult類都是消費端本身編寫的,JAX-RS Client API會自動對它們作序列化/反序列化。負載均衡

固然,在java中也能夠直接用本身熟悉的好比HttpClient,FastJson,XStream等等各類不一樣技術來實現REST客戶端,在此再也不詳述。框架

場景2:dubbo消費端調用dubbo的REST服務

這種場景下,和使用其餘dubbo的遠程調用方式同樣,直接在服務提供端和服務消費端共享Java服務接口,並添加spring xml配置(固然也能夠用spring/dubbo的annotation配置),便可透明的調用遠程REST服務:ide

<dubbo:reference id="userService" interface="xxx.UserService"/>

如前所述,這種場景下必須把JAX-RS的annotation添加到服務接口上,這樣在dubbo在消費端才能共享相應的REST配置信息,並據之作遠程調用:post

@Path("users")public interface UserService {    @GET
    @Path("{id : \\d+}")    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})    User getUser(@PathParam("id") Long id);
}

若是服務接口的annotation中配置了多種數據格式,這裏因爲兩端都是dubbo系統,REST的大量細節被屏蔽了,因此不存在用前述URL後綴之類選擇數據格式的可能。目前在這種狀況下,排名最靠前的數據格式將直接被使用。

所以,咱們建議你在定義annotation的時候最好把最合適的數據格式放到前面,好比以上咱們是把json放在xml前面,由於json的傳輸性能優於xml。

場景3:dubbo的消費端調用非dubbo的REST服務

這種場景下,能夠直接用場景1中描述的Java的方式來調用REST服務。但其實也能夠採用場景2中描述的方式,即更透明的調用REST服務,即便這個服務並非dubbo提供的。

若是用場景2的方式,因爲這裏REST服務並不是dubbo提供,通常也就沒有前述的共享的Java服務接口,因此在此咱們須要根據外部REST服務的狀況,本身來編寫Java接口以及相應參數類,並添加JAX-RS、JAXB、Jackson等的annotation,dubbo的REST底層實現會據此去自動生成請求消息,自動解析響應消息等等,從而透明的作遠程調用。或者這種方式也能夠理解爲,咱們嘗試用JAX-RS的方式去仿造實現一遍外部的REST服務提供端,而後把寫成服務接口放到客戶端來直接使用,dubbo的REST底層實現就能像調用dubbo的REST服務同樣調用其餘REST服務。

例如,咱們要調用以下的外部服務

http://api.foo.com/services/users/1001
http://api.foo.com/services/users/1002

獲取不一樣ID的用戶資料,返回格式是JSON

{    "id": 1001,    "name": "Larry"}

咱們可根據這些信息,編寫服務接口和參數類便可:

@Path("users")public interface UserService {    @GET
    @Path("{id : \\d+}")    @Produces({MediaType.APPLICATION_JSON})    User getUser(@PathParam("id") Long id);
}
public class User implements Serializable {    private Long id;    private String name;    // …}

對於spring中的配置,由於這裏的REST服務不是dubbo提供的,因此沒法使用dubbo的註冊中心,直接配置外部REST服務的url地址便可(如多個地址用逗號分隔):

<dubbo:reference id="userService" interface="xxx.UserService" url="rest://api.foo.com/services/"/>

注意:這裏協議必須用rest://而不是http://之類。若是外部的REST服務有context path,則在url中也必須添加上(除非你在每一個服務接口的@Path annotation中都帶上context path),例如上面的/services/。同時這裏的services後面必須帶上/,這樣才能使dubbo正常工做。

另外,這裏依然能夠配置客戶端可啓動的最大鏈接數和超時時間:

<dubbo:reference id="userService" interface="xxx.UserService" url="rest://api.foo.com/services/" timeout="2000" connections="10"/>

Dubbo中JAX-RS的限制

Dubbo中的REST開發是徹底兼容標準JAX-RS的,但其支持的功能目前是完整JAX-RS的一個子集,部分由於它要受限於dubbo和spring的特定體系。

在dubbo中使用的JAX-RS的侷限包括但不限於:

  1. 服務實現只能是singleton的,不能支持per-request scope和per-lookup scope

  2. 不支持用@Context  annotation對服務的實例字段注入 ServletConfig、ServletContext、HttpServletRequest、HttpServletResponse等等,但能夠支持對服務方法參數的注入。但對某些特定REST server實現,(祥見前面的敘述),也不支持對服務方法參數的注入。

REST常見問題解答(REST FAQ)

Dubbo REST的服務能和Dubbo註冊中心、監控中心集成嗎?

能夠的,並且是自動集成的,也就是你在dubbo中開發的全部REST服務都會自動註冊到服務冊中心和監控中心,能夠經過它們作管理。

可是,只有當REST的消費端也是基於dubbo的時候,註冊中心中的許多服務治理操做才能徹底起做用。而若是消費端是非dubbo的,天然不受註冊中心管理,因此其中不少操做是不會對消費端起做用的。

Dubbo REST中如何實現負載均衡和容錯(failover)?

若是dubbo REST的消費端也是dubbo的,則Dubbo REST和其餘dubbo遠程調用協議基本徹底同樣,由dubbo框架透明的在消費端作load balance、failover等等。

若是dubbo REST的消費端是非dubbo的,甚至是非java的,則最好配置服務提供端的軟負載均衡機制,目前可考慮用LVS、HAProxy、 Nginx等等對HTTP請求作負載均衡。

JAX-RS中重載的方法可以映射到同一URL地址嗎?

http://stackoverflow.com/questions/17196766/can-resteasy-choose-method-based-on-query-params

JAX-RS中做POST的方法可以接收多個參數嗎?

http://stackoverflow.com/questions/5553218/jax-rs-post-multiple-objects

Dubbo當前體系的不足之處(與REST相關的)

我認爲dubbo當前體系中顯然也有很多不足之處,這裏列出幾個與REST有關的、並影響用戶使用的問題(不包括內部實現的問題),供參考評論,爲下一步重構做準備。

RpcContext的侵入性

在前文,前面咱們已經提到過RpcContext用法的侵入性,因爲它是用單例的方式來訪問上下文信息,這徹底不符合spring應用的通常風格,不利於應用擴展和單元測試。將來咱們可能用依賴注入方式注入一個接口,再用它去訪問ThreadLocal中的上下文信息。

Protocol配置的侷限性

dubbo支持多種遠程調用方式,但全部調用方式都是用<dubbo:protocol/>來配置的,例如:

<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 RPC遠程調用方式特有的,不少dubbo中的其它遠程調用方式根本就不支持例如server, client, codec, iothreads, accepts, payload等等(固然,有的是條件所限不支持,有的是根本沒有必要支持)。這給用戶的使用徒增不少困惑,用戶也並不知道有些屬性(好比作性能調優)添加了其實是不起做用的。

另外一方面,各類遠程調用方式每每有大量本身獨特的配置須要,特別是咱們逐步爲每種遠程調用方式都添加更豐富、更高級的功能,這就不可避免的擴展<protocol/>中的屬性(例如目前咱們在REST中已經添加了keepalive和extension兩個屬性),到最後會致使<protocol/>臃腫不堪,用戶的使用也更加困惑。

固然,dubbo中有一種擴展<protocol/>的方式是用<dubbo:parameter/>,但這種方式顯然頗有侷限性,並且用法複雜,缺少schema校驗。

因此,最好的方式是爲每種遠程調用方式設置本身的protocol元素,好比<protocol-dubbo/><protocol-rest/>等等,每種元素用XML schema規定本身的屬性(固然屬性在各類遠程調用方式之間能通用是最好的)。

如此一來,例如前面提到過的extension配置也能夠用更自由的方式,從而更清楚更可擴展(如下只是舉例,固然也許有更好的方式):

<dubbo:protocol-rest port="8080">
    <dubbo:extension>someInterceptor</dubbo:extension>
    <dubbo:extension>someFilter</dubbo:extension>
    <dubbo:extension>someDynamicFeature</dubbo:extension>
    <dubbo:extension>someEntityProvider</dubbo:extension>
</dubbo:protocol-rest>

XML命名不符合spring規範

dubbo的XML配置中大量命名都不符合spring規範,好比:

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

上面threadpool應該改成thread-pool,iothreads應該改成io-threads,單詞之間應該用"-"分隔。這雖然看起來是個小問題,但也涉及到了可讀性,特別是可擴展性,由於有時候咱們不可避免要用更多單詞來描述XML元素和屬性。

其實dubbo自己也是建議遵照spring到XML的命名規範。

相關文章
相關標籤/搜索