Dubbo生成消费方接口代理对象

By ref-nobody 创建时间 2025年4月13日 | 本文最后更新于 2025年5月13日 #dubbo, #service discovery

消费者端接口引用

从消费者端来进行分析,定义消费者的接口的时候可以定义返回ReferenceBean的@Bean:

@Bean
@DubboReference(group = "demo", filter = "-mytest,refFilter1")
public ReferenceBean<DemoService> referenceBean() {
  HashMap<String, Object> props = new HashMap<>();
  // ...
  ReferenceBean<DemoService> referenceBean = new ReferenceBean<>(props);
  return referenceBean;
}

实际上ReferenceBean是一个spring的factory bean,实现了FactoryBean接口,因此在加载到spring容器的时候,会调用factory bean的getObejct方法。在getObject方法中,dubbo会调用createLazyProxy方法,使用javassist或者jdk生成代理对象,这个代理对象就是消费方订阅的接口的真实对象。

无论是使用javassist还是jdk动态代理生成的代理对象,都是使用相同的InvocationHandler的子类:LazyTargetInvocationHandler,传递的真实对象为DubboReferenceLazyInitTargetSource

jdk:
this.lazyProxy = java.lang.reflect.Proxy.newProxyInstance(
          beanClassLoader,
          interfaces.toArray(new Class[0]),
          new LazyTargetInvocationHandler(new DubboReferenceLazyInitTargetSource()));

javassist:
this.lazyProxy = Proxy.getProxy(interfaces.toArray(new Class[0])).newInstance(new LazyTargetInvocationHandler(new DubboReferenceLazyInitTargetSource()));

消费方接口调用流程

当消费方接口进行接口调用的时候,根据动态代理的机制会将方法的调用代理到InvocationHandlerinvoke方法,执行method.invoke(target, args)来执行真实的调用。这里的target在透明的情况下,可以理解为就是服务提供方。

getTarget步骤获取服务提供方代理

从调用层面来看服务提供方的代理对象是在消费方第一次执行方法调用的时候才会生成。
target是dubbo在获取到服务提供方在注册中心注册的实例之后,最终经过层层封装生成的代理对象,消费方可以使用这个对象进行透明调用,像调用本地方法一样调用远程服务提供者的接口,不需要考虑底层的协议通讯的细节。
消费方调用接口的时候首先会延时生成服务提供方的代理对象,执行的流程如下图:

在执行方法调用的时候,首先会通过动态代理机制调用到DubboReferenceLazyInitTargetSource类的getTarget方法,DubboReferenceLazyInitTargetSource类是ReferenceBean的一个内部类,在ReferenceBean被spring 容器加载的时候,用来生成接口引用的动态代理对象。
随后首先会生成消费方订阅的接口所对应的ReferenceConfig对象,调用get方法执行主要的逻辑,主要分为两部分:
1. 获取invoker;
2. 对invoker进行代理,进行调用参数的构造和封装如何执行invoker调用的逻辑;

获取invoker

通过调用链路可以看到invoker的获取是通过Protocol的refer方法获得。Protocol接口是dubbo中的一个拓展点,先来看看Protocol的spi文件中的内容:

filter=org.apache.dubbo.rpc.cluster.filter.ProtocolFilterWrapper
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper
registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol
service-discovery-registry=org.apache.dubbo.registry.integration.RegistryProtocol
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
serializationwrapper=org.apache.dubbo.rpc.protocol.ProtocolSerializationWrapper
securitywrapper=org.apache.dubbo.rpc.protocol.ProtocolSecurityWrapper
invokercount=org.apache.dubbo.rpc.protocol.InvokerCountWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol

dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
tri=org.apache.dubbo.rpc.protocol.tri.TripleProtocol
grpc=org.apache.dubbo.rpc.protocol.tri.GrpcProtocol
rest2=org.apache.dubbo.rpc.protocol.tri.RestProtocol

文中定义了Protocol接口的多种实现和多个以Wrapper结尾的包装类。在使用注册中心的情况下,此时获取到的实现类为org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol,但是在调试的时候看到的是层层嵌套的Wrapper,这是因为spi在加载的时候自动用Wrapper进行了包装。
下面来看详细的调用流程:

首先会通过RegistryFactory根据url获得到对应的Registry实例,Registry表示的就是我们使用的注册中心,如果使用的是Nacos注册中心则实例为NacosRegistry。
向注册中心订阅服务列表创建invoker是在RegistryProtocolListener完成的。dubbo提供了RegistryProtocolListener的默认实现MigrationRuleListener,除此之外在这一步我们也可以自定义实现,来干预消费者refer的过程。
dubbo通过RegistryDirectory来订阅服务,通过调用链路可以看到最终会调用到registry的doSubscribe方法。

对invoker进行代理

方法的最后返回的是invoker的代理对象,而代理的真实对象和之前的lazyProxy有所区别,这里代理的真实对象为new InvokerInvocationHandler(invoker)在这里需要返回代理是因为这个代理中是真正执行invoker调用的地方,包括构造调用参数,和执行invoker的调用

在dubbo中使用ReferenceBeanManager来创建和维护所有ReferenceConfig实例,
根据调用过程可以总结出:
1. 生产者的接口代理是在消费方首次调用接口的时候才会生成;
2. 每一个ReferenceBean都会对应一个ReferenceConfig对象,并且在ReferenceBean中进行维护;
3. 在ReferenceBean中使用ref字段保存创建的消费方接口代理;

Leave a Reply

Your email address will not be published. Required fields are marked *

目录