共计 4850 个字符,预计需要花费 13 分钟才能阅读完成。
前言:之前看到了 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,程序之路,让咱们一起摸索,共同进步。