大纲
前言
随着微服务的流行以及多语言互操作诉求日益增多,在 Dubbo 中暴露 REST 服务变成了一个不容忽视的诉求。为了在 Dubbo 中暴露 REST 服务,通常有两种做法,一种是直接依赖 Spring REST 或者其他 REST 框架来直接暴露,另一种是通过 Dubbo 框架内置的 REST 能力暴露。两种做法各有优缺点,主要体现在前者与微服务体系中的服务发现组件能够更好地工作,而后者可以无缝的享受到 Dubbo 体系中的服务发现以及服务治理的能力。本文将介绍如何通过 Dubbo 框架内置的 REST 能力来暴露 REST 服务。
提示
- 自 Dubbo
2.6.0
版本开始,Dubbo 合并了当当网捐献的 DubboX 4 中的主要特性,其中就包括了基于 RESTeasy 3.0.19.Final
的 REST 支持,具备 JAX-RS 2.0
规范中所有的能力。 - 自 Dubbo
3.3
版本开始,原有的 REST 协议实现已被移除,由 Triple 协议提供更全面的 REST 支持。使用 Triple 协议发布 REST 风格的服务非常简单,只需要在服务接口上添加相应的注解即可,支持三种注解方式:内置注解、Spring Web 注解、JAX-RS 注解。
学习资源
版本对应关系
Spring 与 Dubbo 的版本必须互相匹配,否则不同版本之间可能会存在兼容性问题,最终导致服务无法正常运行。两者的版本对应关系如下:
Dubbo 版本 | 适配 Spring 版本 | 核心特性 / 注意事项 |
---|
Dubbo 3.x (3.3.0+) | ✅ Spring 6.x ✅ Spring 5.3+ | (1) 原生支持 Spring 6 注解驱动 (2) 依赖 Jakarta EE 9+(需 JDK 17+) (3) 默认集成 Spring Boot 3.x |
Dubbo 3.0.x (3.0.0 - 3.2.x) | ✅ Spring 5.3+ ⚠️ Spring 6.x(需手动适配) | (1) 需手动排除旧版 Jakarta API(jakarta.* ) (2) 建议搭配 Spring Boot 2.7+ |
Dubbo 2.7.x | ✅ Spring 5.2+ ❌ Spring 6.x | (1) 仅支持 Java EE(javax.* ) (2) 官方已停止维护,仅建议旧项目使用 |
Dubbo 整合案例
本节将整合 Spring、SpringMVC 与 Dubbo,并配置 Dubbo 支持使用 REST 协议,使用的注册中心是 ZooKeeper。
项目结构
版本说明
组件 | 版本 | 说明 |
---|
Spring | 5.2.25.RELEASE | |
Dubbo | 2.7.23 | |
Zookeeper Server | 3.5.7 | ZooKeeper 服务器,作为服务注册中心 |
Curator Recipes | 5.5.0 | 用于 Dubbo 连接 ZooKeeper 注册中心,依赖 Curator Client 5.5.0 和 ZooKeeper Client 3.7.1 |
JDK | 1.8 | 支持 JDK 1.8 及以上版本 |
模块说明
api
:抽取出来的公共模块,存放公用的实体类和接口provider
:服务提供者,实现 api
模块中的接口customer
:服务消费者,调用服务提供者中的接口
案例代码
API 模块
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <properties> <dubbo.version>2.7.23</dubbo.version> <lombok.version>1.18.16</lombok.version> </properties>
<dependencies> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-rpc-rest</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> <scope>provided</scope> </dependency> </dependencies>
|
- 实体类,需要实现
Serializable
接口,并提供默认构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Data @ToString @NoArgsConstructor @AllArgsConstructor public class User implements Serializable {
private Long id;
private String name;
private Integer age;
}
|
- API 接口,需要在接口中标记 JAX-RS 注解(或者在接口的实现类中标记注解),JAX-RS 注解的使用说明请看 这里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType;
@Path("/user") public interface UserService {
@POST @Path("/add") @Consumes(MediaType.APPLICATION_JSON) @Produces({MediaType.APPLICATION_JSON}) Boolean add(User user);
@GET @Path("/getById/{id}") @Produces({MediaType.APPLICATION_JSON}) User getById(@PathParam("id") Long id);
}
|
提示
- JAX-RS 支持在类级别上定义
@Consumers
和 @Produces
注解来规定参数以及返回值的类型。在类级别上统一定义之后,就可以不用在方法级别上进一步单独定义。
Provider 模块
- 引入依赖,由于需要 Dubbo 支持 REST 协议,因此这里额外引入了
dubbo-rpc-rest
依赖
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
| <properties> <javax.servlet.version>3.1.0</javax.servlet.version> <spring.version>5.2.25.RELEASE</spring.version> <dubbo.version>2.7.23</dubbo.version> <curator.version>5.5.0</curator.version> <commons.version>3.4</commons.version> <slf4j.version>1.7.21</slf4j.version> </properties>
<dependencies> <dependency> <groupId>com.clay.dubbo</groupId> <artifactId>dubbo-lesson-07-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${javax.servlet.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-rpc-rest</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${curator.version}</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> </dependencies>
|
- 接口实现类,
@DubboService
注解主要用于暴露服务,使其能够被 Dubbo 框架识别并注册到服务注册中心,这里需要通过 protocol
属性来指定 Dubbo 使用 REST 协议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import org.apache.dubbo.config.annotation.DubboService;
@DubboService(protocol = "rest") public class UserServiceImpl implements UserService {
@Override public Boolean add(User user) { System.out.println(user); return true; }
@Override public User getById(Long id) { return new User(id, "Peter", 18); }
}
|
- Spring 的 XML 配置文件(
application-context.xml
)
重要提示
- 在 Spring 的 XML 配置文件中,
<dubbo:protocol name="rest" port="8080" server="netty"/>
的作用是指定 Dubbo 使用 REST 协议,并使用基于 Netty 框架的 REST Server。 - 如果 Spring 应用最终是以 War 包的方式独立部署在外部(单独安装)的 Tomcat 中,并使用了 Netty 框架的 REST Server,那么
<dubbo:protocol>
中配置的端口不能与外部 Tomcat 的端口一样,否则会出现端口冲突问题。
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
| <?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://dubbo.apache.org/schema/dubbo" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.clay.dubbo.producer"/>
<dubbo:application name="dubbo-provider-application"> <dubbo:parameter key="qos.enable" value="true"/> <dubbo:parameter key="qos.port" value="22222"/> <dubbo:parameter key="qos.accept.foreign.ip" value="false"/> </dubbo:application>
<dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="5000"/>
<dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="rest" port="8080" server="netty"/>
<dubbo:annotation package="com.clay.dubbo.producer"/>
</beans>
|
- SpringMVC 的 XML 配置文件(
application-mvc.xml
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?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:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.clay.dubbo.producer.controller"/>
</beans>
|
- Java EE 的 XML 配置文件(
web.xml
),存放在项目中的 /src/main/webapp/WEB-INF
目录下
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
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/Java EE" xsi:schemaLocation="http://java.sun.com/xml/ns/Java EE http://java.sun.com/xml/ns/Java EE/web-app_2_5.xsd" version="2.5">
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/application-context.xml</param-value> </context-param>
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/application-mvc.xml</param-value> </init-param> </servlet>
<servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
</web-app>
|
- Log4j 的配置文件(
log4j.properties
)
1 2 3 4 5 6 7 8
| log4j.rootLogger=INFO, stdout, file log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=log/app.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
|
Consumer 模块
- 引入依赖,由于需要 Dubbo 支持 REST 协议,因此这里额外引入了
dubbo-rpc-rest
依赖
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
| <properties> <javax.servlet.version>3.1.0</javax.servlet.version> <spring.version>5.2.25.RELEASE</spring.version> <dubbo.version>2.7.23</dubbo.version> <curator.version>5.5.0</curator.version> <commons.version>3.4</commons.version> <slf4j.version>1.7.21</slf4j.version> </properties>
<dependencies> <dependency> <groupId>com.clay.dubbo</groupId> <artifactId>dubbo-lesson-07-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${javax.servlet.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-rpc-rest</artifactId> <version>${dubbo.version}</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${curator.version}</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> </dependencies>
|
- 业务测试类,通过
@DubboReference
注解在服务消费者端引用远程服务提供者的服务,并设置 protocol
属性来指定 Dubbo 使用 REST 协议
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
| import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/system") public class SystemController {
@DubboReference(protocol = "rest") private UserService userService;
@GetMapping("/getUser/{id}") public User getUser(@PathVariable("id") Long id) { return userService.getById(id); }
@PostMapping("/addUser") public Boolean addUser(@RequestBody User user) { return userService.add(user); }
}
|
- Spring 的 XML 配置文件(
application-context.xml
)
1 2 3 4 5 6 7 8 9 10 11
| <?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.clay.dubbo.consumer"/>
</beans>
|
- SpringMVC 的 XML 配置文件(
application-mvc.xml
)
特别注意
在服务消费者模块中,必须先扫描 Dubbo 的注解(如 @DubboReference
,为了引用远程服务),然后再扫描 Spring 的注解(如 @Controller
),否则会出现 @DubboReference
注解失效的问题,详细分析说明请看这里。
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
| <?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:mvc="http://www.springframework.org/schema/mvc" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<dubbo:application name="dubbo-consumer-application"> <dubbo:parameter key="qos.enable" value="true"/> <dubbo:parameter key="qos.port" value="22223"/> <dubbo:parameter key="qos.accept.foreign.ip" value="false"/> </dubbo:application>
<dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="5000"/>
<dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
<dubbo:annotation package="com.clay.dubbo.consumer"/>
<context:component-scan base-package="com.clay.dubbo.consumer.controller"/>
</beans>
|
- Java EE 的 XML 配置文件(
web.xml
),存放在项目中的 /src/main/webapp/WEB-INF
目录下
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
| <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/Java EE" xsi:schemaLocation="http://java.sun.com/xml/ns/Java EE http://java.sun.com/xml/ns/Java EE/web-app_2_5.xsd" version="2.5">
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/application-context.xml</param-value> </context-param>
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
<servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/application-mvc.xml</param-value> </init-param> </servlet>
<servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
</web-app>
|
- Log4j 的配置文件(
log4j.properties
)
1 2 3 4 5 6 7 8
| log4j.rootLogger=INFO, stdout, file log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=log/app.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %m%n
|
测试代码
- (1) 在 IDEA 开发工具内,将 Provider 和 Consumer 模块以 War 包的方式按顺序分别单独部署到外部(本地单独安装)的 Tomcat 中,如下图所示:
![]()
(2) 使用 Postman 等工具访问 http://127.0.0.1:8080/user/getById/1001
接口,若接口可以返回正确的 JSON 数据结果,则说明 Provider 模块中的 Dubbo 服务可以支持 REST 协议。
(3) 使用 Postman 等工具访问 http://127.0.0.1:9095/system/getUser/1001
接口,若接口可以返回正确的 JSON 数据结果,则说明 Consumer 模块可以通过 REST 协议远程调用 Provider 模块提供的服务。
下载代码
- 完整的案例代码可以直接从 GitHub 下载对应章节
dubbo-lesson-07
。
Dubbo 其他配置
JAX-RS 注解使用
JAX-RS 和 Spring MVC 核心注解的对比
功能 | JAX-RS 注解 | Spring MVC 注解 | 说明 |
---|
处理 GET 请求 | @GET | @RequestMapping(method = RequestMethod.GET) | 处理 HTTP GET 请求 |
处理 POST 请求 | @POST | @RequestMapping(method = RequestMethod.POST) | 处理 HTTP POST 请求 |
指定 URL 路径 | @Path("/user") | @RequestMapping("/user") | 定义请求路径 |
指定请求参数 | @QueryParam("name") | @RequestParam("name") | 绑定 URL 查询参数,如 /users?name=Tom |
指定路径参数 | @PathParam("id") | @PathVariable("id") | 绑定 URL 路径变量,如 /users/123 |
指定请求体格式 | @Consumes(MediaType.APPLICATION_JSON) | @RequestBody | 指定方法接收的 Content-Type,JAX-RS 需搭配 @POST 、@PUT 注解使用 |
指定响应体格式 | @Produces(MediaType.APPLICATION_JSON) | @ResponseBody | 指定方法返回的 Content-Type |
指定请求头参数 | @HeaderParam("token") | @RequestHeader("token") | 绑定请求头参数 |
JAX-RS 注解标记在接口和实现类中的区别
标记在接口中
- 优点:
@Path
、@Consumes
、@Produces
直接写在接口中时,所有实现类都会继承这些 REST 规则。- 让接口本身成为 RESTful 规范的一部分,方便生成 API 文档(如 OpenAPI / Swagger)。
- 适用于 Dubbo + REST 这种分布式场景,客户端可以直接引用接口,不需要依赖具体实现。
- 在多实现类的情况下,接口可以保证所有实现类都遵循相同的 REST 规则。
- 适用场景:
- 适用于 Dubbo + REST 场景,因为 Dubbo 主要通过接口暴露服务。
标记在实现类中
- 优点:
@Path
、@Consumes
、@Produces
只作用于实现类,而接口本身不受影响。- 更符合面向实现的 REST 风格,不会污染接口,使接口可以用于其他用途(比如 RPC)。
- 如果有多个实现类,可以为不同的实现类定义不同的 REST 规则。
- 在 Spring + JAX-RS 组合的场景下,更容易与 Spring 组件(如
@Service
)集成。
- 适用场景:
- 适用于 Spring 生态,因为 Spring 推荐在实现类中标注注解。
总结
- JAX-RS 主要用于 Java EE / Jakarta EE,适用于 Dubbo + REST 方案。
- Spring MVC 的
@RequestMapping
及其变体(@GetMapping
、@PostMapping
等)整合度更高,而 JAX-RS 依赖 @Path
及 @GET
/ @POST
等注解组合使用。 - JAX-RS 支持在类级别上定义
@Consumers
和 @Produces
注解来规定参数以及返回值的类型。在类级别上统一定义之后,就可以不用在方法级别上进一步定义。
同时支持多种协议
配置说明
提示
- 在生产环境中,往往需要内部系统使用 Dubbo 协议来调用服务,而外部系统则使用 REST 协议(HTTP)来调用服务。
Dubbo 可以同时支持多种协议(可能需要额外引入相应的 Maven 依赖),也就是在服务提供者的 Spring XML 配置文件中,可以同时配置多种协议,但不同协议必须使用不同的端口,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <dubbo:protocol name="dubbo" port="20880"/>
<dubbo:protocol name="rest" port="7777" server="tomcat"/>
<dubbo:protocol name="hessian" port="8888" server="tomcat"/>
<dubbo:service interface="com.example.api.OrderService" ref="orderServiceImpl" protocol="dubbo"/>
<dubbo:service interface="com.example.api.RoleService" ref="roleServiceImpl" protocol="rest"/>
<dubbo:service interface="com.example.api.UserService" ref="userServiceImpl" protocol="hessian"/>
<dubbo:service interface="com.example.api.SystemService" ref="systemServiceImpl" protocol="dubbo,rest"/>
|
当服务消费者调用远程服务时,只需要在服务提供者的 Spring XML 配置文件中,直接指定对应的协议即可,如下所示:
1 2 3 4 5 6 7 8
| <dubbo:reference id="orderService" interface="com.example.api.OrderService" protocol="dubbo"/>
<dubbo:reference id="roleService" interface="com.example.api.RoleService" protocol="rest"/>
<dubbo:reference id="systemService" interface="com.example.api.SystemService" protocol="dubbo,rest"/>
|
配置案例
在上面案例代码的基础上,为了让 Provider 模块支持多种协议(比如 Dubbo 和 REST 协议),可以做以下更改。
- (1) 在 Provider 模块(服务提供者)的 Spring XML 配置文件中,新增 Dubbo 协议的定义(不同协议必须使用不同的端口)
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
| <?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://dubbo.apache.org/schema/dubbo" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.clay.dubbo.producer"/>
<dubbo:application name="dubbo-provider-application"> <dubbo:parameter key="qos.enable" value="true"/> <dubbo:parameter key="qos.port" value="22222"/> <dubbo:parameter key="qos.accept.foreign.ip" value="false"/> </dubbo:application>
<dubbo:registry address="zookeeper://127.0.0.1:2181" timeout="5000"/>
<dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:protocol name="rest" port="8080" server="netty"/>
<dubbo:annotation package="com.clay.dubbo.producer"/>
</beans>
|
- (2) 在 Provider 模块(服务提供者)的接口实现类中,通过
@DubboService
注解的 protocol
属性指定使用 Dubbo 和 REST 协议来暴露服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import org.apache.dubbo.config.annotation.DubboService;
@DubboService(protocol = {"dubbo", "rest"}) public class UserServiceImpl implements UserService {
@Override public Boolean add(User user) { System.out.println(user); return true; }
@Override public User getById(Long id) { return new User(id, "Peter", 18); }
}
|
- (3) 在 Consumer 模块(服务消费者)的控制器类中,通过
@DubboReference
注解的 protocol
属性指定使用 Dubbo 或者 REST 协议来进行远程服务调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @RestController @RequestMapping("/system") public class SystemController {
@DubboReference(protocol = "rest") private UserService userService;
@GetMapping("/getUser/{id}") public User getUser(@PathVariable("id") Long id) { return userService.getById(id); }
@PostMapping("/addUser") public Boolean addUser(@RequestBody User user) { return userService.add(user); }
}
|
在 Consumer 模块(服务消费者)中,如果需要同时使用多种协议,可以通过 @DubboReference
注解的 protocol
属性指定 Dubbo 和 REST 协议,Dubbo 会按照协议的声明顺序尝试发起调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @RestController @RequestMapping("/system") public class SystemController {
@DubboReference(protocol = "dubbo,rest") private UserService userService;
@GetMapping("/getUser/{id}") public User getUser(@PathVariable("id") Long id) { return userService.getById(id); }
@PostMapping("/addUser") public Boolean addUser(@RequestBody User user) { return userService.add(user); }
}
|
在 Consumer 模块(服务消费者)中,如果需要同时使用多种协议,还可以创建多个 @DubboReference
注解,然后每个 @DubboReference
注解分别指定不同的协议
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
| @RestController @RequestMapping("/system") public class SystemController {
@DubboReference(protocol = "rest") private UserService userServiceREST;
@DubboReference(protocol = "dubbo") private UserService userServiceDubbo;
@GetMapping("/getUser/{id}") public User getUser(@PathVariable("id") Long id) { return userServiceREST.getById(id); }
@PostMapping("/addUser") public Boolean addUser(@RequestBody User user) { return userServiceDubbo.add(user); }
}
|
支持不同类型的 Server
目前在 Dubbo 2 中,REST 协议可以跑在五种不同的 Server 上,分别是:
Netty
:基于 Netty 框架的 REST Server,通过 <dubbo:protocol name="rest" port="8080" server="netty"/>
来配置。Tomcat
:基于嵌入式 Tomcat 的 REST Server,通过 <dubbo:protocol name="rest" port="8080" server="tomcat"/>
来配置。Jetty
:默认服务器,基于嵌入式 Jetty 的 REST Server,通过 <dubbo:protocol name="rest" port="8080" server="jetty"/>
来配置。SunHttp
:使用 JDK 内置的 Sun HTTP server 作为 REST Server,通过 <dubbo:protocol name="rest" port="8080" server="sunhttp"/>
来配置,仅推荐在开发环境中使用。Servlet
:采用外部应用服务器的 Servlet 容器(如外部 Tomcat)来做 REST Server,通过 <dubbo:protocol name="rest" server="servlet"/>
来配置,这里不需要配置端口,另外还需要在 web.xml
中做额外的其他配置。
参考资料