dubbo源码解析(6)dubbo服务暴露--- 本地暴露

56 篇文章 2 订阅

在启动dubbo服务的时候我们可以看到这样一些日志

第一行代码表示dubbo服务在 spring启动之后准备开始启动了,这行代码定位到

ServiceBean#onApplicationEvent()中

这个方法在spring启动过程的refresh方法中会执行到,具体可以看我spring系列文章

这里关注this.export();

直接定位到

ServiceConfig#this.doExport();

---->this.doExportUrls();

 

private void doExportUrls() {
    List<URL> registryURLs = this.loadRegistries(true);
    Iterator i$ = this.protocols.iterator();

    while(i$.hasNext()) {
        ProtocolConfig protocolConfig = (ProtocolConfig)i$.next();
        this.doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }

}

首先获取到URL,然后遍历所有的Protocol实例

这里只有一个dubbo

进入doExportUrlsFor1Protocol

前面都是一些检查和封装,我们直接找关键语句

 

if (!"none".toString().equalsIgnoreCase(scope)) {
    if (!"remote".toString().equalsIgnoreCase(scope)) {
        this.exportLocal(url);
    }

    if (!"local".toString().equalsIgnoreCase(scope)) {
        if (logger.isInfoEnabled()) {
            logger.info("Export dubbo service " + this.interfaceClass.getName() + " to url " + url);
        }

        if (registryURLs != null && registryURLs.size() > 0 && url.getParameter("register", true)) {
            Iterator i$ = registryURLs.iterator();

            while(i$.hasNext()) {
                URL registryURL = (URL)i$.next();
                url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                URL monitorUrl = this.loadMonitor(registryURL);
                if (monitorUrl != null) {
                    url = url.addParameterAndEncoded("monitor", monitorUrl.toFullString());
                }

                if (logger.isInfoEnabled()) {
                    logger.info("Register dubbo service " + this.interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                }

                Invoker<?> invoker = proxyFactory.getInvoker(this.ref, this.interfaceClass, registryURL.addParameterAndEncoded("export", url.toFullString()));
                Exporter<?> exporter = protocol.export(invoker);
                this.exporters.add(exporter);
            }
        } else {
            Invoker<?> invoker = proxyFactory.getInvoker(this.ref, this.interfaceClass, url);
            Exporter<?> exporter = protocol.export(invoker);
            this.exporters.add(exporter);
        }
    }
}

this.urls.add(url);

 

这里会根据url上scope上的是否为none来进行本地暴露和远程暴露,我们先看本地暴露

this.exportLocal(url);
URL local = URL.valueOf(url.toFullString()).setProtocol("injvm").setHost("127.0.0.1").setPort(0);
Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(this.ref, this.interfaceClass, local));
this.exporters.add(exporter);

首先会吧协议变成injvm,然后获取一个invoker,吧这个invoker对象封装成一个exporter,最后吧这个exporter放在exporters集合中。其中第二句话是关键

proxyFactory.getInvoker(this.ref, this.interfaceClass, local)先看这句,这里会从proxyFactory中获取到一个invoker对象

在ServiceConfig的常量中有一个proxyFactory

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

主意到ProxyFactory借口的实现类上没有标注@adatpive注解的类,所以

proxyFacotry是一个ProxyFacotry$Adaptive

public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws java.lang.Object {
    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);
}

复制出getInvoker方法

这里的extName=javassist

extension=JavassistProxyFactory,这里会调用JavassistProxyFactory的getInvoker方法

public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf(36) < 0 ? proxy.getClass() : type);
    return new AbstractProxyInvoker<T>(proxy, type, url) {
        protected Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable {
            return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
        }
    };
}

首先获取到一个wrapper,关于这个wrapper

Wrapper是一个独立的抽象类, 用来 包装对接口class的操作(上面我们说的IUserService)。这里对class操作,我们可以抽象几种操作: 
1.获取class中所有字段的名称 : abstract public String[] getPropertyNames();

2.根据字段名称获取字段类型:abstract public Class getPropertyType(String pn);

3.判断class中是否存在某字段:abstract public boolean hasProperty(String name);

4.获取class对象的某字段的值:abstract public Object getPropertyValue(Object instance, String pn) throws Throwable;

5.设置字段的值:abstract public void setPropertyValue(Object instance, String pn, Object pv) throws Throwable;

6.执行方法:abstract public Object invokeMethod(Object instance, String methodName, Class[] types, Object[] args) throws Throwable;


然后我们说一下AbstractProxyInvoker,AbstractProxyInvoker继承Invoker接口

public interface Invoker<T>{
    // 执行方法, Invocation 为执行信息(包括执行 哪个方法,参数等信息),Result为执行结果
    Result invoker(Invocation invocation);
    // 省略其它 dubbo中的方法
}

点击进入invocation
public interface Invocation {
    String getMethodName(); ----------方法名称

    Class<?>[] getParameterTypes(); -----------------方法参数类型

    Object[] getArguments(); -----------方法参数

    //忽略
}
Result对象中封装了返回的结果
package com.alibaba.dubbo.rpc.proxy;

import java.lang.reflect.InvocationTargetException;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcResult;

/**
 * InvokerWrapper
 * 
 * @author william.liangf
 */
public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
    
    private final T proxy;
    
    private final Class<T> type;
    
    private final URL url;

    public AbstractProxyInvoker(T proxy, Class<T> type, URL url){
        if (proxy == null) {
            throw new IllegalArgumentException("proxy == null");
        }
        if (type == null) {
            throw new IllegalArgumentException("interface == null");
        }
        if (! type.isInstance(proxy)) {
            throw new IllegalArgumentException(proxy.getClass().getName() + " not implement interface " + type);
        }
        this.proxy = proxy;
        this.type = type;
        this.url = url;
    }

   。。。。。。

    public Result invoke(Invocation invocation) throws RpcException {
        try {
            return new RpcResult(doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments()));
        } catch (InvocationTargetException e) {
            return new RpcResult(e.getTargetException());
        } catch (Throwable e) {
            throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
        }
    }
    
    protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;

   。。。。。
    
}

protected abstract Object doInvoke是一个模板方法,后续很多invoker都会实现这个方法

proxy=接口的实现类

type=接口名称

invoker方法就是真正实现调用的方法

关于这一块,可以参考这篇文章https://blog.csdn.net/LuoZheng4698729/article/details/80111714

https://blog.csdn.net/prestigeding/article/details/80642774

回到protocol.export

这个protocol是protocol$Adaptive

复制export方法

public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
    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);
}

这里的extName=injvm

我们进入这里获取到的extension 是ProtocolFilterWrapper,至于原因可以参考dubbo 内核aop那一篇

进入到ProtocolFilterWrapper#export
buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER);

关于dubbo中filter的实现可以参考https://www.jianshu.com/p/d279349435cd

点击进入ExtensionLoader#getActivateExtension

public List<T> getActivateExtension(URL url, String[] values, String group) {
    List<T> exts = new ArrayList<T>();
    List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
    if (! names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
        getExtensionClasses();
        for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
            String name = entry.getKey();
            Activate activate = entry.getValue();
            if (isMatchGroup(group, activate.group())) {
                T ext = getExtension(name);
                if (! names.contains(name)
                        && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name) 
                        && isActive(activate, url)) {
                    exts.add(ext);
                }
            }
        }
        Collections.sort(exts, ActivateComparator.COMPARATOR);
    }
在这里是加载dubbo自带的Filter,也就是@Activate修饰的
------------------------------------
下面开始是自己实现的Filter
    List<T> usrs = new ArrayList<T>();
    for (int i = 0; i < names.size(); i ++) {
       String name = names.get(i);
        if (! name.startsWith(Constants.REMOVE_VALUE_PREFIX)
              && ! names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
           if (Constants.DEFAULT_KEY.equals(name)) {
              if (usrs.size() > 0) {
              exts.addAll(0, usrs);
              usrs.clear();
              }
           } else {
           T ext = getExtension(name);
           usrs.add(ext);
           }
        }
    }
    if (usrs.size() > 0) {
       exts.addAll(usrs);
    }
    return exts;
}

回到buildInvokerChain方法,

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
    if (filters.size() > 0) {
        for (int i = filters.size() - 1; i >= 0; i --) {
            final Filter filter = filters.get(i);
            final Invoker<T> next = last;
            last = new Invoker<T>() {

                public Class<T> getInterface() {
                    return invoker.getInterface();
                }

                public URL getUrl() {
                    return invoker.getUrl();
                }

                public boolean isAvailable() {
                    return invoker.isAvailable();
                }

                public Result invoke(Invocation invocation) throws RpcException {
                    return filter.invoke(next, invocation);
                }

                public void destroy() {
                    invoker.destroy();
                }

                @Override
                public String toString() {
                    return invoker.toString();
                }
            };
        }
    }
    return last;
}

最终换回的last中封装了所有的filter

回到ProtocolFilterWrapper

这里看到protocol依然是个装饰类,ProtocolListenerWrapper

进入ProtocolListenerWrapper#exprot方法

主要看

return new ListenerExporterWrapper<T>(protocol.export(invoker), 
        Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));

这里会返回一个ListenerExporterWrapper对象

 这个ListenerExporterWrapper是封装过Listner和invoker得对象,invoker中又同时封装了Filter

回到ServiceConfig#exportLocal

exporters.add(exporter);

然后把这ListenerExporterWrapper加入到exporters集合中

如果我们自己实现这样一个Listner
 

public class MyExporterListener implements ExporterListener,InvokerListener {
    @Override
    public void exported(Exporter<?> exporter) throws RpcException {
        System.out.println("暴露" + exporter.getInvoker().getInterface());
    }

    @Override
    public void unexported(Exporter<?> exporter) {

    }

    @Override
    public void referred(Invoker<?> invoker) throws RpcException {
        System.out.println("被引用");
    }

    @Override
    public void destroyed(Invoker<?> invoker) {

    }
}

那么这个时候日志会打印出   "暴露interface spi.Hello"

这时候本地暴露的代码已经分析完毕了

看到console上会打印出

DUBBO] Export dubbo service spi.Hello to local registry, dubbo version: 2.5.3, current host: 127.0.0.1

下一篇我们分析远程暴露

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值