dubbo-消费者代理对象创建

916 阅读3分钟

背景

前面文章分析了dubbo服务端的一些逻辑,这篇文章开始我们分析消费端侧的知识,消费端较生产端略复杂些,不过前面跟上了后面更容易理解。

重点

  • 消费端Invoker对象的创建和包装
  • 如何将Invoker对象和客户端引用的bean对象结合

分析

Spring 入口

消费者的初始化入口还是spring的bean后置处理,我们回顾下消费端的使用

<dubbo:reference id="helloService" interface="com.xx.HelloService" />

public class HelloController {

    @Resource
    private HelloService helloService;

dubbo 客户端对于服务端接口的引用也托管到了spring容器中,通过 ReferenceBean 类注册为bean。

public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean {
    @Override
    public Object getObject() { return get();}
    public synchronized T get() {
        checkAndUpdateSubConfigs();
        if (ref == null) {
            init();
        }
        return ref;
    }
}

改类实现了 FactoryBean 具有个性化的bean创建逻辑,也具有spring 生命周期的能力。

Invoker 对象创建

进到init() 中

org.apache.dubbo.config.ReferenceConfig#init
private void init() {
		//...
        ref = createProxy(map);
        //....
}
org.apache.dubbo.config.ReferenceConfig#createProxy
private T createProxy(Map<String, String> map) {
	//...
   invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
   return (T) PROXY_FACTORY.getProxy(invoker);
}  
private static Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

invoker 通过 Protocol自适应扩展点 得到,urls.get(0) 得到的是注册中心的地址(registry),我们继续往下。

org.apache.dubbo.registry.integration.RegistryProtocol#refer
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        url = URLBuilder.from(url)
                .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
                .removeParameter(REGISTRY_KEY)
                .build();
        Registry registry = registryFactory.getRegistry(url);//1
        return doRefer(cluster, registry, type, url);
    }

这里多了一个概念 “Registry” 注册中心,RegistryFactory 会根据配置类型找到匹配的注册中心,我们这里用的是zookeeper(ZookeeperRegistry),看看doRefer()。

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        // all attributes of REFER_KEY
        Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
        URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
        if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
            directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
            registry.register(directory.getRegisteredConsumerUrl());
        }
        directory.buildRouterChain(subscribeUrl);
        directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
                PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));

        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }

终于出现invoker了,但是这边又出现了新概念“directory”目录、“cluster” 集群,directory的作用是从多个生产者中选择出一个最合适的作为调用目标地址,cluster主要是作为调用失败之后的容错策略,cluster也是自适应扩展点,默认扩展点为"FailoverCluster失败重试"(当然其中还会包装wrapper扩展)。我们进入cluster.join()看看。

org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper#join
public class MockClusterWrapper implements Cluster {
@Override
public <T> Invoker<T> join(Directory<T> directory) {
return new MockClusterInvoker<T>(directory,this.cluster.join(directory));
}
org.apache.dubbo.rpc.cluster.support.FailoverCluster#join
public class FailoverCluster implements Cluster {
    public final static String NAME = "failover";
    @Override
    public <T> Invoker<T> join(Directory<T> directory) {
        return new FailoverClusterInvoker<T>(directory);
}

所以invoker最后被包装为

new MockClusterInvoker(directory, new FailoverClusterInvoker(directory));

代理对象创建

我们继续回到上面的org.apache.dubbo.config.ReferenceConfig#createProxy 方法中 invoker 的值我们已经分析出结果,下面看 PROXY_FACTORY 有看过我另一篇服务暴露文章的应该了解这也是一个ProxyFactory 自适应扩展点。默认实现为 javassist

private static ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

public class JavassistProxyFactory extends AbstractProxyFactory {
    @Override
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }

这个Proxy要留意,并不是jdk中的,而是 org.apache.dubbo.common.bytecode.Proxy,我们debug下看一眼生成的代理类。

org.apache.dubbo.common.bytecode.Proxy#getProxy
public static Proxy getProxy(Class<?>... ics) {
    return getProxy(ClassUtils.getClassLoader(Proxy.class), ics);
}

图中可以看到,getProxy方法中把api接口中所有的方法都生成了一个新的方法体,然后通过反射的方式调用。注意这里和生产者不同,生产者是通过if else进行判断调用的

//重新生成的方法
Object[] args = new Object[1]; 
args[0] = ($w)$1; 
Object ret = handler.invoke(this, methods[0], args); //InvokerInvocationHandler 直接调用
return ret;

反射回调的类是从getProxy 方法传入 InvokerInvocationHandler 这个类实现了java的 InvocationHandler new InvokerInvocationHandler(invoker),在创建对象的时候我们把invoker 对象也传了进去供后面调用远程使用。这样 PROXY_FACTORY.getProxy(invoker); 方法就分析完了,返回的是一个 javassist 生成的字节码对象。这个对象持有invoker远程调用对象