当前位置 : 主页 > 编程语言 > 其它开发 >

1. 从零开发一个RPC框架

来源:互联网 收集:自由互联 发布时间:2022-06-30
目录 搭建基础 rpc server rpc-common rpc-server rpc-client 搭建基础 新建一个项目,叫my-rpc。 这个项目里暂时有三个模块,分别是client, common, server 新建项目时,可以删除不必要的文件,如HELP

目录
  • 搭建基础
  • rpc server
  • rpc-common
  • rpc-server
  • rpc-client

搭建基础

新建一个项目,叫my-rpc。
这个项目里暂时有三个模块,分别是client, common, server
新建项目时,可以删除不必要的文件,如HELP.md、mvnw、mvnw.cwd,以及/test目录和pom文件里的test依赖
最终项目目录形如:

rpc server

在my-rpc-server模块中,新建一个annotation包
1.在annotation包下新建RpcService.java这一个注解: 用于修饰“可以提供RPC服务的接口”

2.在annotation包下新建EnableRpcServer.java注解: 用于指示是否将应用作为一个rpc server
如果在一个应用的启动类(springboot的启动类上)使用这个@EnableRpcServer注解
我们就用spring的条件注入来注入一个配置类RpcServerConfiguration.java

3.参考springcloud-netflix-eureka的条件注入
在my-rpc-server模块中新建一个config包,
在包中新建一个配置类RpcServerConfiguration.java
同时新建一个辅助配置类RpcServerMarkerConfiguration.java

proxyBeanMethods = false // 在调用配置类的方法时,不使用动态代理重新生成代理对象

辅助配置类的作用是当辅助配置类被引入时,一个Marker对象就被注入到容器中;根据条件注入,存在Marker对象,于是就注入真正的配置类

4.在配置类RpcServerConfiguration.java中,需要注入一个基于netty的server,以暴露服务端提供的服务接口
在my-rpc-server模块中新建一个server包
同时在server包中新建一个RpcServer.java类,
一个RpcServer中需要的两个成员变量是服务端地址(ip+port)注册中心
同时在RpcServer类中需要维护一个“接口名称(接口+版本号)到实现接口的对象之间的映射”

5.在my-rpc-server模块中创建一个registry包,
在registry包下新建一个Registry.java接口类
接口只有一个方法register,就是在注册中心注册当前服务器的一个服务名称和服务地址。

6.在register包下新建一个zookeeper包,实现一个zookeeper的注册中心**ZookeeperRegistry.java,实现Register.java接口

7.改造RpcServer.java,构造方法中传入服务端地址(ip+port)注册中心
实现ApplicationContextAware接口: 获取带@RpcSerivce注解的类,获取暴露的接口
实现InitializingBean接口: 启动Netty服务器接收客户端的请求,向注册中心注册暴露的接口

8.在接收到客户端的请求后,需要对客户端的请求进行解码、再请求本地应用的服务获取结果、最后将结果编码后返回客户端。
这些操作需要设计对应的netty的ChannelHanlder,以及ChannelHandler所依赖的编解码服务,编解码服务放在rpc-common模块

rpc-common

1.在rpc-common模块下新建一个serialize包,
采用protostuff/protobuf进行序列化和反序列化
使用Objenesis实例化对象,比Java反射更加强大
在serialize包下新建CustomSerializer.java序列化和反序列化类,实现了序列化和反序列化的静态方法。

2.在rpc-common模块下新建一个codec包,
在codec包下新建两个类,分别是自定义的解码器和编码器: CustomEncoder.java 和 CustomDecoder.java
分别对应了出站和入站处理

3.同时,在rpc-common模块下新建一个entity,
定义了客户端发给服务端的请求体结构RpcRequest.java
以及服务端返回给客户端的响应体结构RpcResponse.java

4.编解码处理器以及请求体、响应体写好后,还需要定义一个处理器(ChannelHandler),用来处理用户的服务调用。
在rpc-server模块中对handler进行定义

rpc-server

1.打开rpc-server模块,首先将需要用的rpc-common模块在pom文件中导入
新建一个handler包
在handler包中新建一个处理器类RpcRequestHandler.java,这是RPC服务端(rpc-server)的核心
重写channelRead0和exceptionCaught方法即可

2.修改RpcServer.java类中的init()方法,将需要的ChannlerHanlder加入到netty服务器中

3.修改自动注册类RpcServerConfiguration.java,自动注册一个基于netty的Server
在修改注册类时,还存在有些属性,比如RpcServer的服务地址,注册中心的地址需要手动指定
但是这些属性应该放在配置文件里,然后让springboot自动读取。

4.所以在config包下新建一个存放配置属性的类RpcServerProperties.java
然后在RpcServerConfiguration.java类中引入一个属性类RpcServerProperties.java的对象。
接着实现在配置类中注册一个RpcServer.java类的对象。

5.最后实现springboot的自动配置,在rpc-server模块的/src/main/resources目录下,新建一个/META-INF目录
在/META-INF目录下新建spring.factories配置文件
在spring.factories配置文件中配置需要被springboot自动注册的配置类,如:

org.springframework.boot.autoconfigure.EnableAutoConfiguration =\
    com.tsj.myrpcserver.config.RpcServerConfiguration

至此服务端已经基本完工了。

rpc-client

1.客户端主要做的事就是:
“将服务(用接口表示)调用进行代理,
由代理向注册中心询问服务端地址,
并发送请求给服务端,
获得响应后进行返回”

2.在rpc-client模块下新建一个annotation包,
在annotation包中新建一个RpcInvoke.java注解,
用于标记某一个接口的在springboot容器中存在一个代理对象,通过rpc完成对应方法
RpcInvoke注解中需要一个ServiceVersion变量用于指示服务的版本号。

3.在解析上述注解之前,需要先定义:

  • 一个向注册中心询问的“服务发现”工具RegistryDiscovery
    在rpc-client模块下新建discovery包,在discovery包中新建RegistryDiscovery.java接口和ZookeeperResgitryDiscovery.java类
    RegistryDiscovery接口主要是向注册中心发送询问,获得服务端地址。
  • 一个基于netty的客户端
    在rpc-client模块下新建client包,在client包中新建RpcClient.java类。定义一个netty客户端,向服务端发送请求并接收服务端响应。
  • 一个代理对象生成类。
    在rpc-client模块下新建proxy包,在proxy包中新建RpcProxy.java类,用于生成代理对象。为了生成代理对象,需要netty客户端和服务发现工具(发现注册中心)。

4.开始解析RpcInvoke.java注解,首先新增一个config包
在config包下新增一个RpcClientAutoConfiguration.java类
在这个配置类中对所有被标记了RpcInvoke.java的接口创建对应的代理对象,并注册到容器中。

5.跟服务端一样,在配置属性的时候,最好再用一个属性配置类来实现(和服务端逻辑一样)

网友评论