如标题所示,本项目是一个简单的rpc项目。使用了 Netty
用于网络通信,使用了 Nacos
用于管理配置以及注册服务
项目分包比较杂,只分为了服务端(rpc-server
),客户端(rpc-client
)以及两端的公共部分(rpc-common
)。其中序列化
,网络通信
,配置管理
,服务注册
等或多或少都放在了 rpc-common
包中
服务端 rpc-server
中,只包含了 请求消息处理
,bean扫描
等
客户端 rpc-client
中,包含了 负载均衡策略
,jdk动态代理
等
- 服务端启动,会读取类路径中的
nacos-server-config.yml
nacos配置,获取 nacos 服务的地址以及命名空间,以读取保存在 nacos 中的服务端配置 - 从 nacos 中拉取
rpc-server-config.yml
配置,解析后保存在全局配置ConfigProperties
中,以便后期使用 rpc-server-config.yml
配置中包含有服务注册相关信息,比如服务暴露ip
,服务暴露port
,服务权重
等。根据这些配置信息,启动服务端服务,并把自身注册到 nacos 中- 服务启动的同时,会扫描指定包下包含
@RpcServer
注解的实现类,并保存改实现类 Class 对象,为后期反射调用做准备 - 服务端启动成功,nacos 服务也注册成功
- 与服务端一样,客户端启动,也会读取类路径中的
nacos-server-config.yml
nacos配置,获取 nacos 服务的地址以及命名空间,以读取保存在 nacos 中的服务端配置 - 从 nacos 中拉去
rpc-client-config.yml
配置,解析后保存在全局配置ConfigProperties
中,以便后期使用 rpc-client-config.yml
配置中包含了序列化方式
,请求服务名称
,负载均衡策略
等。根据请求服务名称
拉取服务端的在线的服务实例信息,并连接改服务下的所有服务实例- 客户端启动成功
由于 Netty 的空闲策略,对于超过 5s 未响应的客户端连接会主动断开连接;客户端则会在连接服务端之后每隔 3s 发送一次 ping
的请求以维护与服务端的连接
一次完整的rpc调用过程
- 首先客户端通过动态代理的对象调用目标方法,根据选定的
负载均衡策略
获取 Netty 的channel
对象,发起与服务端的网络通信,需要将接口全路径
,执行方法名
,方法参数列表
,参数列表类型
发送给服务端 - 客户端发送网络请求之前,会准备一个响应结果容器
Promise
用于异步通信 - 服务端接收到此次请求之后,根据接口名称获取接口实现类的 Class 对象,并反射调用目前方法,之后将返回结果或是异常发送回客户端
- 客户端接收到来自服务端的请求之后,将结果填充至
Promise
中 - 调用线程获取到结果,整个过程结果
图示如下
描述 | 类型 | 占用字节 | 示例值 |
---|---|---|---|
魔数 | byte数组 | 7 | wuhunyu |
版本 | byte | 1 | 1 |
序列化方式 | byte | 1 | 0 |
序列id | long | 8 | 1L |
消息类型 | int | 4 | 0 |
消息长度 | int | 4 | 300 |
无效字符填充 | byte数组 | 7 | 0000000 |
消息内容 | byte数组 | -- | -- |
目前只实现了 轮询
和 随机
,权重
预留了实现,但未真正实现
服务端空闲读
和客户端空闲写
时间未写入配置文件做成可动态配置参数- 各种线程之间交互通信的最大等待时间未写入配置文件做成可动态配置参数
- 客户端和服务端的配置文件名写死了,不方便同一个服务的多个实例注册到 nacos 中
- 项目最初构建的时候就想着不能依赖 Spring 环境,下一步该写成一个 starter 组件