美文网首页
2.3 dubbo服务端暴露(二)

2.3 dubbo服务端暴露(二)

作者: 云养猫达人 | 来源:发表于2018-03-24 22:19 被阅读0次

    从doExportUrls()方法可以看出,dubbo支持多协议发布:

    private void doExportUrls() {
      List<URL> registryURLs = loadRegistries(true);
      for (ProtocolConfig protocolConfig : protocols) {
                doExportUrlsFor1Protocol(protocolConfig, registryURLs);//进行url暴露
            }
        }
    
    /***
    *篇幅问题,只显示关键代码
    */
    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        //========一系列的校验与参数赋值================
        <!--...省略代码...-->
    
         if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                    exportLocal(url);// 进行本地发布
              }
        if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                    if (registryURLs != null && registryURLs.size() > 0) {
                        for (URL registryURL : registryURLs) {
                            url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                            URL monitorUrl = loadMonitor(registryURL);//获取dubbo监视器url,暂时不管);
      for (ProtocolConfig protocolConfig : protocols) {
                doExportUrlsFor1Protocol(protocolConfig, registryURLs);//进行url暴露
            }
        }
    
    /***
    *篇幅问题,只显示关键代码
    */
    private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
        //========一系列的校验与参数赋值================
        <!--...省略代码...-->
    
         if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                    exportLocal(url);// 进行本地发布
              }
        if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                    if (registryURLs != null && registryURLs.size() > 0) {
                        for (URL registryURL : registryURLs) {
                            url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                            URL monitorUrl = loadMonitor(registryURL);//获取dubbo监视器url,暂时不管
                            if (monitorUrl != null) {
                                url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                            }
                            if (logger.isInfoEnabled()) {
                                logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                            }
                           
                            Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                            DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
    
                            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);
                    }
    <!-...省略代码...->
    }
    

    下面我们来看本地发布方法exportLocal,该方法主要分为三个步骤:
    1、调整url参数,如将发布协议改为injvm本地协议
    2、通过代理工厂proxyFactory将ref转为invoker
    3、通过协议类型protocol将invoker转为exporter
    (PS:本地发布的原因是为了方便接口内部调用可以使用dubbo的filter等进行过滤)

    private void exportLocal(URL url) {
            if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
                URL local = URL.valueOf(url.toFullString())
                        .setProtocol(Constants.LOCAL_PROTOCOL)//设置协议为injvm本地协议
                        .setHost(LOCALHOST)//地址127.0.0.1
                        .setPort(0);//端口为0
                Exporter<?> exporter = protocol.export( //protocol位serviceConfig初始化的时候生成的代理类
                        proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
                exporters.add(exporter);
                logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
            }
        }
    //ServiceConfig中的公共变量
    private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
    private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
    

    proxyFactory 实际上是一个生成的代理类ProxyFactory$Adaptive,通过接口ProxyFactory我们可以看到@Adaptive是用在getProxy与getInvoker两个方法上的,生成的代码如下:

    package com.alibaba.dubbo.rpc;
    
    import com.alibaba.dubbo.common.extension.ExtensionLoader;
    
    public class ProxyFactory$Adaptive implements com.alibaba.dubbo.rpc.ProxyFactory {
        public java.lang.Object getProxy(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.getParameter("proxy", "javassist");
            if (extName == null)
                throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
            com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
            return extension.getProxy(arg0);
        }
    
        public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws com.alibaba.dubbo.rpc.RpcException {
            if (arg2 == null) throw new IllegalArgumentException("url == null");
            com.alibaba.dubbo.common.URL url = arg2;
            String extName = url.getParameter("proxy", "javassist");
            if (extName == null)
                throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
            com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
            return extension.getInvoker(arg0, arg1, arg2);
        }
    }
    

    由此我们可知最后代码会调用StubProxyFactoryWrapper.class-->JavassistProxyFactory.class#getInvoker(T proxy, Class<T> type, URL url)。

    JavassistProxyFactory#getInvoker的代码实现如下:

    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
            // TODO Wrapper cannot handle this scenario correctly: the classname contains '$' 该类记录了DemoServiceImpl的属性名称,方法名称等信息
            final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
            return new AbstractProxyInvoker<T>(proxy, type, url) {
                @Override
                protected Object doInvoke(T proxy, String methodName,
                                          Class<?>[] parameterTypes,
                                          Object[] arguments) throws Throwable {
                    return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);//wrapper.invokeMethod(args[]) 就是用来调用传入的方法中的指定名称的方法!
                }
            };
        }
    

    Wrapper 其实就是一个对ref的一层封装类,在调用getWrapper时内部执行makeWrapper进行一层封装,代码实现很简单这里不过多讲解了,执行后会创建一个Wrapper1的代理类,然后返回一个新建的抽象类AbstractProxyInvoker并重写方法doInvoke(),doInvoke方法内部返回的就是Wrapper1的invokeMethod方法。

    获取得到封装好的invoker对象后通过protocol调用injvm协议进行发布,protocol类的生成与proxyFactory 类似,最终会调用InjvmProtocol#export(Invoker<T> invoker)将invoker转为exporter。

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
            return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
        }
    
    class InjvmExporter<T> extends AbstractExporter<T> {
    
        private final String key;
    
        private final Map<String, Exporter<?>> exporterMap;
    
        InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
            super(invoker);
            this.key = key;
            this.exporterMap = exporterMap;
            exporterMap.put(key, this);
        }
    
        public void unexport() {
            super.unexport();
            exporterMap.remove(key);
        }
    
    }
    

    最终,代码将生成的exporter放入List<Exporter<?>> exporters缓存对象中,由此,本地发布结束。

    相关文章

      网友评论

          本文标题:2.3 dubbo服务端暴露(二)

          本文链接:https://www.haomeiwen.com/subject/ekpnfftx.html