大纲
前言
本文将整合 SpringBoot 和 Dubbo,并配置 Dubbo 使用 Triple 协议来间接支持 REST 协议,使用的注册中心是 Nacos。
学习资源
新协议的支持
- Dubbo 支持多种协议,在
3.0
版本之前支持 Dubbo、REST、gRPC 等主流协议。 - 在
3.0
版本之后,新引入了 Dubbo 官方自研的 Triple 协议,不过也保留了 Dubbo 和 REST 协议的支持。 - 在
3.3
版本之后,Dubbo 官方直接移除了 REST 协议的直接支持,使用 Triple 协议间接支持 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 注解。
版本对应关系
SpringBoot 与 Dubbo 的版本必须互相匹配,否则不同版本之间可能会存在兼容性问题,最终导致服务无法正常运行。两者的版本对应关系如下:
Dubbo 分支 | 最新版本 | JDK | SpringBoot | 详细说明 |
---|
3.3.x | 3.3.2 | 8, 17, 21 | 2.x、3.x | 生产可用(推荐,长期维护)! 最新 Triple 协议升级,内置 Metrics、Tracing、GraalVM 支持等 |
3.2.x | 3.2.10 | 8, 17 | 2.x、3.x | 生产可用(长期维护)! |
3.1.x | 3.1.11 | 8, 17 | 2.x、3.x | 仅修复安全漏洞! |
3.0.x | 3.0.15 | 8 | 2.x | 停止维护! |
2.7.x | 2.7.23 | 8 | 2.x | 停止维护! |
2.6.x | 2.6.20 | 6, 7 | - | 停止维护! |
2.5.x | 2.5.10 | 6, 7 | - | 停止维护! |
如果仍然使用版本低于 2.7.0
的旧版 Dubbo,请使用以下 Spring Boot 启动器:
Dubbo Spring Boot Starter | Dubbo | Spring Boot |
---|
0.2.1.RELEASE | 2.6.5+ | 2.x |
0.1.2.RELEASE | 2.6.5+ | 1.x |
Dubbo 整合案例
本节将整合 SpringBoot 与 Dubbo,并配置 Dubbo 使用 Triple 协议来间接支持 REST 协议,使用的注册中心是 Nacos。值得一提的是,本教程的内容也适用于 Spring Cloud 项目。
版本说明
组件 | 版本 | 说明 |
---|
SpringBoot | 3.4.2 | |
Dubbo Spring Boot Starter | 3.3.2 | 依赖 Dubbo 3.3.2 |
Nacos Server | 2.5.0 | Nacos 服务器,作为服务注册中心 |
JDK | 17 | 支持 JDK 17 及以上版本 |
模块说明
api
:抽取出来的公共模块,存放公用的实体类和接口provider
:服务提供者,实现 api
模块中的接口customer
:服务消费者,调用服务提供者中的接口
案例代码
API 模块
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
| <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <spring-boot.version>3.4.2</spring-boot.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </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 接口,可以直接使用 Spring Web 注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@RestController @RequestMapping("/user") public interface UserService {
@PostMapping("/add") Boolean add(@RequestBody User user);
@GetMapping("/getById/{id}") User getById(@PathVariable("id") Long id);
}
|
提示
- 自 Dubbo
3.3
版本开始,当使用 Triple 协议来间接提供 REST 支持,此时可以使用三种注解方式:内置注解、Spring Web 注解、JAX-RS 注解。
Provider 模块
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
| <properties> <spring-boot.version>3.4.2</spring-boot.version> <dubbo-springboot.version>3.3.2</dubbo-springboot.version> <dubbo-nacos.version>3.3.2</dubbo-nacos.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <dependency> <groupId>com.clay.dubbo</groupId> <artifactId>dubbo-lesson-02-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>${dubbo-springboot.version}</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-nacos</artifactId> <version>${dubbo-nacos.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
|
- 接口实现类,
@DubboService
注解主要用于暴露服务,使其能够被 Dubbo 框架识别并注册到服务注册中心,这里需要通过 protocol
属性来指定 Dubbo 使用 Triple 协议
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 = "tri") 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); }
}
|
1 2 3 4 5 6 7 8
| @SpringBootApplication public class ProviderApplication {
public static void main(String[] args) { SpringApplication.run(ProviderApplication.class); }
}
|
- 配置文件(
application.yml
),重点是定义 Triple 协议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| server: port: 9090
spring: application: name: dubbo-provider-application
dubbo: application: name: ${spring.application.name} registry: address: nacos://127.0.0.1:8848 protocol: name: tri port: 50051 scan: base-packages: com.clay.dubbo.provider
|
提示
若不希望在 YML 配置文件中指定 dubbo.scan.base-packages
参数,那么可以在主启动类上标注 @EnableDubbo(scanBasePackages = "xxx")
注解或者 @DubboComponentScan(basePackages = "xxx")
注解来替代。
Consumer 模块
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
| <properties> <spring-boot.version>3.4.2</spring-boot.version> <dubbo-springboot.version>3.3.2</dubbo-springboot.version> <dubbo-nacos.version>3.3.2</dubbo-nacos.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <dependency> <groupId>com.clay.dubbo</groupId> <artifactId>dubbo-lesson-02-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>${dubbo-springboot.version}</version> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-registry-nacos</artifactId> <version>${dubbo-nacos.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
|
- 业务测试类,
@DubboReference
注解主要用于在服务消费者端引用 Dubbo 服务提供者的服务,并设置 protocol
属性来指定 Dubbo 使用 Triple 协议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import org.apache.dubbo.config.annotation.DubboReference;
@Slf4j @RestController @RequestMapping("/system") public class SystemController {
@DubboReference(protocol = "tri") 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); }
}
|
1 2 3 4 5 6 7 8
| @SpringBootApplication public class ConsumerApplication {
public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class); }
}
|
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
| server: port: 9095
spring: application: name: dubbo-consumer-application
dubbo: application: name: ${spring.application.name} registry: address: nacos://127.0.0.1:8848 scan: base-packages: com.clay.dubbo.consumer consumer: check: false retries: 0 timeout: 1000
|
提示
若不希望在 YML 配置文件中指定 dubbo.scan.base-packages
参数,那么可以在主启动类上标注 @EnableDubbo(scanBasePackages = "xxx")
注解或者 @DubboComponentScan(basePackages = "xxx")
注解来替代。
测试代码
(1) 在 IDEA 开发工具内,分别启动 Provider 和 Consumer 模块。
(2) 使用 Postman 等工具访问 http://127.0.0.1:9090/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-02
。
Dubbo 其他配置
JAX-RS 注解使用
提示
- 在 Dubbo
3.3
中使用 JAX-RS 注解,Maven 需要引入 jakarta.ws.rs-api
依赖。
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 依赖),也就是在服务提供者的 YML 配置文件中,可以同时配置多种协议,但不同协议必须使用不同的端口,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| dubbo: protocols: dubbo-protocol: name: dubbo port: 20880 triple-protocol: name: tri port: 50051 hessian-protocol: name: hessian port: 8888 server: tomcat
|
当服务提供者暴露服务时,可以使用 @DubboService
注解的 protocol
属性指定多种协议,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @DubboService(protocol = "dubbo") public class OrderServiceImpl implements OrderService { }
@DubboService(protocol = "tri") public class RoleServiceImpl implements RoleService { }
@DubboService(protocol = "hessian") public class UserServiceImpl implements UserService { }
@DubboService(protocol = {"dubbo", "tri"}) public class SystemServiceImpl implements SystemService { }
|
当服务消费者调用远程服务时,只需要通过 @DubboReference
注解的 protocol
属性指定对应的协议即可,如下所示:
1 2 3 4 5 6 7 8 9 10 11
| @DubboReference(protocol = "dubbo") private OrderService orderService;
@DubboReference(protocol = "tri") private RoleService roleService;
@DubboReference(protocol = "dubbo,tri") private SystemService systemService;
|
配置案例
在上面案例代码的基础上,为了让 Provider 模块支持多种协议(比如 Dubbo 和 Triple 协议),可以做以下更改。
- (1) 在 Provider 模块(服务提供者)的 YML 配置文件中,新增 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
| server: port: 9090
spring: application: name: dubbo-provider-application
dubbo: application: name: ${spring.application.name} registry: address: nacos://127.0.0.1:8848 protocols: dubbo-protocol: name: dubbo port: 20880 triple-protocol: name: tri port: 50051 scan: base-packages: com.clay.dubbo.provider
|
- (2) 在 Provider 模块(服务提供者)的接口实现类中,通过
@DubboService
注解的 protocol
属性指定使用 Dubbo 和 Triple 协议来暴露服务
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", "tri"}) 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 或者 Triple 协议来进行远程服务调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Slf4j @RestController @RequestMapping("/system") public class SystemController {
@DubboReference(protocol = "tri") 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,tri") 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
注解分别指定不同的协议。特别注意,这种写法依赖 JAX-RS 注解,因为使用 Spring Web 注解后会出现路径映射冲突的问题。
1 2 3 4 5
| <dependency> <groupId>jakarta.ws.rs</groupId> <artifactId>jakarta.ws.rs-api</artifactId> <version>3.1.0</version> </dependency>
|
- 在接口中标记 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);
}
|
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 = "tri") private UserService userServiceTriple;
@DubboReference(protocol = "dubbo") private UserService userServiceDubbo;
@GetMapping("/getUser/{id}") public User getUser(@PathVariable("id") Long id) { return userServiceTriple.getById(id); }
@PostMapping("/addUser") public Boolean addUser(@RequestBody User user) { return userServiceDubbo.add(user); }
}
|
直接使用 REST 协议
在上面的案例代码中,是使用 Triple 协议来间接支持 REST 协议的。如果希望直接使用 REST 协议,那么可以在 Dubbo 3.3
的基础上引入 dubbo-rpc-rest
组件来实现,支持三种注解方式:内置注解、Spring Web 注解、JAX-RS 注解,详细教程如下:
使用 ZooKeeper 注册中心
若希望在 SpringBoot 整合 Dubbo 时,使用 ZooKeeper 作为注册中心,那么在上述案例的基础上,按照以下步骤进行更改即可。
- (1) 在 Provider 和 Consumer 模块中,移除 Nacos 的依赖,并添加 ZooKeeper 与 Curator 的依赖,其中 Curator 用于连接 ZooKeeper 注册中心(版本为
3.9.3
)
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
| <properties> <spring-boot.version>3.4.2</spring-boot.version> <dubbo-springboot.version>3.3.2</dubbo-springboot.version> <curator.version>5.7.1</curator.version> </properties>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>${dubbo-springboot.version}</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>${curator.version}</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-x-discovery-server</artifactId> <version>${curator.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>
|
- (2) 在 Provider 和 Consumer 模块中,指定 ZooKeeper 注册中心的地址
1 2 3 4
| dubbo: registry: address: zookeeper://127.0.0.1:2181
|
特别注意
如果使用 ZooKeeper 作为注册中心,那么在 Maven 的配置文件中,Curator 的版本必须与 ZooKeeper 服务器的版本相匹配,否则会导致兼容问题的出现。
参考资料