dubbo服务引用流程大致如下:
(1)首先在dubbo容器启动的时候,会扫描所有的reference配置(也就是dubbo客户端配置的远程引用),生成对应ReferenceBean,例如:客户端的DemoClient类依赖了远程服务DemoService,那么dubbo就会生成一个ReferenceBean<DemoService>
(2)spring容器在初始化本地的bean的时候,发现DemoClient类依赖了DemoService类,于是spring会寻找DemoService的工厂类,结果就找到了ReferenceBean<DemoService>
(3)spring会调用ReferenceBean的getObject方法,以获得一个DemoService实例,ReferenceBean的getObject方法会调用调用ReferenceConfig的createProxy()方法,构建Invoker 实例以及远程服务的代理对象,然后返回这个对象
(4)然后客户端就可以通过这个代理对象执行的对远程服务的调用了
从上面的流程中我们可以看出createProxy()方法是一个关键,接下来我们就来看看这个方法执行了什么操作吧
(1)首先就是我们上面提到的创建Invoker的流程了,主要流程如下所示:
- 按照惯例,在进行具体工作之前,需先进行配置检查与收集工作
- 接着根据收集到的信息决定服务引用的方式,有三种:
本地 (JVM) 服务引用、直连方式引用远程服务、注册中心引用远程服务
- 不管是哪种引用方式,最后都会得到一个 Invoker 实例
- Invoker拥有调用本地或远程服务的能力(通过组合protocol生成的ExchangeClient对象获得远程通信的能力)
(2)创建了Invoker之后,ReferenceConfig会调用ProxyFactory类的getProxy()方法,会通过调用Dubbo的Proxy类而不是java的Proxy类,创建一个被动态代理的远程服务的代理对象(关键就在于这个proxy)
(3)客户端调用代理对象的时候,会被InvokerInvocationHandler动态代理拦截到,InvokerInvocationHandler中的invoke方法会执调用Invoker的invoke()方法
(4)我们知道Invoker拥有调用本地或远程服务的能力,其实就是如通过在invoke()方法里面,调用ExchangeClient进行网络通信
dubbo获得远程服务代理对象时序图:
流程其他关键点:
(1)Dubbo 服务引用的时机
上面为了简化流程我们没有提到,实际上Dubbo 服务引用的时机有两个:
- 第一个是在 Spring 容器调用 ReferenceBean 的 afterPropertiesSet 方法时引用服务(这也是bean的生命周期中的方法,其会在对应bean的所有属性都初始化完成之后被调用)
- 第二个是在 ReferenceBean 对应的服务被注入到其他类中时引用(ReferenceBean 实现了FactoryBean,因此会被spring当做一个生产bean的工厂)(依赖注入,并不是在运行的时候,如果是被单例bean引用,在springIOC容器初始化之后,执行以来注入的时候就会被引用)
这两个引用服务的时机区别在于,第一个是饿汉式的,第二个是懒汉式的。默认情况下,Dubbo 使用懒汉式引用服务。如果需要使用饿汉式,可通过配置 <dubbo:reference> 的 init 属性开启
(2)多个注册中心的invoker
如果有多个注册中心,此时需要通过集群管理类 Cluster 将多个 Invoker 合并成一个实例。合并后的 Invoker 实例同样具备调用本地或远程服务的能力
(3)动态代理的实现机制
dubbo通过代理工厂类 (ProxyFactory) 为服务接口生成代理类Proxy,默认是使用Javassist生成的Proxy
Invoker
Invoker 是 Dubbo 的核心模型,代表一个可执行体。在服务提供方,Invoker 用于调用服务提供类。在服务消费方,Invoker 用于执行远程调用。Invoker 是由 Protocol 实现类构建而来(也就是说或是不同的协议提供不同的Invoker,从中我们可以嗅到什么味道?我们知道协议不同,底层通信机制不同,Invoker下面应该也有对应的通信机制)
果然DubboInvoker中组合了ExchangeClient,但是ExchangeClient其实并不具备通信能力,它需要基于更底层的客户端实例进行通信。比如 NettyClient、MinaClient 等,默认情况下,Dubbo 使用 NettyClient 进行通信
ExchangeClient 客户端是 Exchangers 的 connect 方法被调用的时候创建的,会通过 SPI 机制加载指定的 HeaderExchangeClient 实例,默认是HeaderExchangeClient,
HeaderExchangeClient需要使用底层通信机制就需要,组合dubbo的transport层的对象,假如我们是使用的dubbo协议暴露服务,那么Transporters就会通过SPI机制返回一个NettyTransporter,在往下就是通过 Netty 提供的 API 构建 Netty 客户端了
Invoker的封装
- Invoker 是 Provider 的一个可调用 Service 的抽象, Invoker 封装了 Provider 地址及 Service 接口信息
- Directory 维护服务列表,每个服务被封装成一个 Invoker ,可以把它看成 List<Invoker> ,但与 List 不同的是,它的值可能是动态变化的,比如注册中心推送变更
- Cluster 将 Directory 中的多个 Invoker 伪装成一个 Invoker ,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个
- Router 负责从多个 Invoker 中按路由规则选出子集,比如读写分离,应用隔离等
- LoadBalance 负责从多个 Invoker 中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选