本系列文章的目的是搭建出一个基于Netty,Zookeeper和SpringBoot的简易分布式RPC框架,并且发布到Maven中央仓库以 spring-boot-starter 的形式对外提供开箱即用的服务。1.0 版本使用 protobuf 来做序列化,最终的使用形式比较接近于 Dubbo 。最终效果可见:https://github.com/linshenkx/rpc-netty-spring-boot-starter 。
一 RPC设计
如下图,本RPC框架主要有Registry、RPC Server 和 RPC Client 3个角色。
- Registry
注册中心,底层实现是Zookeeper,可借助ZK集群达到服务的高可用 - RPC Server
RPC服务器,即服务提供方,通过自定义的协议而非http接口对外提供服务实现
需要向Registry注册服务信息和自身信息 - RPC Client
RPC客户端,即服务消费者,通过向Registry订阅服务,获取提供对应服务实现的RPC Server信息,从而通过自定义协议从RPC Server获取服务实现。
注意:本框架1.0版本未完善 RPC Client 和 Registry 的发布订阅模型,所以每次都需要 RPC Client去 Registry 获取信息再 向 RPC Server 发起服务调用,也由此带来极大的性能损耗和浪费。1.0版本只是实现了一个框架原型,后续版本将对性能等做优化。
二 模块结构
结构图如下,其中 examples 模块不依赖于父工程,可独立运行,不属于框架的一部分。
rpc-netty-spring-boot-starter 下有5个模块,其中外部使用的是两个 starter ,而真正工作的事另外三个模块。
- rpc-netty-common
封装统一规则,如使RPC Server和RPC Client 可以基于同一协议通信。
内含 自定义RPC通信对象及序列化方法,还有对应的Netty编码器、解码器等。 - rpc-netty-server-spring-boot-autoconfigure
- 核心类是RpcServer,负责提供RPC服务
- 内含 ZKServiceRegistry 负责向zk集群注册服务,@RpcService 注解对外提供使用
- 另外还有 RpcServerHandler 提供Netty 通信处理,ZKProperties 和 RpcServerProperties 提供属性注入。
- rpc-netty-server-spring-boot-starter
对 rpc-netty-server-spring-boot-autoconfigure 进行包装,本身无功能实现 - rpc-netty-client-spring-boot-autoconfigure
- 核心类是RpcClient,负责获取服务实现生成代理类
- 内含 ZKServiceDiscovery 负责发现指定服务的RPC Server信息
- 另外还有 RpcClientHandler 提供Netty 通信处理,ZKProperties 提供属性注入。
- rpc-netty-client-spring-boot-starter
对rpc-netty-client-spring-boot-autoconfigure 进行包装,本身无功能实现
三 工作流程
RPC Server
需要注意,RPC Server需一直维持与zk集群的连接,如果关闭RPC Server,zk上注册的信息会随之清除。
RPC Client
需要注意,这里没有使用发布订阅模型,没有维持连接,所以每次使用都需要去获取服务实现,没有使用池化技术和缓存。需要在后续版本改良。
四 功能依赖
- rpc-netty-common 模块
- protostuff-core :Rptostuff 核心模块,提供对象序列化和反序列化功能
- protostuff-runtime:Protostuff 运行时模块,用于生成所需的 Protostuff Schema 对象
- objenesis:提供比 JDK 更高效的反射功能,用于对象反序列化
- rpc-netty-server-spring-boot-autoconfigure 和 rpc-netty-server-spring-boot-autoconfigure 模块
- spring-boot-starter:提供spring-boot 基础服务,如 spring-context 信息等,该依赖scope 应为 provided ,由使用方提供依赖,避免版本冲突
- spring-boot-configuration-processor:提供属性注入,scope同上
- rpc-netty-common:提供统一服务
- zkclient :提供对zk集群的操作,注意应使用 exclusion 将其 slf4j-log4j12 模块排除在classpath,否则会与spring-boot日志框架重复或与Lombok的@log4j2冲突
除了上述依赖,还有以上3个模块都依赖的
- netty-all :Netty模块,提供NIO通信所需的API,是整个RPC框架的基础
- lombok:其 optional 应为true ,避免传递依赖
另外,两个spring-boot-starter分别只依赖各自的spring-boot-autoconfigure模块
注意,为了避免依赖冲突问题,这里使用到了 scope设为provided,exclusion排除模块, optional设为true 三种方法。详情请见:provided,optional 和 exclusion 最全区分指南
- scope设为provided:对依赖整体有效,依赖将提供编译而不参与打包,由使用方提供
在本项目中,则是在发布后,由使用 spring-boot-starter 的spring-boot工程提供。 - exclusion排除特定组件:避免传递依赖
本项目中,用于移去 zkclient 的 slf4j-log4j12 模块,避免依赖导入 - optional设为true:避免传递依赖
本项目中,rpc-netty-server-spring-boot-autoconfigure依赖optional为true的 Lombok ,但 rpc-netty-server-spring-boot-starter则不含 Lombok 模块,避免依赖传出
五 父工程pom
这里我把部署到maven 中央仓库的内容删去,不影响正常使用,感兴趣的可以到GitHub找完整项目查看,部署的中央仓库的方法见:使用gpg插件发布jar包到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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.linshenkx</groupId>
<artifactId>rpc-netty-spring-boot-starter</artifactId>
<version>1.0.5.RELEASE</version>
<packaging>pom</packaging>
<name>rpc-netty-spring-boot-starter</name>
<description>基于Netty的简易RPC框架</description>
<url>https://github.com/linshenkx/rpc-netty-spring-boot-starter</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-boot.version>2.1.0.RELEASE</spring-boot.version>
<spring-cloud.version>Finchley.SR2</spring-cloud.version>
<netty.version>4.1.31.Final</netty.version>
<protostuff.version>1.5.9</protostuff.version>
<objenesis.version>3.0.1</objenesis.version>
<zkclient.version>0.11</zkclient.version>
</properties>
<modules>
<module>rpc-netty-common</module>
<module>rpc-netty-server-spring-boot-autoconfigure</module>
<module>rpc-netty-server-spring-boot-starter</module>
<module>rpc-netty-client-spring-boot-autoconfigure</module>
<module>rpc-netty-client-spring-boot-starter</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring-boot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.linshenkx</groupId>
<artifactId>rpc-netty-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.github.linshenkx</groupId>
<artifactId>rpc-netty-server-spring-boot-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.github.linshenkx</groupId>
<artifactId>rpc-netty-client-spring-boot-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-core</artifactId>
<version>${protostuff.version}</version>
</dependency>
<dependency>
<groupId>io.protostuff</groupId>
<artifactId>protostuff-runtime</artifactId>
<version>${protostuff.version}</version>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>${objenesis.version}</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>${zkclient.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
</project>
其他
参考资料:
项目GitHub主页:https://github.com/linshenkx/rpc-netty-spring-boot-starter
《架构探险 轻量级微服务架构 下册》黄勇 著,第四章-微服务通信
Dubbo GitHub主页:https://github.com/apache/incubator-dubbo
https://github.com/luxiaoxun/NettyRpc
https://github.com/yidongnan/grpc-spring-boot-starter