Dubbo 是阿里开源的产品,采用二进制通信,相比 OpenFeign 的 http 通信,具有性能优势,可以轻松集成到 SpringBoot 和 Spring Cloud 中使用,对于性能要求比较高的场景,使用比较广泛。早期的 Dubbo 都采用 Zookeeper 作为注册中心,现在基本上大家都使用 Nacos 作为注册中心,毕竟 Dubbo 和 Nacos 都是阿里的产品,Nacos 也确实非常强大。
本篇博客主要通过代码 Demo 介绍如何使用 SpringBoot 集成 Dubbo,在博客最后会提供源代码下载。
一、准备工作
在前面的博客中,已经介绍了如何搭建 Nacos,这里不再赘述,我在自己的虚拟机中搭建了单机版 Nacos,虚拟机的 ip 地址是 192.168.216.128,搭建好之后默认不需要登录就可以访问,端口是 8848,这里全都保持不变。
阿里官方为 Dubbo 提供了一个管理后台 Dubbo Admin,网址:https://github.com/apache/dubbo-admin
下载最新的 DubboAdmin ,使用 IDEA 打开进行打包,在 dubbo-admin-server 下的 target 目录下能够获取 jar 包。
我把 jar 包名字修改为 dubbo-admin.jar ,上传到虚拟机的 /app/dubbo-admin 目录下。
将 dubbo-admin-server 项目下的 application.properties 复制一份,也上传到虚拟机的 /app/dubbo-admin 目录下。
修改 application.properties 文件,主要修改启动端口,登录的账号密码,以及其注册的 Nacos 地址,内容如下:
# 我这里使用 8181 作为 dubbo admin 网站的启动端口
server.port=8181
admin.registry.address=nacos://192.168.216.128:8848
admin.config-center=nacos://192.168.216.128:8848
admin.metadata-report.address=nacos://192.168.216.128:8848
admin.root.user.name=root
admin.root.user.password=root
然后进入 /etc/systemd/system 目录下,新建一个文件 dubboAdminServer.service ,填写内容如下:
[Unit]
Description=DubboAdminServer
After=syslog.target network.target
[Service]
Type=simple
#注意:ExecStart 后面的命令脚本都是写在一行中,没有手动进行换行,只是横向空间不够,自动换行了
ExecStart=/app/jdk1.8/bin/java -jar /app/dubbo-admin/dubbo-admin.jar --spring.config.location=/app/dubbo-admin/application.properties
ExecStop=/bin/kill -15 $MAINPID
User=root
Group=root
[Install]
WantedBy=multi-user.target
然后执行以下命令,将 dubbo admin 安装为 linux 服务,后续维护就比较方便了
# 只要修改了 dubboAdminServer.service 文件就必须指定下面这个命令
systemctl daemon-reload
# 启动服务
systemctl start dubboAdminServer
# 将服务设置为开机启动
systemctl enable dubboAdminServer
我们配置的 dubbo admin 网站的端口是 8181,账号密码都是 root ,访问 http://192.168.216.128:8181
登录后界面如下:
然后访问 Nacos 的网址:http://192.168.216.128:8848/nacos
就能够看到 dubbo admin 注册的服务信息:
二、搭建工程
搭建一个 SpringBoot 父工程,里面包含 3 个子工程,如下图所示:
dubbo_common 是抽取出来的公共项目,里面是公用的实体类和接口方法
dubbo_provider 是 dubbo 服务接口的提供者,实现 dubbo_common 中定义的接口
dubbo_consumer 是 dubbo 服务的消费者,用来调用 dubbo 服务提供的 tcp 接口
父工程的 pom 文件内容如下所示,对于 3 个子工程都需要引入的 jar 包依赖,可以写在父工程的 pom 文件中:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.jobs</groupId>
<artifactId>springboot_dubbo</artifactId>
<version>1.0</version>
<modules>
<module>dubbo_common</module>
<module>dubbo_provider</module>
<module>dubbo_consumer</module>
</modules>
<packaging>pom</packaging>
<!-- 这里使用的 SpringBoot 的版本是 2.4.5 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!-- 引入该依赖,主要在创建实体对象时,省去写 get 和 set 方法的麻烦,同时使用其日志打印功能 -->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
三、公共实体类和接口
由于 Dubbo 采用 Tcp 协议通信,实体类数据对象在服务器之间传输,需要进行序列化和反序列化,因此实体类必须实现 Serializable 接口。dubbo_common 的实体类和接口代码细节如下所示:
//报销费用
@Data
public class Cost implements Serializable {
private Integer id;
//报销类别
private String category;
//报销金额
private Integer money;
//报销人
private Employee employee;
}
//员工
@Data
public class Employee implements Serializable {
private Integer id;
//姓名
private String name;
//部门
private String depart;
}
//本博客的 Demo 中,接口中只定义了一个方法:通过 id 获取员工信息
public interface EmployeeService {
Employee getEmployeeById(Integer id);
}
四、服务提供者
对于服务的提供者,由于提供的是 Tcp 接口,不需要提供 Http 接口,因此引入最简单的 SpringBoot 依赖即可:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot_dubbo</artifactId>
<groupId>com.jobs</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo_provider</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--如果该 springboot 程序只是做 dubbo 的服务端提供 tcp 接口,
不对外提供 http 接口的话,那么只需要引用 springboot 的起步依赖即可。-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--引入 dubbo 的起步依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<!--我们使用 nacos 作为 dubbo 的注册中心,因此需要引入以下依赖包-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>2.7.8</version>
</dependency>
<!--引入公共的实体类和接口-->
<dependency>
<groupId>com.jobs</groupId>
<artifactId>dubbo_common</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
SpringBoot 集成 Dubbo 需要引入 dubbo-spring-boot-starter 依赖,由于使用 Nacos 作为服务的注册中心,因此需要引入 dubbo-registry-nacos 依赖,然后定义一个类 EmployeeServiceImpl 实现 dubbo_common 中的 EmployeeService 接口,在实现类上需要使用 @DubboService 即可,内容如下:
package com.jobs.service.impl;
import com.jobs.pojo.Employee;
import com.jobs.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import java.util.Random;
//可以指定接口的版本
//@DubboService(version = "1.0")
//@DubboService(version = "2.0")
@Slf4j
@DubboService
public class EmployeeServiceImpl implements EmployeeService {
@Override
public Employee getEmployeeById(Integer id) {
Employee employee = new Employee();
employee.setId(id);
//随机生成汉字
int temp1 = 16 + new Random().nextInt(40);
int temp2 = 1 + new Random().nextInt(94);
byte[] bytes = new byte[2];
bytes[0] = (byte) (0xa0 + temp1);
bytes[1] = (byte) (0xa0 + temp2);
String ch;
try {
ch = new String(bytes, "gb2312");
} catch (Exception ex) {
ch = "null";
}
employee.setName("小" + ch);
String[] departArr = {"研发部", "财务部", "销售部", "运营部", "人事部", "宣传部"};
Random random = new Random();
int index = random.nextInt(6);
employee.setDepart(departArr[index]);
log.info("返回员工的 id 为:" + id);
return employee;
}
}
然后在服务提供者 dubbo_provider 项目的 application.yml 对 dubbo 服务进行相关配置:
spring:
application:
name: dubbo-provider
# 配置 dubbo 提供者信息:采用 dubbo 协议,指定服务的 tcp 端口
dubbo:
protocol:
name: dubbo
port: 22222
# 使用 nacos 作为注册中心
registry:
address: nacos://192.168.216.128:8848
# dubbo 扫描服务接口的包,一般扫描接口所在的包即可
scan:
base-packages: com.jobs.service
这里使用 22222 作为 dubbo 服务的 tcp 端口,因此服务提供者所部署的服务器有防火墙的话,需要开放这个端口,本篇博客由于服务提供者和服务消费者都是在本地的机器上,因此不存在防火墙端口不同的问题,在实际生产环境中,服务提供者和消费者很有可能会部署在不同的服务器上。
五、服务消费者
对于服务的消费者 dubbo_consumer 来说,由于需要提供 Http 接口让我们进行测试,因此需要引入 SpringBoot 的 Web 依赖,除此之外其它的依赖包跟 dubbo_provider 提供者引入的 jar 包依赖完全相同:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot_dubbo</artifactId>
<groupId>com.jobs</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>dubbo_consumer</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--这里的 dubbo 消费者对外提供 http 接口,用于通过浏览器测试,
因此引入了 springboot web 的依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入 dubbo 的起步依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<!--我们使用 nacos 作为 dubbo 的注册中心,因此需要引入以下依赖包-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>2.7.8</version>
</dependency>
<!--引入公共的实体类和接口-->
<dependency>
<groupId>com.jobs</groupId>
<artifactId>dubbo_common</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
定义一个 Controller 提供一个 Get 请求方式的 Http 接口,供我们在浏览器地址栏上直接敲击测试使用:
package com.jobs.controller;
import com.jobs.pojo.Cost;
import com.jobs.pojo.Employee;
import com.jobs.service.EmployeeService;
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;
import java.util.Random;
@RequestMapping("/emp")
@RestController
public class EmployeeController {
//引用 dubbo 提供的服务,可以指定版本,指定负载均衡算法,重试次数,超时毫秒等。
//重试次数 retries 可以统一在 application.yml 进行配置,对于增删改操作,建议不要重试
//负载均衡默认为 random ,还可以配置为:roundrobin 、leastactive 、consistenthash
//超时毫秒数 timeout ,可以统一在 application.yml 进行配置,在具体服务上做个性化配置
@DubboReference(loadbalance = "random")
private EmployeeService employeeService;
@GetMapping("/{id}")
public Cost queryCostDetail(@PathVariable("id") Integer id) {
Cost cost = new Cost();
cost.setId(id);
String[] arr = {"打车费", "团建费", "交通费", "招待费", "电话费", "住宿费"};
Random rd = new Random();
int index = rd.nextInt(6);
cost.setCategory(arr[index]);
cost.setMoney(rd.nextInt(500));
Employee emp = employeeService.getEmployeeById(id);
cost.setEmployee(emp);
return cost;
}
}
然后在 application.yml 中配置上 dubbo 相关的信息,另外可以从全局的角度配置上请求超时时间、重试次数、在启动消费者程序时,是否到注册中心检查服务提供者是否存在等信息,具体内容如下:
server:
port: 8888
spring:
application:
name: dubbo-consumer
# 配置 dubbo 消费者信息
dubbo:
registry:
address: nacos://192.168.216.128:8848
consumer:
# 关闭了启动检查,这样消费者启动时,不会到 nacos 中检查服务提供者是否存在
check: false
# 建议在这里统一配置为不重试请求,对于查询来说可以在代码中单独配置重试次数
retries: 0
# 默认情况下限制请求必须在 1000 毫秒内完成,对于具体服务可以在代码中单独配置
timeout: 1000
六、验证成果
启动 dubbo_provider 和 dubbo_consumer 程序,然后在 Dubbo Admin 上都可以看到服务信息,你也可以点击详情进行查看。
在 Nacos 上能够看到【服务提供者】和【服务消费者】都注册上去了,你也可以点击详情进行查看细节:
由于我们配置的服务消费者网站启动的端口是 8888 ,因此你可以调用一下消费者提供的 http 接口进行测试。
比如调用 http://localhost:8888/emp/1
查询 id 为 1 的员工报销款信息,消费者会调用提供者的 dubbo 接口获取员工信息。
本篇博客的 Demo 源代码下载地址:https://files.cnblogs.com/files/blogs/699532/springboot_dubbo.zip