Apache Dubbo,一款高性能、轻量级的 RPC 框架。这里就其在 Spring Boot 下的实践进行介绍

abstract.png
注册中心:ZooKeeper
这里我们选用 ZooKeeper 作为 Dubbo 的注册中心。Docker 命令如下所示
1 2 3 4 5 6 7 8
| docker pull zookeeper
docker run --name ZooKeeper-1 -p 2181:2181 -d \ -v /Users/zgh/Docker/ZooKeeper/ZooKeeper-1/conf:/conf \ -v /Users/zgh/Docker/ZooKeeper/ZooKeeper-1/data:/data \ -v /Users/zgh/Docker/ZooKeeper/ZooKeeper-1/datalog:/datalog \ -v logs:/logs zookeeper
|
基于 Maven 的聚合工程
为了便于演示,先建立一个基于 Maven 的聚合工程,其含有三个子模块:common-api、provider 和 consumer

figure 1.jpeg
子模块:common-api
该模块用于提供服务提供者、消费者的公共类、接口。这里,我们提供一个学生信息的服务接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.aaron.api.service;
import com.aaron.api.pojo.ResultData; import com.aaron.api.pojo.Student;
public interface StudentService{
ResultData<Student> getStuById(Integer 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 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
| package com.aaron.api.pojo;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable;
@Data @AllArgsConstructor @NoArgsConstructor public class ResultData<T> implements Serializable{
private String code;
private String msg;
private T data; } ... package com.aaron.api.pojo;
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable;
@Data @AllArgsConstructor @NoArgsConstructor public class Student implements Serializable {
private Integer id;
private String name;
private String major; }
|
最后,将该模块打成 jar 包并安装到 Maven 的本地仓库下
子模块:provider
该模块作为服务的提供者,该模块的 POM 文件部分配置如下。可以看到我们不仅引入了 Dubbo、ZooKeeper 客户端、Netty 等相关组件的依赖,还需要引入公共服务接口 common-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 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
| ... <dependencies>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.4.RELEASE</version> </dependency>
<dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.8</version> </dependency>
<dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.8.0</version> </dependency>
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.0.35.Final</version> </dependency>
<dependency> <groupId>com.aaron</groupId> <artifactId>common-api</artifactId> <version>1.0</version> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.aaron.provider.ProdiverApplication</mainClass> </configuration> </plugin> </plugins> </build> ...
|
现在我们提供 StudentService 接口的实现类 ——StudentServiceImpl,代码如下所示。值得一提的是,@Service 是 org.apache.dubbo.config.annotation.Service 包下的注解
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
| package com.aaron.provider.service;
import com.aaron.api.pojo.ResultData; import com.aaron.api.pojo.Student; import com.aaron.api.service.StudentService; import org.apache.dubbo.config.annotation.Service; import java.util.HashMap; import java.util.Map;
@Service public class StudentServiceImpl implements StudentService {
private static Map<Integer, Student> map = new HashMap();
static { map.put( 1, new Student(1, "Aaon", "计算机科学") ); map.put( 2, new Student(2, "Tony", "美容美发") ); map.put( 3, new Student(3, "David", "生物工程") ); }
@Override public ResultData<Student> getStuById(Integer id) { ResultData<Student> studentResultData = new ResultData<>("400", "无数据", null);
Student student = map.get(id); if( student != null ) { studentResultData = new ResultData<>("200","成功, 有数据", student); } return studentResultData; } }
|
对于该模块的配置文件 application.yml 而言,会进行 Dubbo 相关的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| server: port: 8091
dubbo: application: name: Dubbo-Demo1-Provider protocol: name: dubbo port: 20880 registry: address: zookeeper://127.0.0.1:2181 scan: base-packages: com.aaron.provider.service
|
子模块:consumer
该模块作为服务的消费者,其所需的依赖与 provider 模块的依赖完全一致。故其 POM 文件就不再赘述。然后,我们写一个 Controller 来使用 provider 所提供的服务。在依赖注入时,可通过 @Reference 注解引入远程服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.aaron.consumer.controller;
import com.aaron.api.pojo.ResultData; import com.aaron.api.pojo.Student; import com.aaron.api.service.StudentService; import org.apache.dubbo.config.annotation.Reference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;
@RestController public class StudentController {
@Reference private StudentService studentService;
@RequestMapping(value = "/StuInfoController") public ResultData<Student> findStudentById(@RequestParam Integer id) { ResultData<Student> studentResultData = studentService.getStuById(id); return studentResultData; } }
|
对于该模块的配置文件 application.yml 而言,配置如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| server: port: 8093
dubbo: application: name: Dubbo-Demo1-Consumer protocol: name: dubbo port: 20880 registry: address: zookeeper://127.0.0.1:2181
|
验证
至此,本工程涉及到的核心代码就已经开发完成了。现在我们分别启动 provider、consumer 服务来进行验证、测试工作。测试结果如下所示,符合预期

figure 2.jpeg
Dubbo 可视化:dubbo-admin
dubbo-admin,作为 dubbo 自带的可视化工具。这里我们通过 Docker 来进行部署。其中 - e 选项用于设置 ZOOKEEPER_SERVER 环境变量,配置注册中心的地址、端口信息 (通过 ifconfig 命令可知,本机 IP 为 192.168.1.106)。值得一提的是,启动 dubbo-admin 容器时,必须保证 ZooKeeper 服务已经处于运行状态
1 2 3 4 5 6 7
| docker pull webuilder/dubboadmin
docker run --name dubbo-admin \ -p 8080:8080 -d \ -e ZOOKEEPER_SERVER=192.168.1.106:2181 webuilder/dubboadmin
|
现在就可以通过 http://127.0.0.1:8080 来访问 Web 页面。其中,root、guest 账号的密码分别为 root、guest。效果如下所示

figure 3.jpeg
直连
在前面的例子中,服务的消费者是通过注册中心来获取所需服务的提供者信息。而有时候,在开发、联调的过程中,我们希望能够直接调用指定的服务提供者,而不经过注册中心。即所谓的直连。从上图 dubbo-admin 的页面,我们可以看到 StudentService 接口提供者的地址信息:dubbo://192.168.1.106:20880/com.aaron.api.service.StudentService
现在为了验证不使用注册中心,直连也是有效的。我们一方面停止 ZooKeeper 容器,另一方面修改 provider、consumer 模块的配置文件,将注册中心的地址设为 N/A
1 2 3 4 5 6
| ... dubbo: ... registry: address: N/A ...
|
然后,在消费者的 @Reference 注解中,通过 url 属性来指定相关服务的地址即可
1 2 3 4 5 6 7 8 9
| ... @RestController public class StudentController {
@Reference(url = "dubbo://192.168.1.106:20880/com.aaron.api.service.StudentService") private StudentService studentService; ... } ...
|
修改完毕后,重启 provider、consumer 服务。通过 dubbo-admin 的管理页面可以看到,注册中心已经下线了

figure 4.jpeg
现在,让我们测试下,结果符合预期

figure 5.jpeg
Note
- 在生产者、消费者之间进行网络传输的类需要实现序列化接口 Serializable
v1.5.2