聊聊Dubbo(二):简单入门 0 准备 1 服务端 2

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
复制代码<!-- 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

1
2
3
4
5
6
7
8
9
复制代码/**
* @author TaoBangren
* @version 1.0
* @since 2017/5/17 上午9:26
*/
public interface UserService {
User getUser(Long id);
Long registerUser(User user);
}
  1. 定义REST接口:AnotherUserRestService.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
复制代码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);
}
  1. 定义实体:User.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
复制代码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;
}
  1. 定义REST响应结果实体:RegistrationResult.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
复制代码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

1
2
3
4
5
6
7
复制代码<!-- Module依赖 START -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>msa-demo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Module依赖 END -->
  1. 实现UserService接口:UserServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
复制代码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();
}
}
  1. 实现REST接口AnotherUserRestService:AnotherUserRestServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
复制代码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;
}
}
  1. Dubbox与Spring集成配置:msa-demo-provider.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
复制代码 <?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>
  1. 配置dubbo.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
复制代码#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 服务启动

定义服务启动类

1
2
3
4
5
6
7
8
9
10
11
复制代码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发布服务成功

  1. 客户端

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

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

1
2
3
4
5
6
7
复制代码<!-- Module依赖 START -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>msa-demo-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Module依赖 END -->
  1. Dubbox与Spring集成配置:msa-demo-client.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
复制代码<?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>
  1. 消费端

3.1 消费端实现

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

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

1
2
3
4
5
6
7
复制代码<!-- Module依赖 START -->
<dependency>
<groupId>com.jeasy</groupId>
<artifactId>msa-demo-client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Module依赖 END -->
  1. 创建消费端测试类:DemoAction.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
复制代码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));
}
}
  1. Dubbox与Spring集成配置:msa-demo-consumer.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
复制代码<?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>
  1. 配置dubbo.properties
1
2
3
4
5
6
7
8
9
10
11
12
复制代码#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 消费端测试

定义消费启动类:

1
2
3
4
5
6
7
8
9
10
11
复制代码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启动成功

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

服务端调用日志

  1. 规范使用

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

5.1 Dubbox相关资源

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

5.2 Dubbo相关资源

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

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%