Dubbo consumer代理创建流程

简介: 开篇 这篇文章目的是为了将consumer在引用producer的过程中创建代理的细节。Reference创建代理过程public class ReferenceConfig<T> extends AbstractReferenceConfig { private T creat...

开篇

 这篇文章目的是为了将consumer在引用producer的过程中创建代理的细节。


Reference创建代理过程

public class ReferenceConfig<T> extends AbstractReferenceConfig {

    private T createProxy(Map<String, String> map) {
        if (shouldJvmRefer(map)) {
            URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
            invoker = REF_PROTOCOL.refer(interfaceClass, url);
        } else {
          
            // 省略部分代码
            if (urls.size() == 1) {
                invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
            } else {
                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null;
                for (URL url : urls) {
                    invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
                    if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; // use last registry url
                    }
                }
                if (registryURL != null) { // registry url is available
                    // use RegistryAwareCluster only when register's CLUSTER is available
                    URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
                    // The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
                    invoker = CLUSTER.join(new StaticDirectory(u, invokers));
                } else { // not a registry url, must be direct invoke.
                    invoker = CLUSTER.join(new StaticDirectory(invokers));
                }
            }
        }

        // 省略无关代码
     
        // 创建引用代理
        return (T) PROXY_FACTORY.getProxy(invoker);
    }
}
  • reference过程的createProxy()中执行PROXY_FACTORY.getProxy(invoker)获取代理。
  • 整个getProxy()过程如下图的调用链。

Dubbo引用过程调用链


public abstract class AbstractProxyFactory implements ProxyFactory {

    @Override
    public <T> T getProxy(Invoker<T> invoker) throws RpcException {
        return getProxy(invoker, false);
    }

    @Override
    public <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException {

        // 省略相关代码
        return getProxy(invoker, interfaces);
    }
}



public class JavassistProxyFactory extends AbstractProxyFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }
}
  • PROXY_FACTORY.getProxy()最终调用JavassistProxyFactory.getProxy()方法。
  • JavassistProxyFactory.getProxy()内部调用Proxy.getProxy()获取代理并进行实例化。
  • Proxy.getProxy()方法内部动态生成proxy0代理类,生成代码如下图。
public abstract class Proxy {

    public static Proxy getProxy(Class<?>... ics) {
        return getProxy(ClassUtils.getClassLoader(Proxy.class), ics);
    }

    public static Proxy getProxy(ClassLoader cl, Class<?>... ics) {
        if (ics.length > MAX_PROXY_COUNT) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        StringBuilder sb = new StringBuilder();
        // 省略动态生成代理的代码

        return proxy;
    }
}


/**
 * Arthas 反编译步骤:
 * 1. 启动 Arthas
 *    java -jar arthas-boot.jar
 *
 * 2. 输入编号选择进程
 *    Arthas 启动后,会打印 Java 应用进程列表,如下:
 *    [1]: 11232 org.jetbrains.jps.cmdline.Launcher
 *    [2]: 22370 org.jetbrains.jps.cmdline.Launcher
 *    [3]: 22371 com.alibaba.dubbo.demo.consumer.Consumer
 *    [4]: 22362 com.alibaba.dubbo.demo.provider.Provider
 *    [5]: 2074 org.apache.zookeeper.server.quorum.QuorumPeerMain
 * 这里输入编号 3,让 Arthas 关联到启动类为 com.....Consumer 的 Java 进程上
 *
 * 3. 由于 Demo 项目中只有一个服务接口,因此此接口的代理类类名为 proxy0,此时使用 sc 命令搜索这个类名。
 *    $ sc *.proxy0
 *    com.alibaba.dubbo.common.bytecode.proxy0
 *
 * 4. 使用 jad 命令反编译 com.alibaba.dubbo.common.bytecode.proxy0
 *    $ jad com.alibaba.dubbo.common.bytecode.proxy0
 *
 * 更多使用方法请参考 Arthas 官方文档:
 *   https://alibaba.github.io/arthas/quick-start.html
 */

public class proxy0 implements ClassGenerator.DC, EchoService, DemoService {
    public static Method[] methods;
    private InvocationHandler handler;

    public proxy0(InvocationHandler invocationHandler) {
        this.handler = invocationHandler;
    }

    public proxy0() {
    }

    public String sayHello(String string) {
        Object[] arrobject = new Object[]{string};
        Object object = this.handler.invoke(this, methods[0], arrobject);
        return (String)object;
    }

    public Object $echo(Object object) {
        Object[] arrobject = new Object[]{object};
        Object object2 = this.handler.invoke(this, methods[1], arrobject);
        return object2;
    }
}
  • 动态生成的类proxy0如上所示,构造函数参数为InvocationHandler对象。
  • proxy0的sayHello()方法为服务提供的方法,内部执行invocationHandler的调用。


public class InvokerInvocationHandler implements InvocationHandler {
    private static final Logger logger = LoggerFactory.getLogger(InvokerInvocationHandler.class);
    private final Invoker<?> invoker;

    public InvokerInvocationHandler(Invoker<?> handler) {
        this.invoker = handler;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        // 拦截定义在 Object 类中的方法(未被子类重写),比如 wait/notify
        if (method.getDeclaringClass() == Object.class) {
            return method.invoke(invoker, args);
        }
        // 如果 toString、hashCode 和 equals 等方法被子类重写了,这里也直接调用
        if ("toString".equals(methodName) && parameterTypes.length == 0) {
            return invoker.toString();
        }
        if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
            return invoker.hashCode();
        }
        if ("equals".equals(methodName) && parameterTypes.length == 1) {
            return invoker.equals(args[0]);
        }

        // 将 method 和 args 封装到 RpcInvocation 中,并执行后续的调用
        return invoker.invoke(new RpcInvocation(method, args)).recreate();
    }
}
  • InvokerInvocationHandler的实现如上,继承jdk的InvocationHandler代理接口。
  • InvokerInvocationHandler的构造函数参数为真正的引用provider服务的Invoker对象。


总结

  • createProxy()核心在于动态创建proxy0对象,
    proxy0内部包含InvokerInvocationHandler对象,

InvokerInvocationHandler对象内部包含真正的invoker对象。

  • proxy0的调用过程会按照proxy0 => InvokerInvocationHandler => invoker的调用。
目录
相关文章
|
Dubbo Java 应用服务中间件
dubbo 服务开发流程,运行流程?zookeeper 注册中心的作用?
dubbo 服务开发流程,运行流程?zookeeper 注册中心的作用?
89 0
|
XML 存储 缓存
并不简单的代理,Dubbo是如何做服务引用的
并不简单的代理,Dubbo是如何做服务引用的
137 0
|
设计模式 缓存 Dubbo
Apache ShenYu 网关正式支持 Dubbo3 服务代理
本文介绍了如何通过 Apache ShenYu 网关访问 Dubbo 服务,主要内容包括从简单示例到核心调用流程分析,并对设计原理进行了总结。
954 7
Apache ShenYu 网关正式支持 Dubbo3 服务代理
|
设计模式 Dubbo Java
dubbo生产者暴露服务流程
在这个过程中,url是我们需要进行关注的,此时我们可以看到基本上都是以url为主题进行组装操作。将所有需要放入的输入进行放入,同时最终会以观察者模式,实现配置的实时更新。低版本的dubbo则是以实现InitializingBean,重写AfterPropertiesSet方法。之所以改成基于ApplicationEvent,是因为可以进行更新,这是ApplicationEvent的优势。 同时通过对bubbo的学习,可以看到Netty的使用。
74 0
dubbo生产者暴露服务流程
|
编解码 负载均衡 监控
Dubbo调用流程学习总结
首先我们知道Dubbo是一个RPC框架,因此解决的问题是服务治理,这个治理是解决服务注册和调用列表的维护治理,产生注册中心维护服务列表和更新,同时方便远程调用和本地调用是一样的,同时方便解耦,我猜这个是dubbo框架产生的初衷吧。而服务的调用和服务的引用是采用网络编程框架Netty,由于其基于NIO,因此其具有很高的性能。同时因为服务的调用和服务的引用,与IM通信或者我们看到的Http请求三次握手是类似的,采用的是应答模式。
147 0
Dubbo调用流程学习总结
|
JSON 运维 Dubbo
Dubbo-go 服务代理模型
HSF 是阿里集团 RPC/服务治理 领域的标杆,Go 语言又因为其高并发,云原生的特性,拥有广阔的发展前景和实践场景,服务代理模型只是一种落地场景,除此之外,还有更多的应用场景值得我们在研发的过程中去探索和总结。
740 6
Dubbo-go 服务代理模型
|
Dubbo Java 应用服务中间件
|
监控 Dubbo 应用服务中间件
Dubbo 注册中心的工作流程是啥?
Dubbo 注册中心的工作流程是啥?
121 0
Dubbo 注册中心的工作流程是啥?
|
存储 Kubernetes Dubbo
深入解析 Dubbo 3.0 服务端暴露全流程
随着云原生时代的到来,Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生。正因如此,Dubbo 3.0 为了能够更好的适配云原生,将原来的接口级服务发现机制演进为应用级服务发现机制。 基于应用级服务发现机制,Dubbo 3.0 能大幅降低框架带来的额外资源消耗,大幅提升资源利用率
697 0
深入解析 Dubbo 3.0 服务端暴露全流程
|
存储 Kubernetes Dubbo
深入解析 Dubbo 3.0 服务端暴露全流程
随着云原生时代的到来,Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生。正因如此,Dubbo 3.0 为了能够更好的适配云原生,将原来的接口级服务发现机制演进为应用级服务发现机制。
深入解析 Dubbo 3.0 服务端暴露全流程