前言
使用 Dubbo,只需要服务消费者引用服务提供者的接口jar包(泛化调用可以不用服务提供者的接口jar包),就可以像调用本地方法一样,调用远程的服务,但在消费者这边,并没有服务实现的代码,那是怎么实现调用的呢?本文就分析一下服务引用的原理。
服务引用流程
解析配置文件
根据配置文件 <dubbo:reference init=“true”> init的配置走不同的服务引用时机,最终都走到同样的路口:ReferenceBean#getObject。
服务引用的时机
服务引用使用 < dubbo:reference> 标签,按照是否配置 init=“true” 会有两种服务引用的时机:
- 配置了 <dubbo:reference init=“true”> 会在解析到这个标签的时候就进行服务引用,也就是饿汉式的方式。
- 配置了 <dubbo:reference > 并不会在解析这个标签的时候进行服务引用,而是在获取这个 bean 的时候进行服务引用,也就是懒汉式的方式。
饿汉式的服务引用原理是 ReferenceBean 标签实体类实现了 InitializingBean 接口,会在初始化 Bean 的时候就执行 afterPropertiesSet 方法,并且 afterPropertiesSet 方法会判断 init =true 是否成立,成立的话就调用 getObject 方法,getObject 方法是服务引用的入口。
而懒汉式的服务引用原理是 ReferenceBean 实现了 FactoryBean 接口,这个接口会在获取 Bean 的时候调用 getObject 方法。对比这两种服务引用的时机,最后都是调用了 FactoryBean 接口的 getObject 方法,唯一的不同就是在初始化 Bean 的时候执行 afterPropertiesSet 方法中会判断 init 是否为 true。
注册订阅
根据协议不同会走不同服务引用策略,包括 injvm、直连、注册中心。对于注册中心的服务引用,会根据 URL 中参数 protocol 走具体的注册中心(ZookeeperRegistry),然后会完成注册consumer节点,订阅provider、router、configurators节点。
订阅之后创建invoker,启动Netty连接远程服务,invoker放入Map
订阅之后对应节点的信息会通知到消费者,在 RegistryDirectory 根据 URL 引用服务,引用服务就是创建 netty 客户端并且连接远程服务提供者(lazy=false),并创建 invoker 对象(DubboInvoker),这里的 invoker 对象会持有远程服务的 NettyClient,最后把创建的 DubboInvoker 对象放入 Map 中,在后面调用的时候根据 URL/方法名从 Map 中获取对应的 DubboInvoker 对象从而发起调用。
// RegistryDirectory.toInvokers 调用具体协议发起引用
invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url),