Dubbo/Dubbox的服务消费(一)- 服务代理的创建

来源:互联网 发布:java自带的观察者模式 编辑:程序博客网 时间:2024/05/20 05:57

dubbo的consumer的初始过程

一个常见的consumer配置是这样的

<dubbo:reference id="dubboDemo" interface="com.company.dsp.adcenter.protocol.dubbo.DubboDemo"                 protocol="dubbo"/>

这样spring会创建一个beanName为dubboDemo的bean,使用上是这样的

@Resource(name = "dubboDemo")private DubboDemo dubboDemo;

那么问题来了why why why? dubbo是怎么创建这样的bean的?全程都没有要求我们使用bean标签,也没有要求使用Componment和Service注解,即使使用显示的方式声明一个Bean,那我们也没有相关的实现类啊。

既然我们已经拥有上面的配置项,拍脑袋一想,已知dubbo启动时会利用spring对自定义配置项的spring.handles文件加载对应的handler解析自定义节点,且上文书已经分析
dubbo:reference节点的配置项存储在com.alibaba.dubbo.config.spring.ReferenceBean 中,该类的uml图如下。
这里写图片描述
可见该类集成自com.alibaba.dubbo.config.ReferenceConfig 类,并实现了下面四个接口

org.springframework.beans.factory.FactoryBeanorg.springframework.context.ApplicationContextAwareorg.springframework.beans.factory.InitializingBeanorg.springframework.beans.factory.DisposableBean

InitializingBean和ApplicationContextAware我们都很熟悉了,还没详细了解过FactoryBean,不过这里有一个道友的文章可以看看《FactoryBean的实现原理与作用》
其中提到了以下两句

OK,那么这个时候我们getBean(“personFactory”)得到的就是Person对象而不是PersonFactoryBean对象。具体原理参考上面在IOC的应用,我们通过bean = getObjectForBeanInstance(sharedInstance, name, beanName, null)这个方法,具体调用到了getObject方法,所以结果很明显。
通过上面的小案例的代码,我们可以看到如果一个类实现了FactoryBean接口,那么getBean得到的不是他本身了,而是它所产生的对象,如果我们希望得到它本身,只需要加上&符号即可。至于FactoryBean的实际应用,需要大家去发现理解,后面如果有机会会继续聊聊这个东西。

那把目光转向:com.alibaba.dubbo.config.spring.ReferenceBean,开启找寻getObject方法之旅

/**    * ReferenceFactoryBean    *    * @author william.liangf    * @export    */    public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {        //......        public Object getObject() throws Exception {        return get();        }        //......    }

其中get()方法 继承自com.alibaba.dubbo.config.ReferenceConfig

 public synchronized T get() {        if (destroyed){            throw new IllegalStateException("Already destroyed!");        }        if (ref == null) {            init();        }        return ref;    }

可见get方法内部调用了一个init()方法

private void init() {        if (initialized) {            return;        }        initialized = true; //初始化标记,防止重复创建对象        if (interfaceName == null || interfaceName.length() == 0) {            throw new IllegalStateException("<dubbo:reference interface=\"\" /> interface not allow null!");        }        // 获取消费者全局配置,检查consumer是否有配置,如果该属性为null,则new一个ConsumerConfig对象        // 方法内部调用appendProperties(consumer)方法,该方法内部会拼装一个 dubbo.tagName.属性名的key,在配置文件中查找值,如果有值则调用属性的setter方法,设置属性值。        checkDefault();        // 上边注释已经说明appendProperties方法用途        appendProperties(this);        if (getGeneric() == null && getConsumer() != null) { //判断是不是泛化调用            setGeneric(getConsumer().getGeneric());        }        if (ProtocolUtils.isGeneric(getGeneric())) {            interfaceClass = GenericService.class;        } else {            try {                interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()                .getContextClassLoader());            } catch (ClassNotFoundException e) {                throw new IllegalStateException(e.getMessage(), e);            }            //继承自com.alibaba.dubbo.config.AbstractInterfaceConfig            //检查接口类中是否存在指定的方法,如果dubbo:service->dubbo:method 没有配置的情况下,methods为null,该方法不会执行方法校验。            //如果有相关的配置,该方法会检查name属性对应的方法是否存在,不存在会抛IllegalStateException异常。            checkInterfaceAndMethods(interfaceClass, methods);        }        //用-Ddubbo.resolve.file指定映射文件路径,此配置优先级高于<dubbo:reference>中的配置,1.0.15及以上版本支持        //......省略-Ddubbo.resolve.file的解析代码行......//        //........省略进一步初始化相关配置的代码行 .......//        //检查应用配置,如果没有配置会创建默认对象。其内部会调用appendProperties(AbstractConfig config) 填充属性。        //检查失败会抛出IllegalStateException异常        checkApplication();        //        checkStubAndMock(interfaceClass);        //看到Map就应当想到dubbo又要初始化一个URL对象了        Map<String, String> map = new HashMap<String, String>();        Map<Object, Object> attributes = new HashMap<Object, Object>();        map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);        map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());        map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));        if (ConfigUtils.getPid() > 0) {            map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));        }        if (! isGeneric()) { //非泛化调用            String revision = Version.getVersion(interfaceClass, version);            if (revision != null && revision.length() > 0) {                map.put("revision", revision);            }            String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();            if(methods.length == 0) {                logger.warn("NO method found in service interface " + interfaceClass.getName());                map.put("methods", Constants.ANY_VALUE);            }            else {                map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));            }        }        map.put(Constants.INTERFACE_KEY, interfaceName);        appendParameters(map, application);        appendParameters(map, module);        appendParameters(map, consumer, Constants.DEFAULT_KEY);        appendParameters(map, this);        String prifix = StringUtils.getServiceKey(map);            //如果指定了接口暴露哪些方法时才有效        if (methods != null && methods.size() > 0) {            for (MethodConfig method : methods) {                appendParameters(map, method, method.getName());                String retryKey = method.getName() + ".retry";                if (map.containsKey(retryKey)) {                    String retryValue = map.remove(retryKey);                    if ("false".equals(retryValue)) {                        map.put(method.getName() + ".retries", "0");                    }                }                appendAttributes(attributes, method, prifix + "." + method.getName());                checkAndConvertImplicitConfig(method, map, attributes);            }        }        //attributes通过系统context进行存储.        StaticContext.getSystemContext().putAll(attributes);        //创建当前服务的消费代理(十分重要)        ref = createProxy(map);    }
@SuppressWarnings({ "unchecked", "rawtypes", "deprecation" })            private T createProxy(Map<String, String> map) {                URL tmpUrl = new URL("temp", "localhost", 0, map);                final boolean isJvmRefer;                if (isInjvm() == null) {                    if (url != null && url.length() > 0) { //指定URL的情况下,不做本地引用                        isJvmRefer = false;                    } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {                        //默认情况下如果本地有服务暴露,则引用本地服务.                        isJvmRefer = true;                    } else {                        isJvmRefer = false;                    }                } else {                    isJvmRefer = isInjvm().booleanValue();                }                if (isJvmRefer) {                    URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);                    invoker = refprotocol.refer(interfaceClass, url);                    if (logger.isInfoEnabled()) {                        logger.info("Using injvm service " + interfaceClass.getName());                    }                } else {                    if (url != null && url.length() > 0) { // 用户指定URL,指定的URL可能是对点对直连地址,也可能是注册中心URL                        //......省略点对点直连的代码行......//                    } else { // 通过注册中心配置拼装URL                        //该方法继承自com.alibaba.dubbo.config.AbstractInterfaceConfig                        //在http://blog.csdn.net/weiythi/article/details/78467094 有相关介绍                        List<URL> us = loadRegistries(false);                        if (us != null && us.size() > 0) { //遍历注册中心地址                            for (URL u : us) {                                URL monitorUrl = loadMonitor(u);                                if (monitorUrl != null) {                                    map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));                                }                                urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));                            }                        }                        if (urls == null || urls.size() == 0) {                            throw new IllegalStateException("No such any registry to reference " + interfaceName  + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");                        }                    }                    //以下的代码反映一个主题,即创建一个invoker实例(com.alibaba.dubbo.rpc.Invoker)                    if (urls.size() == 1) {                        //这里refprotocol又是一个自适应的扩展点                        invoker = refprotocol.refer(interfaceClass, urls.get(0));                    } else {                        List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();                        URL registryURL = null;                        for (URL url : urls) {                            //注意这里refprotocol是一个自适应扩展点,根据url的protocol判断,这里返回的是一个ProtocolListenerWrapper包装类实例,并包装com.alibaba.dubbo.registry.integration.RegistryProtocol,refer方法涉及服务发现。独立章节说明                            invokers.add(refprotocol.refer(interfaceClass, url));                            if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {                                 registryURL = url; // 用了最后一个registry url                            }                        }                        if (registryURL != null) { // 有 注册中心协议的URL                            // 对有注册中心的Cluster 只用 AvailableCluster                            URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);                            invoker = cluster.join(new StaticDirectory(u, invokers));                        }  else { // 不是 注册中心的URL                            invoker = cluster.join(new StaticDirectory(invokers));                        }                    }                }                Boolean c = check;                if (c == null && consumer != null) {                    c = consumer.isCheck();                }                if (c == null) {                    c = true; // default true                }                if (c && ! invoker.isAvailable()) {                    throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());                }                if (logger.isInfoEnabled()) {                    logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());                }                // 创建服务代理,                return (T) proxyFactory.getProxy(invoker);            }

注意这里refprotocol是一个自适应扩展点,根据url的protocol判断,这里返回的是一个ProtocolListenerWrapper包装类实例,并包装com.alibaba.dubbo.registry.integration.RegistryProtocol,refer方法涉及服务发现。有时间独立章节说明
createProxy方法的最后一行return (T) proxyFactory.getProxy(invoker); 那么这个proxyFactory又是啥?

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

好吧,他又是一个自适应的扩展点,看一下ProxyFactory的接口定义,结合我们对dubbo中扩展点机制的掌握,看一看这个proxyFactory创建了一个怎样的代理 。com.alibaba.dubbo.rpc.ProxyFactory类的定义如下

                package com.alibaba.dubbo.rpc;                import com.alibaba.dubbo.common.Constants;                import com.alibaba.dubbo.common.URL;                import com.alibaba.dubbo.common.extension.Adaptive;                import com.alibaba.dubbo.common.extension.SPI;                /**                 * ProxyFactory. (API/SPI, Singleton, ThreadSafe)                 *                 * @author william.liangf                 */                @SPI("javassist")                public interface ProxyFactory {                    /**                     * create proxy.                     *                     * @param invoker                     * @return proxy                     */                    @Adaptive({Constants.PROXY_KEY})                    <T> T getProxy(Invoker<T> invoker) throws RpcException;                    /**                     * create invoker.                     *                     * @param <T>                     * @param proxy                     * @param type                     * @param url                     * @return invoker                     */                    @Adaptive({Constants.PROXY_KEY})                    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;                }

Constants.PROXY_KEY 常量的值为proxy,在\META-INF\dubbo\internal\com.alibaba.dubbo.rpc.ProxyFactory文件中找到扩展点的配置如下:

stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper                jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory                javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory

其中@SPI注解的值为javassist 所以使用com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory 类作为扩展点的实现,但是这里还有com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper包装类

所以这里会使用StubProxyFactoryWrapper实例包装JavassistProxyFactory,调用getProxy(invoker)的化学反应;方法会调用继承自com.alibaba.dubbo.rpc.proxy.AbstractProxyFactory 的 getProxy(invoker); 方法,其内部实现如下

public <T> T getProxy(Invoker<T> invoker) throws RpcException {        Class<?>[] interfaces = null;        //config 一般配置下这里会返回 null        String config = invoker.getUrl().getParameter("interfaces");        if (config != null && config.length() > 0) {            String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);            if (types != null && types.length > 0) {                interfaces = new Class<?>[types.length + 2];                interfaces[0] = invoker.getInterface();                interfaces[1] = EchoService.class;                for (int i = 0; i < types.length; i ++) {                    interfaces[i + 1] = ReflectUtils.forName(types[i]);                }            }        }        if (interfaces == null) {            interfaces = new Class<?>[] {invoker.getInterface(), EchoService.class};        }        return getProxy(invoker, interfaces);    }

可见dubbo 加进来了一个奇怪的接口 com.alibaba.dubbo.rpc.service.EchoService,这里先暂时不管这个EchoService
看一下 getProxy(invoker, interfaces);的实现

@SuppressWarnings("unchecked")    public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));    }

com.alibaba.dubbo.common.bytecode.Proxy 是一个生成代理对象的工具类
1. 遍历所有入参接口,以;分割连接起来, 以它为key以map为缓存查找如果有,说明代理对象已创建返回
2. 利用AtomicLong对象自增获取一个long数组来作为生产类的后缀,防止冲突
3. 遍历接口获取所有定义的方法,加入到一个集合Set worked中 ,用来判重,
Proxy.getProxy(interfaces),会返回一个代理对象,该方法内部操作复杂,会返回一个如下所示的代理类

package com.alibaba.dubbo.common.bytecode;import java.lang.reflect.InvocationHandler;public class Proxy0 extends com.alibaba.dubbo.common.bytecode.Proxy {    @Override    public Object newInstance(InvocationHandler h) {        return new proxy0(h);    }}class proxy0 implements 服务接口,com.alibaba.dubbo.rpc.service.EchoService{    /**     * 被代理的方法     */    public static java.lang.reflect.Method[] methods;    private java.lang.reflect.InvocationHandler handler;    public proxy0() {    }    /**     * 构造函数     *     * @param handler     */    public proxy0(java.lang.reflect.InvocationHandler handler) {        this.handler = handler;    }    /**     * 被代理方法(有返回值,有参数)     *     * @return     */    com.company.project.common.dto.Result methodName0(com.company.project.module.protocol.request.AdGroupRequest adGroupRequest) throws Throwable {        //args数组长度为参数列表长度        Object[] args = new Object[1];        args[0] = adGroupRequest;        Object ret = handler.invoke(this, methods[0], args);        return (com.ksyun.dsp.common.dto.Result) ret;    }    /**     * 被代理方法(无返回值,有参数)     *     * @return     */    void methodName1(com.company.project.module.protocol.request.AdGroupRequest adGroupRequest) throws Throwable {        Object[] args = new Object[1];        args[0] = adGroupRequest;        Object ret = handler.invoke(this, methods[1], args);    }    /**     * 被代理方法(无返回值,无参数)     *     * @return     */    void methodName2() throws Throwable {        Object[] args = new Object[0];        Object ret = handler.invoke(this, methods[2], args);    }}

从以上示例代码可见,剩下的操作都是通过
handler.invoke(this, method, args); 这种方式来实现代理方法的调用的
dubbo内部对InvocationHandler的实现如下,可见其还是实现java.lang.reflect.InvocationHandler这个接口的,但是以dubbo的代理生成方式来说,
好像没什么必要实现InvocationHandler接口,这里可能是为了维护动态代理的语义吧

/** * InvokerHandler * * @author william.liangf */public class InvokerInvocationHandler implements InvocationHandler {    private final Invoker<?> invoker;    public InvokerInvocationHandler(Invoker<?> handler){        this.invoker = handler;    }    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        String methodName = method.getName();        Class<?>[] parameterTypes = method.getParameterTypes();        if (method.getDeclaringClass() == Object.class) {            return method.invoke(invoker, args);        }        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]);        }        return invoker.invoke(new RpcInvocation(method, args)).recreate();    }}

最后一行 return invoker.invoke(new RpcInvocation(method, args)).recreate();,为便于理解拆分为以下两行

      Result rpcResult = invoker.invoke(new RpcInvocation(method, args));       return rpcResult.recreate();

其中RpcInvocation如图
这里写图片描述

总结:
1.dubbo通过实现org.springframework.beans.factory.FactoryBean接口的方式实现对服务的代理
2.Dubbo将所有的服务都实现了一个EchoService(回声测试用于检测服务是否可用,回声测试按照正常请求流程执行,能够测试整个调用是否畅通,可用于监控。所有服务自动实现EchoService接口,只需要将任意服务引用强制转换为EchoService,即可使用。)
3.服务消费最终会调用com.alibaba.dubbo.rpc.Invoker.invoke(new RpcInvocation(method, args)); 方法