聊聊Dubbox(二):简单入门

原文:http://www.jianshu.com/p/c602b347de88

0. 准备

  1. 安装注册中心:Zookeeper、Dubbox自带的dubbo-registry-simple;
  2. 安装DubboKeeper监控:https://github.com/dubboclub/dubbokeeper;

以上两点准备,不是本文重点,不做详细介绍,安装比较简单,自行查阅相关资料学习。

1. 服务端

1.2 接口定义

  1. 创建Maven模块:msa-demo-api


    msa-demo-api
  2. msa-demo-api:配置pom.xml

    <!-- Dubbox依赖 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>dubbo</artifactId>
      <version>2.8.4</version>
    </dependency>
    <!-- END -->
    
    <!-- 如果要使用lombok -->
    <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
    </dependency>
    <!-- END -->
    
    <!-- 如果要使用REST风格远程调用 -->
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jaxrs</artifactId>
      <version>3.0.7.Final</version>
    </dependency>
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-client</artifactId>
      <version>3.0.7.Final</version>
    </dependency>
    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>1.0.0.GA</version>
    </dependency>
    <!-- END -->
    
    <!-- 如果要使用json序列化 -->
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jackson-provider</artifactId>
      <version>3.0.7.Final</version>
    </dependency>
    <!-- END -->
    
    <!-- 如果要使用xml序列化 -->
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jaxb-provider</artifactId>
      <version>3.0.7.Final</version>
    </dependency>
    <!-- END -->
    
    <!-- 如果要使用netty server -->
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-netty</artifactId>
      <version>3.0.7.Final</version>
    </dependency>
    <!-- END -->
    
    <!-- 如果要使用Sun HTTP server -->
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jdk-http</artifactId>
      <version>3.0.7.Final</version>
    </dependency>
    <!-- END -->
    
    <!-- 如果要使用tomcat server -->
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-core</artifactId>
      <version>8.0.11</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-logging-juli</artifactId>
      <version>8.0.11</version>
    </dependency>
    <!-- END -->
    
    <!-- 如果要使用Kyro序列化 -->
    <dependency>
      <groupId>com.esotericsoftware.kryo</groupId>
      <artifactId>kryo</artifactId>
      <version>2.24.0</version>
    </dependency>
    <dependency>
      <groupId>de.javakaffee</groupId>
      <artifactId>kryo-serializers</artifactId>
      <version>0.26</version>
    </dependency>
    <!-- END -->
    
    <!-- 如果要使用FST序列化 -->
    <dependency>
      <groupId>de.ruedigermoeller</groupId>
      <artifactId>fst</artifactId>
      <version>1.55</version>
    </dependency>
    <!-- END -->
    
    <!-- 如果要使用Jackson序列化 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.3.3</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.3.3</version>
    </dependency>
    <!-- END -->

    以上POM配置,从dubbox-2.8.4开始,所有依赖库的使用方式将和dubbo原来的一样:即如果要使用REST、Kyro、FST、Jackson等功能,需要用户自行手工添加相关的依赖。

  3. 定义接口:UserService.java

    /**
     * @author TaoBangren
     * @version 1.0
     * @since 2017/5/17 上午9:26
     */
    public interface UserService {
       User getUser(Long id);
       Long registerUser(User user);
    }
  4. 定义REST接口:AnotherUserRestService.java

    package com.alibaba.dubbo.demo.user.facade;
    import com.alibaba.dubbo.demo.user.User;
    import com.alibaba.dubbo.rpc.protocol.rest.support.ContentType;
    import javax.validation.constraints.Min;
    import javax.ws.rs.*;
    import javax.ws.rs.core.MediaType;
    /**
     * @author TaoBangren
     * @version 1.0
     * @since 2017/5/17 上午9:26
     */
    // 在Dubbo中开发REST服务主要都是通过JAX-RS的annotation来完成配置的,
    // 在上面的示例中,我们都是将annotation放在服务的实现类中。但其实,我
    // 们完全也可以将annotation放到服务的接口上,这两种方式是完全等价的.
    //
    // 在一般应用中,我们建议将annotation放到服务实现类,这样annotation和
    // java实现代码位置更接近,更便于开发和维护。另外更重要的是,我们一般倾向
    // 于避免对接口的污染,保持接口的纯净性和广泛适用性。
    // 但是,如后文所述,如果我们要用dubbo直接开发的消费端来访问此服务,则annotation必须放到接口上。
    // 如果接口和实现类都同时添加了annotation,则实现类的annotation配置会生效,接口上的annotation被直接忽略。
    @Path("u")
    @Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
    @Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8})
    public interface AnotherUserRestService {
    
       @GET
       @Path("{id : \\d+}")
       // 在一个REST服务同时对多种数据格式支持的情况下,根据JAX-RS标准,
       // 一般是通过HTTP中的MIME header(content-type和accept)来指定当前想用的是哪种格式的数据。
       // @Produces({ContentType.APPLICATION_JSON_UTF_8, ContentType.TEXT_XML_UTF_8})
       // 但是在dubbo中,我们还自动支持目前业界普遍使用的方式,即用一个URL后缀(.json和.xml)来指定
       // 想用的数据格式。例如,在添加上述annotation后,直接访问http://localhost:8888/users/1001.json
       // 则表示用json格式,直接访问http://localhost:8888/users/1002.xml则表示用xml格式,
       // 比用HTTP Header更简单直观。Twitter、微博等的REST API都是采用这种方式。
       // 如果你既不加HTTP header,也不加后缀,则dubbo的REST会优先启用在以上annotation定义中排位最靠前的那种数据格式。
       // 注意:这里要支持XML格式数据,在annotation中既可以用MediaType.TEXT_XML,也可以用MediaType.APPLICATION_XML,
       // 但是TEXT_XML是更常用的,并且如果要利用上述的URL后缀方式来指定数据格式,只能配置为TEXT_XML才能生效。
       User getUser(@PathParam("id") @Min(1L) Long id);
    
       @POST
       @Path("register")
       RegistrationResult registerUser(User user);
    }
  5. 定义实体:User.java

    package com.alibaba.dubbo.demo.user;
    import lombok.Data;
    import org.codehaus.jackson.annotate.JsonProperty;
    import javax.validation.constraints.Min;
    import javax.validation.constraints.NotNull;
    import javax.validation.constraints.Size;
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlRootElement;
    import java.io.Serializable;
    /**
     * @author TaoBangren
     * @version 1.0
     * @since 2017/5/17 上午9:26
     */
    // 由于JAX-RS的实现一般都用标准的JAXB(Java API for XML Binding)来序列化和反序列化XML格式数据,
    // 所以我们需要为每一个要用XML传输的对象添加一个类级别的JAXB annotation(@XmlRootElement) ,否则序列化将报错。
    @Data
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class User implements Serializable {
    
       @NotNull
       @Min(1L)
       private Long id;
    
       // REST的底层实现会在service的对象和JSON/XML数据格式之间自动做序列化/反序列化。
       // 但有些场景下,如果觉得这种自动转换不满足要求,可以对其做定制。
       // Dubbo中的REST实现是用JAXB做XML序列化,用Jackson做JSON序列化,
       // 所以在对象上添加JAXB或Jackson的annotation即可以定制映射。
       @JsonProperty("username")
       @XmlElement(name = "username")
       @NotNull
       @Size(min = 6, max = 50)
       private String name;
    }
  6. 定义REST响应结果实体:RegistrationResult.java

    package com.alibaba.dubbo.demo.user.facade;
    import lombok.Data;
    import javax.xml.bind.annotation.XmlRootElement;
    import java.io.Serializable;
    /**
     * @author TaoBangren
     * @version 1.0
     * @since 2017/5/17 上午9:26
     */
    // 此外,如果service方法中的返回值是Java的 primitive类型(如int,long,float,double等),
    // 最好为它们添加一层wrapper对象,因为JAXB不能直接序列化primitive类型。这样不但能够解决XML序列化的问题,
    // 而且使得返回的数据都符合XML和JSON的规范。
    // 这种wrapper对象其实利用所谓Data Transfer Object(DTO)模式,采用DTO还能对传输数据做更多有用的定制。
    @Data
    @XmlRootElement
    public class RegistrationResult implements Serializable {
       private Long id;
    }

1.3 服务实现

  1. 创建Maven模块:msa-demo-provider


    msa-demo-provider
  2. msa-demo-provider:配置pom.xml

    <!-- Module依赖 START -->
    <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>msa-demo-api</artifactId>
       <version>1.0-SNAPSHOT</version>
    </dependency>
    <!-- Module依赖 END -->
  3. 实现UserService接口:UserServiceImpl.java

    package com.alibaba.dubbo.demo.user;
    import lombok.extern.slf4j.Slf4j;
    import java.util.concurrent.atomic.AtomicLong;
    /**
     * @author TaoBangren
     * @version 1.0
     * @since 2017/5/17 上午9:26
     */
    @Slf4j
    public class UserServiceImpl implements UserService {
       private final AtomicLong idGen = new AtomicLong();
       public User getUser(Long id) {
           User user = new User();
           user.setId(id);
           user.setName("username" + id);
           return user;
       }
    
       public Long registerUser(User user) {
           // System.out.println("Username is " + user.getName());
           return idGen.incrementAndGet();
       }
    }
  4. 实现REST接口AnotherUserRestService:AnotherUserRestServiceImpl.java

    package com.alibaba.dubbo.demo.user.facade;
    import com.alibaba.dubbo.demo.user.User;
    import com.alibaba.dubbo.demo.user.UserService;
    import com.alibaba.dubbo.rpc.RpcContext;
    import lombok.extern.slf4j.Slf4j;
    /**
     * @author TaoBangren
     * @version 1.0
     * @since 2017/5/17 上午9:26
     */
    @Slf4j
    public class AnotherUserRestServiceImpl implements AnotherUserRestService {
    
       private UserService userService;
    
       public void setUserService(UserService userService) {
           this.userService = userService;
       }
    
       public User getUser(Long id) {
           System.out.println("Client name is " + RpcContext.getContext().getAttachment("clientName"));
           System.out.println("Client impl is " + RpcContext.getContext().getAttachment("clientImpl"));
           return userService.getUser(id);
       }
    
       public RegistrationResult registerUser(User user) {
           Long id = userService.registerUser(user);
           RegistrationResult registrationResult = new RegistrationResult();
           registrationResult.setId(id);
           return registrationResult;
       }
    }
  5. Dubbox与Spring集成配置:msa-demo-provider.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
         xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
         http://code.alibabatech.com/schema/dubbo
         http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    
      <!-- 当前应用信息配置 -->
      <dubbo:application name="msa-demo-provider" owner="tbr" organization="tbr"/>
      <dubbo:monitor address="x.x.x.x:20884"/>
    
      <!-- 多注册中心配置,竖号分隔表示同时连接多个不同注册中心,同一注册中心的多个集群地址用逗号分隔 -->
      <dubbo:registry protocol="zookeeper" address="x.x.x.x:2181,x.x.x.x:2181,x.x.x.x:2181"/>
      <dubbo:protocol name="dubbo" port="20880" serialization="kryo"/>
    
      <!--
         1. 选用了嵌入式的jetty来做rest server,同时,如果不配置server属性,rest协议默认也是选用jetty。
         jetty是非常成熟的java servlet容器,并和dubbo已经有较好的集成(目前5种嵌入式server中只有jetty
         和后面所述的tomcat、tjws,与dubbo监控系统等完成了无缝的集成),所以,如果你的dubbo系统是单独启动的进程,
         你可以直接默认采用jetty即可。dubbo中的rest协议默认将采用80端口.
         <dubbo:protocol name="rest" server="jetty"/>
    
         2. 配置选用了嵌入式的tomcat来做rest server。在嵌入式tomcat上,REST的性能比jetty上要好得多(参见后面的基准测试),
         建议在需要高性能的场景下采用tomcat。
         <dubbo:protocol name="rest" server="tomcat"/>
    
         3. 配置选用嵌入式的netty来做rest server。
         <dubbo:protocol name="rest" server="netty"/>
    
         4. 配置选用嵌入式的tjws或Sun HTTP server来做rest server。这两个server实现非常轻量级,
         非常方便在集成测试中快速启动使用,当然也可以在负荷不高的生产环境中使用。 注:tjws目前已经
         被deprecated掉了,因为它不能很好的和servlet 3.1 API工作。
         <dubbo:protocol name="rest" server="tjws"/>
         <dubbo:protocol name="rest" server="sunhttp"/>
    
         5. 如果你的dubbo系统不是单独启动的进程,而是部署到了Java应用服务器中,则建议你采用以下配置:
         <dubbo:protocol name="rest" server="servlet"/>
    
         6. 通过将server设置为servlet,dubbo将采用外部应用服务器的servlet容器来做rest server。同时,还要在dubbo系统的web.xml中添加如下配置:
         <web-app>
             <context-param>
                 <param-name>contextConfigLocation</param-name>
                 <param-value>/WEB-INF/classes/META-INF/spring/dubbo-demo-provider.xml</param-value>
             </context-param>
    
             <listener>
                 <listener-class>com.alibaba.dubbo.remoting.http.servlet.BootstrapListener</listener-class>
             </listener>
    
             <listener>
                 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
             </listener>
    
             <servlet>
                 <servlet-name>dispatcher</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>dispatcher</servlet-name>
                 <url-pattern>/*</url-pattern>
             </servlet-mapping>
         </web-app>
         即必须将dubbo的BootstrapListener和DispatherServlet添加到web.xml,以完成dubbo的REST功能与外部servlet容器的集成。
    
         其实,这种场景下你依然可以坚持用嵌入式server,但外部应用服务器的servlet容器往往比嵌入式server更加强大
         (特别是如果你是部署到更健壮更可伸缩的WebLogic,WebSphere等),另外有时也便于在应用服务器做统一管理、监控等等。
    
         如果将dubbo REST部署到外部Tomcat上,并配置server="servlet",即启用外部的tomcat来做为rest server的底层实现,
         则最好在tomcat上添加如下配置:
         <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
                connectionTimeout="20000"
                redirectPort="8443"
                minSpareThreads="20"
                enableLookups="false"
                maxThreads="100"
                maxKeepAliveRequests="-1"
                keepAliveTimeout="60000"/>
         特别是maxKeepAliveRequests="-1",这个配置主要是保证tomcat一直启用http长连接,以提高REST调用性能。
         但是请注意,如果REST消费端不是持续的调用REST服务,则一直启用长连接未必是最好的做法。另外,一直启用长连
         接的方式一般不适合针对普通webapp,更适合这种类似rpc的场景。所以为了高性能,在tomcat中,dubbo REST应
         用和普通web应用最好不要混合部署,而应该用单独的实例。
    
         7. 注意:如果你是用spring的ContextLoaderListener来加载spring,
         则必须保证BootstrapListener配置在ContextLoaderListener之前,否则dubbo初始化会出错。
      -->
    
      <!--
         1. 设置一个所有rest服务都适用的基础相对路径,即java web应用中常说的context path。只需要添加如下contextpath属性即可.
         <dubbo:protocol name="rest" port="8888" keepalive="true" server="netty" iothreads="5" threads="100" contextpath="services"/>
    
         2. 可以为rest服务配置线程池大小:
         <dubbo:protocol name="rest" threads="500"/>
         注意:目前线程池的设置只有当server="netty"或者server="jetty"或者server="tomcat"的时候才能生效。另外,如果server="servlet",由于这时候启用
         的是外部应用服务器做rest server,不受dubbo控制,所以这里的线程池设置也无效。
    
         如果是选用netty server,还可以配置Netty的IO worker线程数:
         <dubbo:protocol name="rest" iothreads="5" threads="100"/>
    
         3. 注意:如果你是选用外部应用服务器做rest server, 即配置:
         <dubbo:protocol name="rest" port="8888" contextpath="services" server="servlet"/>
         则必须保证这里设置的port、contextpath,与外部应用服务器的端口、DispatcherServlet的上下文路径(即webapp path加上servlet url pattern)保持一致。
    
         4. Dubbo中的rest服务默认都是采用http长连接来访问,如果想切换为短连接,直接配置:
         <dubbo:protocol name="rest" keepalive="false"/>
         注意:这个配置目前只对server="netty"和server="tomcat"才能生效。
    
         5. 配置服务器提供端所能同时接收的最大HTTP连接数,防止REST server被过多连接撑爆,以作为一种最基本的自我保护机制:
         <dubbo:protocol name="rest" accepts="500" server="tomcat/>
         注意:这个配置目前只对server="tomcat"才能生效。
    
         6. 如果rest服务的消费端也是dubbo系统,可以像其他dubbo RPC机制一样,配置消费端调用此rest服务的最大超时时间以及每个消费端所能启动的最大HTTP连接数。
         <dubbo:service interface="xxx" ref="xxx" protocol="rest" timeout="2000" connections="10"/>
         当然,由于这个配置针对消费端生效的,所以也可以在消费端配置:
         <dubbo:reference id="xxx" interface="xxx" timeout="2000" connections="10"/>
         但是,通常我们建议配置在服务提供端提供此类配置。按照dubbo官方文档的说法:“Provider上尽量多配置Consumer端的属性,让Provider实现者一开始就思考Provider
         服务特点、服务质量的问题。”
         注意:如果dubbo的REST服务是发布给非dubbo的客户端使用,则这里<dubbo:service/>上的配置完全无效,因为这种客户端不受dubbo控制。
    
         7. Dubbo的REST支持用GZIP压缩请求和响应的数据,以减少网络传输时间和带宽占用,但这种方式会也增加CPU开销。
      -->
    
      <!--
         1. use tomcat server
         2. 用rest协议在8888端口暴露服务
         3. Dubbo的REST也支持JAX-RS标准的Filter和Interceptor,以方便对REST的请求与响应过程做定制化的拦截处理。
            其中,Filter主要用于访问和设置HTTP请求和响应的参数、URI等等。如:CacheControlFilter.java
            Interceptor主要用于访问和修改输入与输出字节流,例如,手动添加GZIP压缩.如:GZIPWriterInterceptor.java
         4. 在标准JAX-RS应用中,我们一般是为Filter和Interceptor添加@Provider annotation,然后JAX-RS runtime会
            自动发现并启用它们。而在dubbo中,我们是通过添加XML配置的方式来注册Filter和Interceptor.
         5. 在此,我们可以将Filter、Interceptor和DynamicFuture这三种类型的对象都添加到extension属性上,多个之间用
            逗号分隔。(DynamicFuture是另一个接口,可以方便我们更动态的启用Filter和Interceptor,感兴趣请自行google。)
         6. 当然,dubbo自身也支持Filter的概念,但我们这里讨论的Filter和Interceptor更加接近协议实现的底层,
            相比dubbo的filter,可以做更底层的定制化。
            注:这里的XML属性叫extension,而不是叫interceptor或者filter,是因为除了Interceptor和Filter,未来我们
            还会添加更多的扩展类型。
         7. 如果REST的消费端也是dubbo系统(参见下文的讨论),则也可以用类似方式为消费端配置Interceptor和Filter。但注
            意,JAX-RS中消费端的Filter和提供端的Filter是两种不同的接口。例如前面例子中服务端是ContainerResponseFilter接口,
            而消费端对应的是ClientResponseFilter.
         8. Dubbo的REST也支持JAX-RS标准的ExceptionMapper,可以用来定制特定exception发生后应该返回的HTTP响应。
         9. Dubbo rest支持输出所有HTTP请求/响应中的header字段和body消息体。LoggingFilter
      -->
      <dubbo:protocol name="rest" port="8888" threads="500" contextpath="services" server="tomcat" accepts="500"
                     extension="com.alibaba.dubbo.demo.extension.TraceInterceptor,
                     com.alibaba.dubbo.demo.extension.TraceFilter,
                     com.alibaba.dubbo.demo.extension.ClientTraceFilter,
                     com.alibaba.dubbo.demo.extension.DynamicTraceBinding,
                     com.alibaba.dubbo.demo.extension.CustomExceptionMapper,
                     com.alibaba.dubbo.rpc.protocol.rest.support.LoggingFilter"/>
    
      <!--
         use the external tomcat or other server with the servlet approach; the port and contextpath must be exactly the same as those in external server
         <dubbo:protocol name="rest" port="8888" contextpath="services" server="servlet"/>
      -->
    
      <dubbo:protocol name="http" port="8889"/>
      <dubbo:protocol name="hessian" port="8890"/>
      <dubbo:protocol name="webservice" port="8892"/>
    
      <!-- 声明需要暴露的服务接口 -->
      <dubbo:service interface="com.alibaba.dubbo.demo.user.UserService" ref="userService" protocol="dubbo" group="xmlConfig"/>
    
      <!--
         1. 为了和其他dubbo远程调用协议保持一致,在rest中作校验的annotation必须放在服务的接口上,
         把annotation放在接口上至少有一个好处是,dubbo的客户端可以共享这个接口的信息,dubbo甚
         至不需要做远程调用,在本地就可以完成输入校验。
    
         然后按照dubbo的标准方式在XML配置中打开验证:
         <dubbo:service interface=xxx.UserService" ref="userService" protocol="rest" validation="true"/>
    
         2. 在dubbo的其他很多远程调用协议中,如果输入验证出错,是直接将RpcException抛向客户端,而在rest中由于客户端经常是非dubbo,甚至非java的系统,所以不便直接抛出Java异常。因此,目前我们将校验错误以XML的格式返回:
         <violationReport>
             <constraintViolations>
                 <path>getUserArgument0</path>
                 <message>User ID must be greater than 1</message>
                 <value>0</value>
             </constraintViolations>
         </violationReport>
    
         如果你认为默认的校验错误返回格式不符合你的要求,可以如上面章节所述,添加自定义的ExceptionMapper来自由的定制错误返回格式。
         需要注意的是,这个ExceptionMapper必须用泛型声明来捕获dubbo的RpcException,才能成功覆盖dubbo rest默认的异常处理策略。
         为了简化操作,其实这里最简单的方式是直接继承dubbo rest的RpcExceptionMapper,并覆盖其中处理校验异常的方法即可.
      -->
      <dubbo:service interface="com.alibaba.dubbo.demo.user.facade.AnotherUserRestService" ref="anotherUserRestService" protocol="rest" timeout="2000" connections="100" validation="true"/>
    
      <bean id="userService" class="com.alibaba.dubbo.demo.user.UserServiceImpl"/>
    
      <bean id="anotherUserRestService" class="com.alibaba.dubbo.demo.user.facade.AnotherUserRestServiceImpl">
          <property name="userService" ref="userService"/>
      </bean>
    
      <!--
         对于jax-rs和spring mvc,其实我对spring mvc的rest支持还没有太深入的看过,说点初步想法,请大家指正:
    
         spring mvc也支持annotation的配置,其实和jax-rs看起来是非常非常类似的。
    
         我个人认为spring mvc相对更适合于面向web应用的restful服务,比如被AJAX调用,也可能输出HTML之类的,应用中还
         有页面跳转流程之类,spring mvc既可以做好正常的web页面请求也可以同时处理rest请求。但总的来说这个restful服务
         是在展现层或者叫web层之类实现的
    
         而jax-rs相对更适合纯粹的服务化应用,也就是传统Java EE中所说的中间层服务,比如它可以把传统的EJB发布成restful
         服务。在spring应用中,也就把spring中充当service之类的bean直接发布成restful服务。总的来说这个restful服务是
         在业务、应用层或者facade层。而MVC层次和概念在这种做比如(后台)服务化的应用中通常是没有多大价值的。
    
         当然jax-rs的有些实现比如jersey,也试图提供mvc支持,以更好的适应上面所说的web应用,但应该是不如spring mvc。
    
         在dubbo应用中,我想很多人都比较喜欢直接将一个本地的spring service bean(或者叫manager之类的)完全透明的发布
         成远程服务,则这里用JAX-RS是更自然更直接的,不必额外的引入MVC概念。当然,先不讨论透明发布远程服务是不是最佳实践,
         要不要添加facade之类。
    
         当然,我知道在dubbo不支持rest的情况下,很多朋友采用的架构是spring mvc restful调用dubbo (spring) service
         来发布restful服务的。这种方式我觉得也非常好,只是如果不修改spring mvc并将其与dubbo深度集成,restful服务不能
         像dubbo中的其他远程调用协议比如webservices、dubbo rpc、hessian等等那样,享受诸多高级的服务治理的功能,比如:
         注册到dubbo的服务注册中心,通过dubbo监控中心监控其调用次数、TPS、响应时间之类,通过dubbo的统一的配置方式控制其
         比如线程池大小、最大连接数等等,通过dubbo统一方式做服务流量控制、权限控制、频次控制。另外spring mvc仅仅负责服务
         端,而在消费端,通常是用spring restTemplate,如果restTemplate不和dubbo集成,有可能像dubbo服务客户端那样自动
         或者人工干预做服务降级。如果服务端消费端都是dubbo系统,通过spring的rest交互,如果spring rest不深度整合dubbo,
         则不能用dubbo统一的路由分流等功能。
    
         当然,其实我个人认为这些东西不必要非此即彼的。我听说spring创始人rod johnson总是爱说一句话,
         the customer is always right,其实与其非要探讨哪种方式更好,不如同时支持两种方式就是了,
         所以原来在文档中也写过计划支持spring rest annoation,只是不知道具体可行性有多高。
    
         1. JAX-RS中重载的方法能够映射到同一URL地址吗?
         http://stackoverflow.com/questions/17196766/can-resteasy-choose-method-based-on-query-params
    
         2. JAX-RS中作POST的方法能够接收多个参数吗?
         http://stackoverflow.com/questions/5553218/jax-rs-post-multiple-objects
    
         注:以上备注,均来自:https://dangdangdotcom.github.io/dubbox/rest.html
      -->
    </beans>
  6. 配置dubbo.properties
    #dubbo.container=log4j,spring
    #dubbo.application.name=demo-provider
    #dubbo.application.owner=
    #dubbo.registry.address=multicast://224.5.6.7:1234
    #dubbo.registry.address=zookeeper://127.0.0.1:2181
    #dubbo.registry.address=redis://127.0.0.1:6379
    #dubbo.registry.address=dubbo://127.0.0.1:9090
    #dubbo.monitor.protocol=registry
    #dubbo.protocol.name=dubbo
    #dubbo.protocol.port=20880
    #dubbo.service.loadbalance=roundrobin
    #dubbo.log4j.file=logs/msa-demo-provider.log
    #dubbo.log4j.level=INFO
    #dubbo.log4j.subdirectory=20880
    dubbo.application.logger=slf4j
    dubbo.spring.config=classpath*:msa-*.xml

1.4 服务启动

定义服务启动类:

package com.alibaba.dubbo.demo.provider;
/**
  * @author TaoBangren
  * @version 1.0
  * @since 2017/5/17 上午9:26
  */
public class DemoProvider {
      public static void main(String[] args) {
          com.alibaba.dubbo.container.Main.main(args);
      }
}

执行main方法启动,看到以下日志输出时,msa-demo-provider启动成功:


msa-demo-provider启动成功

查看DubboKeeper监控大盘,msa-demo-provider发布服务成功,可以看到我们发布的两个接口:


msa-demo-provider发布服务成功

2. 客户端

  1. 创建Maven模块:msa-demo-client


    msa-demo-client
  2. msa-demo-client:配置pom.xml

    <!-- Module依赖 START -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>msa-demo-api</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    <!-- Module依赖 END -->
  3. Dubbox与Spring集成配置:msa-demo-client.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    
       <!-- 当前应用信息配置 -->
       <!--<dubbo:application name="msa-demo-client" owner="shark" organization="shark"/>-->
    
       <!-- 多注册中心配置,竖号分隔表示同时连接多个不同注册中心,同一注册中心的多个集群地址用逗号分隔 -->
       <!--<dubbo:registry protocol="zookeeper" address="x.x.x.x:2181,x.x.x.x:2181,x.x.x.x:2181"/>-->
       <!--<dubbo:monitor address="x.x.x.x:20884"/>-->
    
       <dubbo:reference id="userService" interface="com.alibaba.dubbo.demo.user.UserService" group="xmlConfig"/>
       <dubbo:reference id="anotherUserRestService" interface="com.alibaba.dubbo.demo.user.facade.AnotherUserRestService"/>
    
       <!--
           directly connect to provider to simulate the access to non-dubbo rest services
           <dubbo:reference id="anotherUserRestService" interface="com.alibaba.dubbo.demo.user.facade.AnotherUserRestService" url="rest://localhost:8888/services/"/>
       -->
    </beans>

3. 消费端

3.1 消费端实现

  1. 创建Maven模块:msa-demo-consumer


    msa-demo-consumer
  2. msa-demo-consumer:配置pom.xml

    <!-- Module依赖 START -->
    <dependency>
       <groupId>com.jeasy</groupId>
       <artifactId>msa-demo-client</artifactId>
       <version>1.0-SNAPSHOT</version>
    </dependency>
    <!-- Module依赖 END -->
  3. 创建消费端测试类:DemoAction.java

    package com.alibaba.dubbo.demo;
    import com.alibaba.dubbo.rpc.RpcContext;
    import com.alibaba.dubbo.demo.user.User;
    import com.alibaba.dubbo.demo.user.UserService;
    import com.alibaba.dubbo.demo.user.facade.AnotherUserRestService;
    /**
     * @author TaoBangren
     * @version 1.0
     * @since 2017/5/17 上午9:26
     */
    public class DemoAction {
    
       private UserService userService;
    
       private AnotherUserRestService anotherUserRestService;
    
       public void setUserService(final UserService userService) {
           this.userService = userService;
       }
    
       public void setAnotherUserRestService(final AnotherUserRestService anotherUserRestService) {
           this.anotherUserRestService = anotherUserRestService;
       }
    
       public void start() throws Exception {
           User user = new User();
           user.setId(1L);
           user.setName("larrypage");
    
           System.out.println("SUCCESS: registered user with id by rest" + anotherUserRestService.registerUser(user).getId());
           System.out.println("SUCCESS: registered user with id " + userService.registerUser(user));
    
           RpcContext.getContext().setAttachment("clientName", "demo");
           RpcContext.getContext().setAttachment("clientImpl", "dubbox rest");
           System.out.println("SUCCESS: got user by rest" + anotherUserRestService.getUser(1L));
           System.out.println("SUCCESS: got user " + userService.getUser(1L));
       }
    }
  4. Dubbox与Spring集成配置:msa-demo-consumer.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
        xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
    
       <!-- 当前应用信息配置 -->
       <dubbo:application name="msa-demo-consumer" owner="tbr" organization="tbr"/>
    
       <!-- 多注册中心配置,竖号分隔表示同时连接多个不同注册中心,同一注册中心的多个集群地址用逗号分隔 -->
       <dubbo:registry protocol="zookeeper" address="x.x.x.x:2181,x.x.x.x:2181,x.x.x.x:2181"/>
       <dubbo:monitor address="x.x.x.x:20884"/>
    
       <bean class="com.alibaba.dubbo.demo.DemoAction" init-method="start">
           <property name="userService" ref="userService"/>
           <property name="anotherUserRestService" ref="anotherUserRestService"/>
       </bean>
    </beans>
  5. 配置dubbo.properties

    #dubbo.container=log4j,spring
    #dubbo.application.name=demo-consumer
    #dubbo.application.owner=
    #dubbo.registry.address=multicast://224.5.6.7:1234
    #dubbo.registry.address=zookeeper://127.0.0.1:2181
    #dubbo.registry.address=redis://127.0.0.1:6379
    #dubbo.registry.address=dubbo://127.0.0.1:9090
    #dubbo.monitor.protocol=registry
    #dubbo.log4j.file=logs/msa-demo-consumer.log
    #dubbo.log4j.level=INFO
    dubbo.application.logger=slf4j
    dubbo.spring.config=classpath*:msa-*.xml

3.2 消费端测试

定义消费启动类:

package com.jeasy;
/**
 * @author TaoBangren
 * @version 1.0
 * @since 2017/5/17 上午9:26
 */
public class DemoConsumer {
      public static void main(String[] args) {
          com.alibaba.dubbo.container.Main.main(args);
      }
}

执行main方法启动,看到以下日志输出时,msa-demo-consumer启动成功:


msa-demo-consumer启动成功

同时服务端会输出服务调用日志信息,并调用成功,如下:


服务端调用日志

4. 规范使用

模块描述是否必须
msa-xxx-api定义接口&实体必须
msa-xxx-provider依赖api模块,实现服务接口,提供服务必须
msa-xxx-client依赖api模块,Spring配置文件&测试用例,提供给第三方调用服务使用必须
msa-xxx-consumer依赖client模块,建议保留该模块,避免client模块直接与应用方紧耦合可选

5. 推荐阅读

5.1 Dubbox相关资源

  1. 源码地址 : https://github.com/dangdangdotcom/dubbox
  2. 在Dubbo中开发REST风格的远程调用 : https://dangdangdotcom.github.io/dubbox/rest.html
  3. 在Dubbo中使用高效的Java序列化 : https://dangdangdotcom.github.io/dubbox/serialization.html
  4. 使用JavaConfig方式配置dubbox : https://dangdangdotcom.github.io/dubbox/java-config.html
  5. Dubbo Jackson序列化使用说明 : https://dangdangdotcom.github.io/dubbox/jackson.html
  6. Demo : https://dangdangdotcom.github.io/dubbox/demo.html
  7. 当当网开源Dubbox,扩展Dubbo服务框架支持REST风格远程调用 : http://www.infoq.com/cn/news/2014/10/dubbox-open-source
  8. Dubbox Wiki : https://github.com/dangdangdotcom/dubbox/wiki

5.2 Dubbo相关资源

  1. 源码地址 : https://github.com/alibaba/dubbo
  2. Dubbo Wiki : https://github.com/alibaba/dubbo/wiki
  3. http://dubbo.io/


作者:陶邦仁
链接:http://www.jianshu.com/p/c602b347de88
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值