之前我们讲解了RPC的基本原理,接下来,我们就试着自己去实现一个轻量级的 RPC 框架。
技术选型
- 我们首先选择 ZooKeeper 作为我们 RPC 框架的注册中心,提供服务注册和发现的功能。
- 选用 Netty 作为我们的底层通信框架,Netty 是一个异步的基于事件驱动的 IO 通信框架,基于 NIO 的,能很好的满足我们 RPC 对于通信的要求。
- 我们提供了多种序列化框架,如 Hessian、Kryo、Protostuff、JSON 等,用户可以根据自己的需求选择合适的序列化框架。
- 我们自定义了消息协议,能更好的满足 RPC 的需求。
项目总体结构
使用maven聚合工程,结构如下:
- lightweight-rpc-framework,父工程。
- consumer,服务消费者,是lightweight-rpc-framework的子工程,依赖于rpc-client-spring-boot-starter。
- provider,服务提供者,是lightweight-rpc-framework的子工程,依赖于rpc-server-spring-boot-starter。
- provider-api,服务提供者暴露的服务API,是lightweight-rpc-framework的子工程。
- rpc-client-spring-boot-starter,rpc客户端starter,封装客户端发起的请求过程(动态代理、网络通信)。
- rpc-core,rpc核心依赖,负载均衡策略、消息协议、协议编解码、序列化、请求响应实体、服务注册发现。
- rpc-server-spring-boot-starter,rpc服务端starter,负责发布 rpc 服务,接收和处理 rpc 请求,反射调用服务端。
项目的基本使用
由上面的模块依赖可以知道 rpc 框架主要是就是以 rpc 开头的这几个模块,在使用的时候:
- 消费者(consumer)需要依赖
rpc-client-spring-boot-starter
。 - 服务提供者需要依赖
rpc-server-spring-boot-starter
。这样基本就可以了,因为使用了spring boot自动配置,所以消费者和提供者启动的时候都会去加载starter里的spring.factories文件,会自动将需要的bean自动装配到IOC容器中。 - 注册中心使用 Zookeeper,所以需要安装 ZooKeeper项目才能使用。
- 消费者和服务提供者需要配置注册中心的地址(默认127.0.0.1:2181)以及服务启动端口,服务提供者还需要配置 rpc 监听端口。
发布服务和消费服务
对于发布的服务需要使用 @RpcService 注解标识,复合注解,基于 @Service。
@RpcService(interfaceType = HelloWordService.class, version = "1.0")
public class HelloWordServiceImpl implements HelloWordService {
@Override
public String sayHello(String name) {
return String.format("您好:%s, rpc 调用成功", name);
}
}
消费服务需要使用 @RpcAutowired 注解标识。
@RpcAutowired(version = "1.0")
private HelloWordService helloWordService;
这样,我们就能直接使用 helloWordService 去调用里面的方法了。
@GetMapping("/hello/rpc")
@ResponseBody
public String helloRpcService(@RequestParam("name") String name){
return helloRpcService.sayHello(name);
}
项目启动流程
服务提供者启动
- 服务提供者 provider 会依赖 rpc-server-spring-boot-starter 。
- ProviderApplication 启动,根据springboot 自动装配机制,RpcServerAutoConfiguration 自动配置生效。
- RpcServerProvider 是一个bean后置处理器,会发布服务,将服务元数据注册到ZK上。
- RpcServerProvider.run 方法会开启一个 netty 服务。
服务消费者启动
- 服务消费者 consumer 会依赖 rpc-client-spring-boot-starter。
- ConsumerApplication 启动,根据springboot 自动装配机制,RpcClientAutoConfiguration 自动配置生效。
- 将服务发现、负载均衡、代理等bean加入IOC容器。
- 后置处理器 RpcClientProcessor 会扫描 bean ,将被 @RpcAutowired 修饰的属性动态赋值为代理对象。
调用过程
1. 服务消费者 发起请求 http://localhost:9090/hello?name=hello 。
2. 服务消费者 调用 helloWordService.sayHello() 方法,会被代理到执ClientStubInvocationHandler.invoke()方法。
3. 服务消费者 通过ZK服务发现获取服务元数据,找不到报错404。
4. 服务消费者 自定义协议,封装请求头和请求体。
5. 服务消费者 通过自定义编码器 RpcEncoder 将消息编码。
6. 服务消费者 通过服务发现获取到服务提供者的ip和端口, 通过Netty网络传输层发起调用。
7. 服务消费者 通过 RpcFuture 进入返回结果(超时)等待。
8. 服务提供者 收到消费者请求。
9. 服务提供者 将消息通过自定义解码器 RpcDecoder 解码 。
10. 服务提供者 解码之后的数据发送到 RpcRequestHandler 中进行处理,通过反射调用执行服务端本地方法并获取结果。
11. 服务提供者 将执行的结果通过 编码器 RpcEncoder 将消息编码。(由于请求和响应的协议是一样,所以编码器和解码器可以用一套)。
12. 服务消费者 将消息通过自定义解码器 RpcDecoder 解码。
13. 服务消费者 通过RpcResponseHandler将消息写入 请求和响应 池中,并设置 RpcFuture 的响应结果。
14. 服务消费者 获取到结果。
环境搭建
- 操作系统:Windows
- 集成开发工具:IntelliJ IDEA
- 项目技术栈:SpringBoot 2.5.2 + JDK 1.8 + Netty 4.1.42.Final
- 项目依赖管理工具:Maven 4.0.0
- 注册中心:Zookeeeper 3.7.0
项目测试
- 启动 Zookeeper 服务器:bin/zkServer.cmd
- 启动 provider 模块 ProviderApplication
- 启动 consumer 模块 ConsumerApplication
- 测试:浏览器输入 http://localhost:9090/hello?name=hello,成功返回 您好:hello, rpc 调用成功
上面大致介绍了 RPC 的实现,关于该 RPC 中的组件,我们后面再一个个详细的讲解,先清楚大致的流程即可。