栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

dubbo之服务本地引用

dubbo之服务本地引用

debug测试源码 。

写在前面

在dubbo之服务本地暴露 文章中我们分析了本地暴露的过程,本文一起看下对应的服务消费者端是如何引用的,即本地引用。

一般的,当使用本地引用时,xml配置如下:


    

java代码如下:

class FakeCls {
    public class MyProviderScopeLocalMain {
        public static void main(String[] args) throws Exception {
            //加载xml配置文件启动
            ClassPathXmlApplicationContext providerContext = new ClassPathXmlApplicationContext("meta-INF/spring/scopelocal/provider-scope-local.xml");
            providerContext.start();
                    ClassPathXmlApplicationContext consumerContext = new ClassPathXmlApplicationContext("meta-INF/spring/scopelocal/consumer-scope-local.xml");
            consumerContext.start();
            ScopeLocalService scopeLocalService = consumerContext.getBean("scopeLocalService", ScopeLocalService.class);
            scopeLocalService.sayHi("hello scope local!");
            System.in.read(); // 按任意键退出
        }
    }

}

其中的代码consumerContext.getBean("scopeLocalService", ScopeLocalService.class)就是获取用于本地调用服务提供者的代理类的,在dubbo中对应的bean是一个工厂bean ,通过该工厂bean获取我们最终需要的代理类,之后用来调用服务提供者,如下debug:

从图中可到最终调用到com.alibaba.dubbo.config.ReferenceConfig#createProxy,接下来我们就从这个方法来开始分析吧!

1:createProxy

源码如下:

class FakeCls {
    // com.alibaba.dubbo.config.ReferenceConfig.createProxy
    // map:配置相关信息,如
    
    private T createProxy(Map map) {
        URL tmpUrl = new URL("temp", "localhost", 0, map);
        // 是否本地引用的标记
        final boolean isJvmRefer;
        // 1:isInjvm()方法已经标记为@Deprecated
        // 2:可以认为返回值为null,即只会进if
        if (isInjvm() == null) {
            // 如果是指定了url,则说明要使用注册中心,直接将isJvmRefer设置为false,代表不是本地引用
            if (url != null && url.length() > 0) {
                isJvmRefer = false;
            // 2022-02-09 18:08:29
            } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
                isJvmRefer = true;
            } else {
                isJvmRefer = false;
            }
        } else {
            isJvmRefer = isInjvm().booleanValue();
        }
        if (isJvmRefer) {
            // 构造url,如injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?... 因为是本地调用所以是127.0.0.1
            // 本地调用为什么还需要url呢?我认为是为了和远程调用保持格式上的统一,这样做到最小的差异性,代码更加容易编写
            URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
            // 2022-02-15 10:02:41
            invoker = refprotocol.refer(interfaceClass, url);
            if (logger.isInfoEnabled()) {
                logger.info("Using injvm service " + interfaceClass.getName());
            }
        // 正常流程,一般是远程引用,本文分析本地引用,因此,该部分先不看
        } else {
            // 省略远程引用逻辑
        }
        // 服务检查
        Boolean c = check;
        if (c == null && consumer != null) {
            c = consumer.isCheck();
        }
        if (c == null) {
            c = true;
        }
        if (c && !invoker.isAvailable()) {
            initialized = false;
            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());
        }
        // 创建Service代理对象,内部会调用invoker.invoke
        return (T) proxyFactory.getProxy(invoker);
    }
}

2022-02-09 18:08:29处InjvmProtocol.getInjvmProtocol()处是获取InJvmProtocol扩展类的实例,2022-02-09 18:08:29处isInjvmRefer,是根据协议判断是否是本地引用,具体参考1.1:isInjvmRefer。2022-02-15 10:02:41处是获取调用本地服务提供者的Invoker,这里refprotocol是Protocol$Adaptive,该类为动态生成的类,源码如下:

public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public void destroy() {
        throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }

    public int getDefaultPort() {
        throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }
    
    // 获取服务引用调用的方法
    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.refer(arg0, arg1);
    }

    // 暴露服务引用调用的方法
    public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
        if (arg0.getUrl() == null)
            throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
        com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
        if (extName == null)
            throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
        com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        return extension.export(arg0);
    }
}

看其中的方法refer,因为我们的url是injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?...,所以最终调用的是Protocol扩展类是com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol,再考虑wrapper的话,调用顺序是Protocol$Adaptive => ProtocolFilterWrapper => ProtocolListenerWrapper => InjvmProtocol,具体参考2:Protocol。

1.1:isInjvmRefer

源码如下:

class FakeCls {
    // com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol.isInjvmRefer
    // url此时值:temp://localhost?...&scope=local&...
    public boolean isInjvmRefer(URL url) {
        final boolean isJvmRefer;
        // temp://localhost?...&scope=local&... -> local
        String scope = url.getParameter(Constants.SCOPE_KEY);
        // url.getProtocol():temp
        // public static final String LOCAL_PROTOCOL = "injvm"; 
        if (Constants.LOCAL_PROTOCOL.toString().equals(url.getProtocol())) {
            isJvmRefer = false;
        // public static final String SCOPE_LOCAL = "local"; 条件Constants.SCOPE_LOCAL.equals(scope) 满足,进入该判断,结果为true
        } else if (Constants.SCOPE_LOCAL.equals(scope) || (url.getParameter("injvm", false))) {
            isJvmRefer = true;
        // public static final String SCOPE_REMOTE = "remote";
        } else if (Constants.SCOPE_REMOTE.equals(scope)) {
            isJvmRefer = false;
        // 泛化调用
        } else if (url.getParameter(Constants.GENERIC_KEY, false)) {
            isJvmRefer = false;
        // 2022-02-14 17:49:09
        // 从本地获取服务提供者对应的Exporter
        } else if (getExporter(exporterMap, url) != null) {
            isJvmRefer = true;
        } else {
            isJvmRefer = false;
        }
        return isJvmRefer;
    }
}

2022-02-14 17:49:09处是尝试获取服务提供者对应的Exporter,如果是获取到则说明是本地引用,具体参考1.1.1:getExporter。

1.1.1:getExporter

源码如下:

class FakeCls {
    // com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol.getExporter
    // map: dongshi.daddy.service.scopelocal.ScopeLocalService -> {InjvmExporter@2488} "interface dongshi.daddy.service.scopelocal.ScopeLocalService -> injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?..."
    // key: injvm://127.0.0.1/dongshi.daddy.service.scopelocal.ScopeLocalService?...
    static Exporter getExporter(Map> map, URL key) {
        Exporter result = null;
        // key.getServiceKey() -> 组/xxx.yyy.AService:版本号
        if (!key.getServiceKey().contains("*")) {
            // 从已暴露map中获取Exporte,这里正常获取的是InjvmExporter
            result = map.get(key.getServiceKey());
        } else {
            // ...
        }
        // 没有获取到肯定为远程调用,直接返回null
        if (result == null) {
            return null;
        // 泛化调用也是远程调用,所以也返回null
        } else if (ProtocolUtils.isGeneric(
                result.getInvoker().getUrl().getParameter(Constants.GENERIC_KEY))) {
            return null;
        } else {
            return result;
        }
    }
}
2:Protocol 2.1:ProtocolFilterWrapper
class FakeCls {
    @Override
    public  Invoker refer(Class type, URL url) throws RpcException {
        // registry://协议,远程暴露时会走这里
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        // 2022-02-15 10:57:02
        return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    }
}

2022-02-15 10:57:02处是先继续向下调用,然后将最终返回的Invoker添加Filter调用链,buildInvokerChain获取的是public static final String ConSUMER = "consumer";即group=consumer的Filter,如下就会获取到:

@Activate(group = Constants.CONSUMER)
public class DubboHystrixFilter implements Filter {}
2.2:ProtocolFilterWrapper

包装原始Invoker,增加InvokerListener监听器们,监听暴露和取消暴露事件,源码如下:

class FakeCls {
    // com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper.refer
    public  Invoker refer(Class type, URL url) throws RpcException {
        // registry://协议,远程引用才会走这里
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        // 2022-02-15 14:03:50
        // 将获取的Invoker和InvokerListener,并返回包装类ListenerInvokerWrapper
        return new ListenerInvokerWrapper(protocol.refer(type, url),
                Collections.unmodifiableList(
                        ExtensionLoader.getExtensionLoader(InvokerListener.class)
                                .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
    }
}

2022-02-15 14:03:50处ListenerInvokerWrapper是Invoker的包装类,主要源码如下:

// 实现了Invoker接口,保证被装饰类有相同的行为
public class ListenerInvokerWrapper implements Invoker {
    private static final Logger logger = LoggerFactory.getLogger(ListenerInvokerWrapper.class);
    // 被装饰的invoker
    private final Invoker invoker;
}

这其实就是一种装饰设计模式 的应用。

2.3:InjvmProtocol

源码如下:

class FakeCls {
    // com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol.refer
    @Override
    public  Invoker refer(Class serviceType, URL url) throws RpcException {    
        // 2022-02-15 13:19:45
        return new InjvmInvoker(serviceType, url, url.getServiceKey(), exporterMap);
    }
}

2022-02-15 13:19:45处是直接封装InjvmInvoker,其中exporterMap中包含了所有的服务提供者类,如下可能配置:




    
    
    
    
    
    
    

exporterMap中就会有2个元素,如下:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/742780.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号