SPI机制
SPI,service provider interface,服务发现机制,其实就是把接口实现类的全限定名配置在文件里面,然后通过加载器ServiceLoader去读取配置加载实现类,比如说数据库驱动,我们把mysql的jar包放到项目的lib后,DriverManager.getConnection()
就获取到mysql的连接了呢?
其实 DriverManager的静态代码块里面有个loadInitialDrivers方法,调用的ServiceLoader.load(Driver.class)
然后迭代遍历hasNext方法再调用hasNextService,里面指定从META-INF/service目录下找接口名的文件,里面写的java.sql.Driver的mysql实现的全限定名com.mysql.cj.jdbc.Driver
参考java,dubbo也实现自己的,并进行增强,java的只能循环遍历,dubbo搞的是k=v的形式,可以按需查找
dubbo写了个ExtensionLoader,里面有几个全局映射map,Class和ExtensionLoader的映射、Class和对应实例对象的映射,根据key找到持有value的holder,然后根据holder获取实例,通过双检锁保证实例唯一。
那dubbo除了生成实例外,还有什么增强吗?
有的,dubbo也有IOC,有个injectExtension方法,是通过 setter 方法注入依赖。Dubbo 首先会通过反射获取到实例的所有方法,然后再遍历方法列表,检测方法名是否具有 setter 方法特征。若有,则通过 ObjectFactory 获取依赖对象,最后通过反射调用 setter 方法将依赖设置到目标对象中。
那除了IOC外,还有什么增强吗?
哈哈,其实也有AOP,装饰器模式,有Wrapper类,使用Wrapper类包装原始的扩展点实例。在原始扩展点实现前后插入其他逻辑,实现AOP功能。也是在createExtension方法里面,在IOC之后,做AOP,判断是否有wrapperClass,有的话,遍历,将当前实例包装到wrapper中,通过构造注入,在wrapper中注入依赖,实现增强,还能层层套,如果多个wrapper,由于instance已经变成第一个wrapper包装后的instance,会在外层再包装
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
除了AOP还有什么增强吗?
自适应,我们一般扩展点的想法是什么,比如在yaml中配置protol是dubbo,然后呢,框架启动的时候去加载对应的扩展点,那假如我们不想框架启动阶段就加载,而是希望拓展方法被调用的时候,根据运行时的参数进行加载,也就是根据参数动态加载实现类,那要怎么实现呢?
创建接口的自适应实例getAdaptiveExtensionClass()是核心
首先会使用一个StringBuilder来生成自适应类的Java源码,然后再将源码编译成Java的字节码,加载到JVM中,目前是通过javassist实现的。
dubbo的服务暴露机制和源码实现
服务暴露,其实核心就是两点:
- 开启服务
- 服务注册
怎么实现呢?
先要了解dubbo怎么传递注册信息等配置信息的,通过url,还挺巧妙的
采用 URL 作为配置信息的统一格式,所有扩展点都通过传递 URL 携带配置信息
dubbo的ServiceBean实现了ApplicationListener,方法onApplicationEvent在容器完成初始化后,调用export导出服务实现服务暴露。核心是doExportUrlsFor1Protocol方法,先各种装配配置参数信息,组装url,然后如果是远程调用,通过代理ProxyFactory转换为Invoker,Dubbo 默认的 ProxyFactory 实现类是 JavassistProxyFactory。RegistryProtocol.export。里面先protocol.export,根据协议如dubbo导出,把Invoker转换为Exporter,这里面会开启服务openServer。
服务注册呢?先根据URL加载 Registry 实现类,然后调register方法向注册中心注册服务。根据SPI机制找到 ZookeeperRegistry extends FailbackRegistry extends AbstractRegistry,register方法在 FailbackRegistry 中,真正执行注册是在ZookeeperRegistry中的doRegister方法里。
dubbo的服务发现机制和源码分析
和provider对应,有个ReferenceConfig,核心也是两点:
- 先从注册中心获取服务信息
- 创建客户端
入口在哪里呢?ReferenceBean实现了FactoryBean,真正被注入到容器中的是由getObject方法返回的对象。里面init方法主要用于处理配置,以及调用 createProxy 生成代理类。createProxy一个是构建Invoker实例。调用 RegistryProtocol 的 refer 构建 Invoker 实例。然后getProxy根据Invoker 真正生成代理,实际是子类 JavassistProxyFactory 实现 getProxy方法。
其实注册和创建客户端都是在 RegistryProtocol 的 refer 方法中实现的。先是registry.register注册到zk,然后directory.subscribe订阅,会调ZookeeperRegistry#doSubscribe,最终是调RegistryDirectory#refreshOverrideAndInvoker将providers对应url封装成 invoker,核心是new DubboInvoker,里面的getClients去initClient初始化新的客户端,内部调Exchanger调NettyClient的doOpen再doConnect打开并和对端建立连接,
dubbo的RPC协议
如图,包括header和body
- 16bit 魔术 高位和低位,总共16bit,0xdabb
- 1bit 请求响应标志 请求1 响应0
- 1bit 是否需要往返 请求的时候有用,如果需要来自服务器的返回值,值为1
- 1bit event事件,如心跳事件 值为1
- 5bit 序列化ID,标识序列化类型,如fastjson是6
- 8bit 响应状态码 20ok 30 client超时等
- 64bit requestId唯一请求 long类型 8个字节
- 32bit 消息体的data length,int类型 4个字节
- body 序列化后的body