前言: 之前看到了JavaGuide 写的一个rpc我的项目,拜读了一下文档,有点小缺点就是没有我的项目运行的文档,在我的项目上面有局部小伙伴留言说,“我的项目运行不起来”。本人试了一下 运行其实做过微服务的人多多少少都晓得怎么弄,但局部没怎么接触微服务架构的人就不晓得怎么弄,此文章一方面是作为JavaGuide的运行阐明,次要的还是从底层钻研下 这个rpc性能 具体是怎么实现的,为什么服务可能被注册 被发现并调用呢,当然同时你还坚固了一下spring根底的常识。
1. 对于我的项目
技术: 反射(如注解式注入)、jdk动静代理 、序列化(Kryo 序列化) 、 中间件(Zookeeper) Map缓存 、 Netty(通信技术)、音讯编码器/解码器 、
机制: SPI机制
其余: Curator操作zookeeper
2. 我的项目运行
2.1. 应用 Edit Configurations 新建两个Application
别离命名为Server_Netty_Application 和
Client_Netty_Application。主类别离为example-server文件夹下的NettyServerMain类,以及example-client文件夹下的NettyClientMain(这样看起来比拟直观而已)
2.2. 其余阐明:
先开启zookeeper ,再一次启动Server_Netty_Application 和
Client_Netty_Application。 倡议zookeeper 版本在3.5以上,貌似Curator对3.5以下版本的兼容性不是很好,之前我用的是3.4版本 间接报错。
3我的项目代码
3.1 服务端启动类代码:
@RpcScan(basePackage = {"github.javaguide"})public class NettyServerMain { public static void main(String[] args) { // Register service via annotation // 通过正文 注册服务 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(NettyServerMain.class); NettyRpcServer nettyRpcServer = (NettyRpcServer) applicationContext.getBean("nettyRpcServer"); // 能够不必手动注册服务 // Register service manually //手动注册服务/* HelloService helloService2 = new HelloServiceImpl2(); //理解@lomlock注解的用法 RpcServiceProperties rpcServiceProperties = RpcServiceProperties.builder() .group("test2").version("version2").build(); nettyRpcServer.registerService(helloService2, rpcServiceProperties);*/ nettyRpcServer.start(); }}
3.2 客户端启动代码:
package github.javaguide;import github.javaguide.annotation.RpcScan;import org.springframework.context.annotation.AnnotationConfigApplicationContext;/** * @author shuang.kou * @createTime 2020年05月10日 07:25:00 */@RpcScan(basePackage = {"github.javaguide"})public class NettyClientMain { public static void main(String[] args) throws InterruptedException { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(NettyClientMain.class); HelloController helloController = (HelloController) applicationContext.getBean("helloController"); helloController.test(); }}
3.3 代码剖析:
3.3.1 剖析相干的“SPi类”等 注解式 注入是怎么实现的 ,服务是如何注册和发现的
察看两端代码有两处公共的局部,一是类下面都有个@RpcScan的注解,二是都用到了
Spring的注解式 注入 的形式new AnnotationConfigApplicationContext(Class);
上面追随AnnotationConfigApplicationContext的源码,找到服务是怎么被注册的?
这就在AnnotationConfigApplicationContext类外面了,如下图
调用invokeBeanFactoryPostProcessors办法前的 beanFactory
调用 invokeBeanFactoryPostProcessors办法,次要作用实例化并调用所有已注册的 BeanFactoryPostProcessor. 这办法外面会调用CustomScannerRegistrar的相干办法,进行注解式注入并生成BeanDefinition,并将其追加到beanFactory容器中,如果你不信,能够试着把registerBeanDefinitions办法的内容置空,看看调用完invokeBeanFactoryPostProcessors办法的中beanFactory中的BeanDefinition内容,上面是注解式生成了相干的BeanDefinition的截图:
在刚开始的beanDefinition外面 只有一个 nettyServerMain 主类
ConfigurationClassParser类 doProcessConfigurationClass办法
processImports 外面有一行代码
ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry);
他须要实现的接口为 ImportBeanDefinitionRegistrar
而后执行了 invokeAwareMethods办法:(意义是调用“Aware接口”的办法)
阐明该类须要实现 父接口是Aware的接口
ConfigurationClassBeanDefinitionReader类外面的办法:loadBeanDefinitionsForConfigurationClass
调用 registerBeanDefinitions办法
从下面能够看到 首先会CustomScannerRegistrar 会顺次调用setResourceLoader办法和 registerBeanDefinitions办法,setResourceLoader办法是获取到ResourceLoader,registerBeanDefinitions办法是注入了 应用了RpcService和Component的接口/类 通通被主动注入了
SpringBeanPostProcessor 实现BeanPostProcessor接口
SpringBeanPostProcessor办法外面 手动注入了这两个成员:
ServiceProvider serviceProvider;
RpcRequestTransport rpcClient;
postProcessBeforeInitialization办法:
获取了version和group,服务提供者上线==》serviceProvider.publishService(bean, rpcServiceProperties),并把服务链 进行存入缓存(Map,能够是this.getServiceName() + this.getGroup() + this.getVersion())
如上图 this.registerBeanPostProcessors(beanFactory);调用这个办法是生成所有的BeanPostProcessor,如我的项目中实现BeanPostProcessor的类SpringBeanPostProcessor外面两个办法
postProcessBeforeInitialization 和 postProcessAfterInitialization 就会被先后调用。
SpringBeanPostProcessor的结构器后被调用,因为被注入了
这个办法 次要是创立单例的 服务提供者 和创立 RpcRequestTransport(作用是发送申请)
postProcessBeforeInitialization办法
在 ExtensionLoader的类办法外面 作者应用SPI的形式(不太了解这块的同学 能够网上搜一下),地位惟一resources 的META-INF上面 文件名为接口的全门路 外面为key和对应的接口实现,【key的不同 代表接口的实现不同而已】
应用了 SPI的接口别离为 RpcRequestTransport 、ServiceRegistry、ServiceProvider、LoadBalance 。
所有的“SPI类”经此都实例化了。
//SPI类实例化程序 ZkServiceRegistry 》 NettyRpcClient 》ZkServiceDiscovery 》 ConsistentHashLoadBalance
// 别离是 zookeeper注册器 NettyRpcClient zk服务发现 负载均衡器
例如 RpcRequestTransport serviceDiscovery
postProcessBeforeInitialization 办法的作用 其实 就是生产者提供服务接口,满足带有注解RpcService的”生产者”,这外面就是保留了所有注册服务【服务链】,并把其注册到zookeeper下来(实际操作是在zookeeper上增加节点,父节点为my-rpc代表是rpc服务,二级节点是服务地址蕴含group和version ,三级节点是服务主机和端口号)。
postProcessAfterInitialization办法
postProcessAfterInitialization 这个办法在这里就是 取获取援用RpcReference的字段 ,并把通过JDK动静代理生成该对象,并把代理对象赋予给该字段。前面调用时,间接走我的项目中的RpcClientProxy类,因为该实现了InvocationHandler接口,最终也是 依据下面提到的postProcessBeforeInitialization办法 的serviceProvider 取出对应的接口的实现类, 这个依据需要 整个我的项目也只有充当“消费者”的 example-client中才应用了,这也更明确了这方是调用方/消费者了。
最初文章若有有余,请斧正。
欢送关注我的公众号:程序员ken,程序之路,让咱们一起摸索,共同进步。