- 搭建基础
- rpc server
- rpc-common
- rpc-server
- rpc-client
新建一个项目,叫my-rpc。
这个项目里暂时有三个模块,分别是client, common, server
新建项目时,可以删除不必要的文件,如HELP.md、mvnw、mvnw.cwd,以及/test目录和pom文件里的test依赖
最终项目目录形如:
在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模块
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进行定义
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-client1.客户端主要做的事就是:
“将服务(用接口表示)调用进行代理,
由代理向注册中心询问服务端地址,
并发送请求给服务端,
获得响应后进行返回”
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.跟服务端一样,在配置属性的时候,最好再用一个属性配置类来实现(和服务端逻辑一样)