Apache Dubbo,一款高性能、轻量级的RPC框架。这里就其在Spring Boot下的实践进行介绍
注册中心: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
子模块: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服务来进行验证、测试工作。测试结果如下所示,符合预期
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。效果如下所示
直连
在前面的例子中,服务的消费者是通过注册中心来获取所需服务的提供者信息。而有时候,在开发、联调的过程中,我们希望能够直接调用指定的服务提供者,而不经过注册中心。即所谓的直连。从上图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的管理页面可以看到,注册中心已经下线了
现在,让我们测试下,结果符合预期
Note
- 在生产者、消费者之间进行网络传输的类需要实现序列化接口Serializable