在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的命名规范。

相关文章
相关标签/搜索