zoukankan      html  css  js  c++  java
  • dubbo源码阅读-服务调用(十二)之本地调用(Injvm)

    使用例子

     <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoServiceImpl" protocol="injvm" scope="local" /> 

    本地如何引用

    ReferenceConfig#createProxy 

    参见《dubbo源码阅读-服务订阅(八)之主流程》

     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) { // if a url is specified, don't do local reference
                    isJvmRefer = false;
                    //根据url配置判断是否是本地调用
                } else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
                    // by default, reference local service if there is
                    isJvmRefer = true;
                } else {
                    isJvmRefer = false;
                }
            } else {
                isJvmRefer = isInjvm().booleanValue();
            }
    
            //如果是本地引用
            if (isJvmRefer) {
                //组织url为injvm://127.0.0.1
                URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
                /**
                 * <1>SPI扩展点
                 * private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
                 */
                invoker = refprotocol.refer(interfaceClass, url);
                if (logger.isInfoEnabled()) {
                    logger.info("Using injvm service " + interfaceClass.getName());
                }
            } else {
                //如果我们手动配置了url;隔开 追加到urls列表
                if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
                    // 拆分地址成数组,使用 ";" 分隔。
                    String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
                    // 循环数组,添加到 `url` 中。
                    if (us != null && us.length > 0) {
                        for (String u : us) {
                            // 创建 URL 对象
                            URL url = URL.valueOf(u);
                            // 设置默认路径
                            if (url.getPath() == null || url.getPath().length() == 0) {
                                url = url.setPath(interfaceName);
                            }
                            // 注册中心的地址,带上服务引用的配置参数
                            if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                                urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                            } else {
                                // 服务提供者的地址
                                urls.add(ClusterUtils.mergeUrl(url, map));
                            }
                        }
                    }
                } else { // assemble URL from register center's configuration
                    //获取注册中心地址 具体可以看你https://www.cnblogs.com/LQBlog/p/12469007.html#autoid-6-10-0
                    List<URL> us = loadRegistries(false);
                    if (us != null && !us.isEmpty()) {
                        for (URL u : us) {
                            //加载Monitor 使用例子https://blog.csdn.net/sunhuaqiang1/article/details/80141651
                            URL monitorUrl = loadMonitor(u);
                            if (monitorUrl != null) {
                                map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                            }
                            //添加refer标识 标识是从注册中心地尼公约
                            urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
                        }
                    }
                    if (urls.isEmpty()) {
                        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.");
                    }
                }
                //  // 单 `urls` 时,引用服务,返回 Invoker 对象
                if (urls.size() == 1) {
                    /**
                     * Protocol 取得registryProtocol 不过会被代理 具体可以看 https://www.cnblogs.com/LQBlog/p/12470179.html#autoid-2-0-0
                     * 默认是registry SPI扩展 private static final Protocol refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
                     */
                    invoker = refprotocol.refer(interfaceClass, urls.get(0));
                } else {
                    //集群订阅
                    List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                    URL registryURL = null;
                    // 循环 `urls` ,引用服务,返回 Invoker 对象
                    for (URL url : urls) {
                        // 引用服务
                        invokers.add(refprotocol.refer(interfaceClass, url));
                        // 使用最后一个注册中心的 URL
                        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                            registryURL = url; // use last registry url
                        }
                    }
    
                    //  // 有注册中心
                    if (registryURL != null) { // registry url is available
                        // 对有注册中心的 Cluster 只用 AvailableCluster
                        // use AvailableCluster only when register's cluster is available
                        URL u = registryURL.addParameterIfAbsent(Constants.CLUSTER_KEY, AvailableCluster.NAME);
                        invoker = cluster.join(new StaticDirectory(u, invokers));
                        // 无注册中心,全部都是服务直连
                    } else { // not a registry 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()) {
                // make it possible for consumer to retry later if provider is temporarily unavailable
                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());
            }
            //<2>创建代理类
            return (T) proxyFactory.getProxy(invoker);
        }

    <1>InjvmProtocol#refer

     public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
            //<3>创建一个 InjvmInvoker 引用对象
            return new InjvmInvoker<T>(serviceType, url, url.getServiceKey(), exporterMap);
    }

    参见《dubbo源码阅读-服务订阅(八)之本地订阅(injvm)》

    <2>InvokerInvocationHandler

    参见《dubbo源码阅读-ProxyFactory(十一)之JdkProxyFactory》

    /**
     * InvokerHandler
     */
    public class InvokerInvocationHandler implements InvocationHandler {
    
        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();
            //如果当前method是Object直接调用
            if (method.getDeclaringClass() == Object.class) {
                return method.invoke(invoker, args);
            }
            //toString hashCode equals 直接调用object的 // 基础方法,不使用 RPC 调用
            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]);
            }
            //<3>走RPC
            return invoker.invoke(new RpcInvocation(method, args)).recreate();
        }
    
    }

    InjvmInvoker

    <3>doInvoke

     @Override
        public Result doInvoke(Invocation invocation) throws Throwable {
            //可以发现是从exporterMap获取对应的Exporter然后再获取内部Invoker执行调用
            Exporter<?> exporter = InjvmProtocol.getExporter(exporterMap, getUrl());
            if (exporter == null) {
                throw new RpcException("Service [" + key + "] not found.");
            }
            RpcContext.getContext().setRemoteAddress(NetUtils.LOCALHOST, 0);
    //<4>exporter如何产生
    return exporter.getInvoker().invoke(invocation); }

     注意看2 此时的InvoCation是RpcInvocation 封装了method和方法信息

    Exporter如何产生

    参见《dubbo源码阅读-服务暴露(七)之主流程》

    <4>ServiceConfig#doExportUrlsFor1Protocol

     private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
            //获取协议名
            String name = protocolConfig.getName();
            //如果没配置 则默认dubbo
            if (name == null || name.length() == 0) {
                name = "dubbo";
            }
    
            Map<String, String> map = new HashMap<String, String>();
            //封装 sid=provider 到map
            map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
            //设置dubbo版本
            map.put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());
            //设置时间
            map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
            if (ConfigUtils.getPid() > 0) {
                map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
            }
            //反射获取application所有公共的get方法 以及parameter配置 封装到map
            appendParameters(map, application);
            //反射获取moule所有公共的get方法 以及parameter配置 封装到map
            appendParameters(map, module);
            //反射获取provider所有公共的get方法 以及parameter配置 封装到map
            appendParameters(map, provider, Constants.DEFAULT_KEY);
            //反射获取provider所有公共的get方法 以及parameter配置 封装到map
            appendParameters(map, protocolConfig);
            //反射获取当前Service所有公共的get方法 以及parameter配置 封装到map
            appendParameters(map, this);
            //封装methodConfig配置到map
            if (methods != null && !methods.isEmpty()) {
                ....//胜率部分代码
            }
    
            //是否为泛型化发布
            if (ProtocolUtils.isGeneric(generic)) {
                //增加参数generic=true
                map.put(Constants.GENERIC_KEY, generic);
                //增加methods=*
                map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);
            } else {
                //这种版本号到map
                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(Constants.METHODS_KEY, Constants.ANY_VALUE);
                } else {
                    //主要是将暴露方法封装到map
                    map.put(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
                }
            }
            //是否有token配置 将token配置到map
            if (!ConfigUtils.isEmpty(token)) {
                if (ConfigUtils.isDefault(token)) {
                    map.put(Constants.TOKEN_KEY, UUID.randomUUID().toString());
                } else {
                    map.put(Constants.TOKEN_KEY, token);
                }
            }
            //获取协议是否是本地调用injvm
            if (Constants.LOCAL_PROTOCOL.equals(protocolConfig.getName())) {
                //配置不注册
                protocolConfig.setRegister(false);
                //map增加参数notify=false
                map.put("notify", "false");
            }
            // 如果protocolConfig 没有配置contextPath则从provider获取
            String contextPath = protocolConfig.getContextpath();
            if ((contextPath == null || contextPath.length() == 0) && provider != null) {
                contextPath = provider.getContextpath();
            }
            /**
             * 获取暴露服务的host
             */
            String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
            //暴露服务的port
            Integer port = this.findConfigedPorts(protocolConfig, name, map);
            //组织暴露的url
            URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
    
    
            //SPI扩展点根据协议判断是否有指定协议Protocol的ConfiguratorFactory 扩展 可以覆盖我们的Url
            if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .hasExtension(url.getProtocol())) {
                url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                        .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
            }
    
    
            /**
             * 构建invoker实例
             * 获取dubbo:service配置的scope属性
             * 其可选值为 none (不暴露)、local (本地)、remote (远程),如果配置为 none,则不暴露。默认为 local。
             */
            String scope = url.getParameter(Constants.SCOPE_KEY);
            // don't export when none is configured SCOPE_NONE!=none如果未配置none
            if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
    
                // export to local if the config is not remote (export to remote only when config is remote)
                //remote!=scope  什么是本地暴露: https://zhuanlan.zhihu.com/p/98423741
                if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                    //<5>本地暴露
                    exportLocal(url);
                }
                // export to remote if the config is not local (export to local only when config is local)
                //非本地暴露则远程暴露
                if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                    if (registryURLs != null && !registryURLs.isEmpty()) {
                        for (URL registryURL : registryURLs) {
                            //是否配置了动态注册dynamic 作用看文档注释 http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-service.html
                            url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
                            //获取监控地址url monitor配置
                            URL monitorUrl = loadMonitor(registryURL);
                            if (monitorUrl != null) {
                                //将监控地址追加到url
                                url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                            }
                            if (logger.isInfoEnabled()) {
                                logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                            }
    
                            // For providers, this is used to enable custom proxy to generate invoker
                            //是否有配置proxy 生成动态代理的方式 如果有追加到registryURL参数 见文档http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-service.html
                            String proxy = url.getParameter(Constants.PROXY_KEY);
                            if (StringUtils.isNotEmpty(proxy)) {
                                registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
                            }
                            /**
                             *     private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
                             *     spi扩展点 生成Invoker
                             *     invoker封装了代理类也就是我们的ServiceImpl类 以及url信息
                             *     registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())
                             *     在registryUrl 追加export=暴露的url
                             *     invoker的url是regitry 参数加了export八路url
                             */
                            Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                            //增强 代理了invoker 封装了serviceConfig信息
                            DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                            //注意 invoker的url是registryUrl 所以protocol=registry SPI调用的是
                            //registry=com.alibaba.dubbo.registry.integration.RegistryProtocol  直通车:https://www.cnblogs.com/LQBlog/p/12470681.html
                            Exporter<?> exporter = protocol.export(wrapperInvoker);
                            //追加到暴露列表
                            exporters.add(exporter);
                        }
                    } else {
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
    
                        Exporter<?> exporter = protocol.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                }
            }
            this.urls.add(url);
        }

    <5>ServiceConfig#exportLocal

      private void exportLocal(URL url) {
            //如果协议为不是injvm
            if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                //生成本地Url
                URL local = URL.valueOf(url.toFullString())
                        .setProtocol(Constants.LOCAL_PROTOCOL)//injvm
                        .setHost(LOCALHOST) //127.0.0.1
                        .setPort(0);
                //service.classimpl 将本地暴露接口存入StaticContext map key为接口 value为实现类的名字
                StaticContext.getContext(Constants.SERVICE_IMPL_CLASS).put(url.getServiceKey(), getServiceClass(ref));
                /**
                 * 使用 ProxyFactory 创建 Invoker 对象 根据url配置的proxy来作为spi的Key 缺省值是javasist 动态生成一个代理class 也可以使用jdk
                 * private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
                 * 使用 Protocol 暴露 Invoker 对象 根据url的protcol 来作为SPIKey查找 缺省值duboo
                 *
                 * private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
                 * 直通车:https://www.cnblogs.com/LQBlog/p/12470179.html
                 *<6>proxyFactory.getInvoker
                 *<7>protocol.export
                 */
                Exporter<?> exporter = protocol.export(
                        proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
                // 添加到 `exporters`
                exporters.add(exporter);
                logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
            }
        }

    <6>JDKProxyFactory#getInvoker

    参见《dubbo源码阅读-ProxyFactory(十一)之JdkProxyFactory》

    /**
         * proxy为我们的代理对象 type为我们的接口 url为我们的发布和订阅协议
         * 包装成Invoker对象
         * @param proxy
         * @param type
         * @param url
         * @param <T>
         * @return
         */
        @Override
        public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
            return new AbstractProxyInvoker<T>(proxy, type, url) {
                @Override
                protected Object doInvoke(T proxy, String methodName,
                                          Class<?>[] parameterTypes,
                                          Object[] arguments) throws Throwable {
                    Method method = proxy.getClass().getMethod(methodName, parameterTypes);
                    return method.invoke(proxy, arguments);
                }
            };
        }

    <7>InjvmProtocol#

    参见《dubbo源码阅读-服务暴露(七)之本地暴露(Injvm)》

      public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
            //创建一个InjvmExporter代理对象 内部保存了invoker以及所有的本地发布实例 key为 service com.alibaba.dubbo.demo.DemoService
            //<6>exporterMap为InJvmProtocol成员变量 构造函数内部会add key为Service全名称
            return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
        }
    InjvmExporter构造函数
     InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
            super(invoker);
            //key为service全名称
            this.key = key;
            //为协议成员变量的map
            this.exporterMap = exporterMap;
            //将当前export添加到map
            exporterMap.put(key, this);
        }
  • 相关阅读:
    php 有趣的头像拼图
    php基础篇-二维数组排序姐妹篇
    php基础篇-二维数组排序 array_multisort
    php应用篇-百度图片的防盗链
    《留给自己,也留给每一位在青春里迷茫找不到自己的年轻人》 爱你现在的时光——白岩松
    没有什么能一下打垮你,就像没有什么能一下拯救你
    php基础篇-双引号、单引号的区别
    TortoiseSVN Start
    cover-view文案被切割:加全角空格
    canvas不显示,必须设置canvas-id
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12510478.html
Copyright © 2011-2022 走看看