Dubbo 搭建一个简单的RPC框架

    科技2024-07-18  62

    安装 ZooKeeper brew install zookeeper安装完成以后启动 zookeeper,如果是通过 homebrew 方式安装的话,直接调用 zkServer start即可。

    生产者部分

    创建一个 Spring Boot 项目,作为其中的生产者。其中,在 maven 依赖的时候,需要引入以下依赖包。 <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itcenter.mall</groupId> <artifactId>provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>provider</name> <description>User service provider project for Spring Boot</description> <properties> <java.version>11</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.itcenter.mall</groupId> <artifactId>mallInterface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-spring-boot-starter --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.8</version> </dependency> <!-- 引入 zookeeper 的注册中心 --> <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>5.1.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>5.1.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${java.version}</source> <target>${java.version}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.itcenter.mall.provider.ProviderApplication</mainClass> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project> 配置生产者,注意:生产者配置在resources文件夹下的 application.properties 配置以下属性。 # dubbo 的应用名称 dubbo.application.name=userServiceProvider # dubbo 注册中心相关属性 # 注册中心所用的协议名 dubbo.registry.protocol=zookeeper # 注册中心的地址 dubbo.registry.address=127.0.0.1:2181 # dubbo RPC 协议部分相关属性 # 协议名 dubbo.protocol.name=dubbo # 协议端口 dubbo.protocol.port=20880

    注意:这些都可以配置成 xml 格式的,但是由于这是 Spring Boot 工程,所以说配置起来的话都是在 properties 文件中配置即可。 5. 创建 Java 类 其中文件目录如下: 其中 UserServiceImpl 当中的代码实现如下:

    package com.itcenter.mall.provider.service.impl; import com.itcenter.mall.entity.UserAddress; import com.itcenter.mall.service.UserService; import org.apache.dubbo.config.annotation.DubboService; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.List; /** * UserServiceImpl class * * @author apple */ @Service @DubboService public class UserServiceImpl implements UserService { @Override public List<UserAddress> getUserAddressList(String userId) { var userAddress1 = new UserAddress(1, "1"); var userAddress2 = new UserAddress(2, "2"); // 注意:这个地方不能使用 List.of 方法来进行传递,因为传递的对象不是 ImmutableCollections.List12 类的对象,是他的代理类对象,代理类对象不序列化其中的列表。所以消费者那边会抛出 UnsupportedOperationException 异常 return Arrays.asList(userAddress1, userAddress2); } }

    其中我们发现,UserAddress 类与 UserService 接口并没有导入,这是因为在 RPC 中,消费者也同时需要引入 UserService 来调用里面的方法,所以说需要将 RPC 调用的接口以及其中需要传输的实体类封装到一个项目当中,并且在生产者与消费者之间的数据传递过程中,都需要依赖该接口包来使用。(生产者负责实现的接口,而消费者需要调用接口来获得数据。)所以说,我们需要创建一个接口项目,来封装整个项目。

    注意:在启动项目的类中,添加 @EnableDubbo 注解才能正常启动 Dubbo,否则消费者会找不到生产者而报错。

    接口封装项目

    在接口封装的项目中,我们使用一个 maven 项目即可。不用使用 Spring Boot 项目进行封装。里面的文件结构如下: 其中的 pom.xml 当中的数据如下:

    <?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.itcenter.mall</groupId> <artifactId>mallInterface</artifactId> <version>1.0-SNAPSHOT</version> <name>mallInterface</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> </dependencies> </project>

    由于 Service 中需要传递实体类数据,所以说传递的实体类也需要封装到该项目当中,并且实现 Serializable 接口,并且需要将 serialVersionUID 生成出来,防止在不同机器的 JVM 中序列化错误。 实体类 UserAddress.java 文件如下:

    package com.itcenter.mall.entity; import lombok.AllArgsConstructor; import lombok.Data; import java.io.Serializable; /** * UserAddress class * * @author apple */ @Data @AllArgsConstructor public class UserAddress implements Serializable { // 这个地方根据项目的不同生成不同的 serialVersionUID private static final long serialVersionUID = -7540180539502669771L; private Integer id; private String userId; }

    OrderService.java 文件实现如下:

    package com.itcenter.mall.service; import com.itcenter.mall.entity.UserAddress; import java.util.List; /** * OrderService class * * @author apple */ public interface OrderService { /** * 根据用户 id 初始化订单 * * @param userId 用户 id * @return 初始化订单信息 */ List<UserAddress> initOrder(String userId); }

    UserService.java 文件实现如下:

    package com.itcenter.mall.service; import com.itcenter.mall.entity.UserAddress; import java.util.List; /** * UserService class * * @author apple */ public interface UserService { List<UserAddress> getUserAddressList(String userId); }

    注意:配置完成了以后,生产者部分需要引入接口依赖

    <dependency> <groupId>com.itcenter.mall</groupId> <artifactId>mallInterface</artifactId> <version>1.0-SNAPSHOT</version> </dependency>

    这样生产者部分项目就正常了

    消费者配置

    创建一个 Spring Boot 项目,作为其中的消费者。其中,在 maven 依赖的时候,pom.xml 格式如下。 <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itcenter.mall</groupId> <artifactId>consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>consumer</name> <description>Order service consumer project for Spring Boot</description> <properties> <java.version>11</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.itcenter.mall</groupId> <artifactId>mallInterface</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo-spring-boot-starter --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>2.7.8</version> </dependency> <!-- 引入 zookeeper 的注册中心 --> <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-framework --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>5.1.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes --> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>5.1.0</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${java.version}</source> <target>${java.version}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.itcenter.mall.consumer.ConsumerApplication</mainClass> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project> 配置 application.properties # 应用名称 spring.application.name=consumer # 应用服务 WEB 访问端口 server.port=80 # spring 静态资源扫描路径 spring.resources.static_locations=classpath:/static/ dubbo.application.name=orderServiceImpl dubbo.registry.protocol=zookeeper dubbo.registry.address=127.0.0.1:2181 Java 类文件目录如下: 其中 OrderController.java 文件中代码如下: package com.itcenter.mall.consumer.controller; import com.itcenter.mall.entity.UserAddress; import com.itcenter.mall.service.OrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * OrderController * * @author apple */ @RestController public class OrderController { @Autowired private OrderService orderService; @GetMapping("/initOrder/{userId}") public List<UserAddress> initOrder(@PathVariable("userId") String userId) { return orderService.initOrder(userId); } }

    OrderServiceImpl.java 文件中代码如下:

    package com.itcenter.mall.consumer.service.impl; import com.itcenter.mall.entity.UserAddress; import com.itcenter.mall.service.OrderService; import com.itcenter.mall.service.UserService; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.stereotype.Service; import java.util.List; /** * OrderServiceImpl class * * @author apple */ @Service @Slf4j public class OrderServiceImpl implements OrderService { // 引入 RPC 接口。因为这个接口是生产者实现的 Service,不是本机的 Service。所以说他是用 @DubboReference 注解注入的。 // 注意:这个地方代替的是原来 xml 的 <dubbo:reference> 标签 @DubboReference private UserService userService; @Override public List<UserAddress> initOrder(String userId) { var userAddressList = userService.getUserAddressList(userId); log.info("userAddressList = " + userAddressList); return userAddressList; } }

    注意:在启动项目的类中,添加 @EnableDubbo 注解才能正常启动 Dubbo,否则消费者会找不到生产者而报错。

    消费者部分搭建完成了以后,再分别启动生产者和消费者的程序(注意:这个地方如果不配置检查属性为 false 的话,必须先启动生产者再启动消费者,否则消费者会找不到生产者报错),然后输入http://localhost/initOrder/1 进行验证。最后显示结果如下: 一个生产者与消费者的模型就搭建完成。

    搭建源码地址:DubboExample: 自己的 Dubbo 练习仓库

    Processed: 0.008, SQL: 8