Dubbo 服务暴露之服务本地暴露

Injvm

Posted by Jay on March 3, 2019

Dubbo 服务暴露之服务本地暴露

服务暴露的流程如下图所示:

简单看一下服务暴露的伪代码:

/** dubbo 服务暴露伪代码 */
public class DubboProviderServiceExportSimpleCode {
    private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();
    // 获取注册中心url列表
    // [ registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&cellinvokemode=sharing&client=zkclient&dubbo=2.0.0&group=dubbo_test&pid=74378&registry=zookeeper&timestamp=1550318759960 ]
    List<URL> registryURLs = loadRegistries(); // 获取注册中心url列表
    for (ProtocolConfig protocolConfig : protocols) {
        // 创建dubbo协议的url
        // dubbo://172.16.132.166:20881/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider&timestamp=1550318831051
        URL url = new URL(name, host, port, path, map);
        // 本地暴露
        if (<dubbo:service scope!=remote>) {
            // 构造injvm协议的url
            // injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider&timestamp=1550318831051
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL)
                    .setHost(NetUtils.LOCALHOST)
                    .setPort(0);
            Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, local);
            Exporter<?> exporter = protocol.export(invoker);
            exporters.add(exporter);
        }
        // 远程暴露
        if (<dubbo:service scope!=local>) {
            for (URL registryURL : registryURLs) {
                // registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())-- 
                // registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&cellinvokemode=sharing&client=zkclient&dubbo=2.0.0&export=dubbo%3A%2F%2F172.16.132.166%3A20881%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind.ip%3D172.16.132.166%26bind.port%3D20881%26cellinvokemode%3Dsharing%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D74378%26side%3Dprovider%26timestamp%3D1550318831051&group=dubbo_test&pid=74378&registry=zookeeper&timestamp=1550318759960
                Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                Exporter<?> exporter = protocol.export(invoker);
                exporters.add(exporter);
            }
        }
    }
}

本地暴露:

  • 通过JavassistProxyFactory(默认),将具体的实现类包装成AbstractProxyInvoker实例
  • InjvmProtocol将上述的AbstractProxyInvoker实例转换成Exporter(导出流程: ProtocolListenerWrapper-->ProtocolFilterWrapper-->InjvmProtocol)

远程暴露:

  • 通过JavassistProxyFactory(默认),将具体的实现类包装成AbstractProxyInvoker实例
  • DubboProtocol将上述的AbstractProxyInvoker实例转换成Exporter(导出流程: ProtocolListenerWrapper-->ProtocolFilterWrapper-->RegistryProtocol-->DubboProtocol)

本文先来看本地暴露。首先给出本地服务暴露的整个调用链。

ServiceBean.onApplicationEvent(ContextRefreshedEvent event)
-->ServiceConfig.export()
   -->doExport()
      -->doExportUrls()
         -->loadRegistries(boolean provider) // 生成注册中心URL
         -->doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs)
            -->Wrapper.getWrapper(Class<?> c)
               -->makeWrapper(Class<?> c)
            -->new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map)
            
            <!--  本地暴露  -->
            -->exportLocal(url)
               -->构造injvm协议的url: injvmUrl
               <!--  1 将ref包装成Invoker   -->
               -->ProxyFactory$Adaptive.getInvoker(ref实例, interfaceClass, injvmUrl)
                  -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist")
                  -->StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
                     -->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
                        -->Wrapper getWrapper(Class<?> c)
                           -->makeWrapper(Class<?> c)
                        -->new AbstractProxyInvoker<T>(ref实例, interfaceClass, injvmUrl)
               <!--  2 将Invoker暴露为Exporter   -->
               -->Protocol$Adaptive.export(Invoker<T> invoker)
                  -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension("injvm")
                  -->ProtocolListenerWrapper.export(Invoker<T> invoker)
                     -->ProtocolFilterWrapper.export(Invoker<T> invoker)
                        -->buildInvokerChain(final Invoker<T> invoker, key:"service.filter", group:"provider")
                           -->ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(injvmUrl, "service.filter", "provider")
                        -->InjvmProtocol.export(Invoker<T> invoker)
                           -->new InjvmExporter(Invoker<T> invoker, key:"com.alibaba.dubbo.demo.DemoService", Map<String, Exporter<?>> exporterMap)
                              -->InjvmExporter.exporterMap({"com.alibaba.dubbo.demo.DemoService" -> "injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider&timestamp=1550318831051"})
                  	 -->new ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners)
               <!--  Exporter添加到List<Exporter<?>> exporters   -->
               -->exporters.add(exporter)

本地暴露的方法及代码如下:

// 本地暴露
// 1 根据传进来的url(例如dubbo协议的url)构造injvm协议的url
// injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider&timestamp=1550318831051
// 2 将ref实现类实例包装为AbstractProxyInvoker实例
// 3 将AbstractProxyInvoker实例转换为ListenerExporterWrapper实例,包装InjvmExporter实例
@SuppressWarnings({"unchecked", "rawtypes"})
private void exportLocal(URL url) {
    if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
        // local url: injvm://ip....
        URL local = URL.valueOf(url.toFullString())
            .setProtocol(Constants.LOCAL_PROTOCOL)
            .setHost(LOCALHOST)
            .setPort(0);
        // Protocol$Adaptive.export()代码片段:
        // com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
        // 包装: ProtocolListenerWrapper-->ProtocolFilterWrapper-->InjvmProtocol
        // exporter变量为ListenerExporterWrapper类型,包装InjvmExporter实例
        Exporter<?> exporter = protocol.export(
            // proxyFactory为Adaptive类实例,proxyFactory.getInvoker()的调用: StubProxyFactoryWrapper --> JavassistProxyFactory
            // 返回AbstractProxyInvoker实现类实例
            proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
        exporters.add(exporter);
        logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
    }
}

本地暴露的整体流程如下:

  • 首先将dubbo协议的url,改成injvm协议的urllocal

  • 将服务实现类实例ref通过proxyFactory包装成AbstractProxyInvoker实例;

  • AbstractProxyInvoker实例转化为Exporter实例;

  • 最后将生成的Exporter实例存放在ServiceConfigList<Exporter> exporters中。

一、具体的服务实现类实例包装成AbstractProxyInvoker实例

proxyFactory.getInvoker(ref, (Class) interfaceClass, local)

具体服务实现类:com.alibaba.dubbo.demo.provider.DemoServiceImpl2。调用链如下:

<!--  1 将ref包装成Invoker   -->
-->ProxyFactory$Adaptive.getInvoker(ref实例, interfaceClass, injvmUrl)
  -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist")
  -->StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
     -->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
        -->Wrapper getWrapper(Class<?> c)
           -->makeWrapper(Class<?> c)
        -->new AbstractProxyInvoker<T>(ref实例, interfaceClass, injvmUrl)
1.ProxyFactory$Adaptive.getInvoker(Object arg0, Class arg1, URL arg2)
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);
}
  • arg0: com.alibaba.dubbo.demo.provider.DemoServiceImpl2 实例
  • arg1: interface com.alibaba.dubbo.demo.DemoService Class对象
  • arg2: injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider&timestamp=1550318831051

这里首先是参数检查和赋值。之后获取key为javassistProxyFactory实现类:JavassistProxyFactory,该类会在spi进行aop的时候包裹在StubProxyFactoryWrapper中,最终调用链为:

ProxyFactory$Adaptive -> StubProxyFactoryWrapper -> JavassistProxyFactory

2.StubProxyFactoryWrapper.getInvoker(T proxy, Class<T> type, URL url)
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
    // 这个类先执行,后执行JavassistProxyFactory.getInvoker() -- 包装
    return proxyFactory.getInvoker(proxy, type, url);
}

这里的proxyFactory就是JavassistProxyFactory实例。

3.JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
// 将具体服务转为Invoker
// @param proxy 对外提供服务的实现类实例
// @param type 服务接口
// @param url URL
// @param <T> 服务类型
// @return Invoker
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
    // TODO Wrapper类不能正确处理带$的类名
    // 包装类
    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);
        }
    };
}

这里首先会创建一个class com.alibaba.dubbo.demo.provider.DemoServiceImpl2的包装类Wrapper:Wrapper.getWrapper(Class<DemoServiceImpl2>)。该类记录了DemoServiceImpl2的属性名称,方法名称等信息。具体代码如下:

package com.alibaba.dubbo.common.bytecode;

import com.alibaba.dubbo.common.bytecode.Wrapper;
import java.util.Map;
// 这里类名为Wrapper1是因为之前ServiceConfig.doExportUrlsFor1Protocol()方法中
// 已使用过Wrapper进行包装
public class Wrapper1 extends Wrapper {

    public static String[] pns; // property name array
    public static java.util.Map pts = new HashMap();// <property name, property types>
    public static String[] mns;// all method name array.
    public static String[] dmns;// declared method name array.
    public static Class[] mts0; // 方法参数类型数组
	// 默认构造器
    public Wrapper1() {
    }

    public String[] getPropertyNames() {
        return pns;
    }

    public boolean hasProperty(String n) {
        return pts.containsKey(n);
    }

    public Class getPropertyType(String n) {
        return (Class) pts.get(n);
    }

    public String[] getMethodNames() {
        return mns;
    }

    public String[] getDeclaredMethodNames() {
        return dmns;
    }

    public void setPropertyValue(Object o, String n, Object v) {
        com.alibaba.dubbo.demo.provider.DemoServiceImpl2 w;
        try {
            w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl2) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl2.");
    }

    public Object getPropertyValue(Object o, String n) {
        com.alibaba.dubbo.demo.provider.DemoServiceImpl2 w;
        try {
            w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl2) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + n + "\" filed or setter method in class com.alibaba.dubbo.demo.provider.DemoServiceImpl2.");
    }

    // 调用方法
    // @param o  实现类
    // @param n  方法名称
    // @param p  参数类型
    // @param v  参数值
    // @return 方法调用结果
    // @throws java.lang.reflect.InvocationTargetException
    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
        com.alibaba.dubbo.demo.provider.DemoServiceImpl2 w;
        try {
            w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl2) o);
        } catch (Throwable e) {
            throw new IllegalArgumentException(e);
        }
        try {
            if ("sayHello".equals(n) && p.length == 1) {
                return ($w) w.sayHello((java.lang.String) v[0]);
            }
        } catch (Throwable e) {
            throw new java.lang.reflect.InvocationTargetException(e);
        }
        throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + n + "\" in class com.alibaba.dubbo.demo.provider.DemoServiceImpl2.");
    }
}

之后创建一个AbstractProxyInvoker实例。AbstractProxyInvoker类定义如下:

public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
    // 服务接口的实现类实例
    private final T proxy;
    // 服务接口类
    private final Class<T> type;
    // 注册中心url 或者 提供者url(injvm协议)
    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;
    }

    @Override
    public Class<T> getInterface() {
        return type;
    }

    @Override
    public URL getUrl() {
        return url;
    }

    @Override
    public boolean isAvailable() {
        return true;
    }

    @Override
    public void destroy() {
    }

    @Override
    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);
        }
    }

    // 方法实际调用
    // @param proxy 对外提供服务的实现类实例
    // @param methodName 方法名
    // @param parameterTypes 参数类型数组
    // @param arguments 具体参数数组
    // @return 调用结果
    protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;

    @Override
    public String toString() {
        return getInterface() + " -> " + (getUrl() == null ? " " : getUrl().toString());
    }


}

其中:

  • proxy: com.alibaba.dubbo.demo.provider.DemoServiceImpl2 实例
  • type: ` com.alibaba.dubbo.demo.DemoService Class`对象
  • url: injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider&timestamp=1550318831051

这样,具体服务就被包装成AbstractProxyInvoker实例了。

二、AbstractProxyInvoker实例转换为Exporter实例

Exporter<?> exporter = protocol.export(AbstractProxyInvoker实例);

调用链如下:

<!--  2 将Invoker暴露为Exporter   -->
-->Protocol$Adaptive.export(Invoker<T> invoker)
   -->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension("injvm")
   -->ProtocolListenerWrapper.export(Invoker<T> invoker)
      -->ProtocolFilterWrapper.export(Invoker<T> invoker)
         -->buildInvokerChain(final Invoker<T> invoker, key:"service.filter", group:"provider")
            -->ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(injvmUrl, "service.filter", "provider")
         -->InjvmProtocol.export(Invoker<T> invoker)
            -->new InjvmExporter(Invoker<T> invoker, key:"com.alibaba.dubbo.demo.DemoService", Map<String, Exporter<?>> exporterMap)
               -->InjvmExporter.exporterMap({"com.alibaba.dubbo.demo.DemoService" -> "injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=172.16.132.166&bind.port=20881&cellinvokemode=sharing&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=74378&side=provider&timestamp=1550318831051"})
   	 -->new ListenerExporterWrapper(Exporter<T> exporter, List<ExporterListener> listeners)
<!--  Exporter添加到List<Exporter<?>> exporters   -->
-->exporters.add(exporter)
1.Protocol$Adaptive.export(Invoker<T> invoker)
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);
}
  • arg0: 上边创建出来的AbstractProxyInvoker实例。

这里首先是参数检查和赋值。之后获取key为injvmProtocol实现类:InjvmProtocol,该类会在spi进行aop的时候被ProtocolFilterWrapperProtocolListenerWrapper递归包裹,最终调用链为:

Protocol$Adaptive -> ProtocolListenerWrapper -> ProtocolFilterWrapper -> InjvmProtocol

2.ProtocolListenerWrapper.export(Invoker<T> invoker)
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // 注册中心协议
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        // 协议为registry,通过ProtocolFilterWrapper继续导出服务  
        return protocol.export(invoker);
    }
    // 1.调用ProtocolFilterWrapper.export()方法导出服务,返回InjvmExporter实例
    // 2.找到所有激活的ExporterListener (可自定义导出监听器)(这里没有listener)
    // 3.回调所有监听器的void exported(Exporter<?> exporter)方法
    // 4.返回包装InjvmExporter实例的ListenerExporterWrapper实例给Protocol$Adaptive
    return new ListenerExporterWrapper<T>(protocol.export(invoker), Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                    .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}

这里首先调用ProtocolFilterWrapper.export()方法导出服务,返回InjvmExporter实例;找到所有激活的ExporterListener (可自定义导出监听器);回调所有监听器的void exported(Exporter<?> exporter)方法;最后返回包装InjvmExporter实例的ListenerExporterWrapper实例给Protocol$Adaptive

3.ProtocolFilterWrapper.export(Invoker<T> invoker)
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
        // 若协议为registry,直接通过RegistryProtocol注册并暴露服务
        return protocol.export(invoker);
    }
    // 构建invoker的拦截器,调用InjvmProtocol.export(Invoker<T> invoker)方法
    return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY,
            Constants.PROVIDER));
}

这里首先使用Filterinvoker进行了递归包裹,之后使用InjvmProtocol将包裹后的invoker转化为InjvmExporter

buildInvokerChain(final Invoker invoker, String key, String group)

// 构建服务调用拦截链。
 1.根据key从url中获取相应的filter的values再根据这个values和group去获取类上带有@Active注解的filter集合
 2.之后将这些filter对传入的invoker进行递归包装成invoker就是一个链表
// @param invoker AbstractProxyInvoker实例
// @param key service.filter
// @param group provider
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker; // 最后调用的invoker,原invoker最后执行
    // 拦截器列表
    // 根据order从小到大排序
    List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class)
            .getActivateExtension(invoker.getUrl(), key, group);
    if (filters.size() > 0) {
        // 构建调用链,从filter(0)开始执行到filter(size()-1),最后到原invoker
        for (int i = filters.size() - 1; i >= 0; i--) {
            final Filter filter = filters.get(i);
            final Invoker<T> next = last; // 下一个被调用的invoker

            last = new Invoker<T>() {
                @Override
                public Class<T> getInterface() {
                    return invoker.getInterface();
                }

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

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

                @Override
                public Result invoke(Invocation invocation) throws RpcException {
                    // 进行调用拦截
                    return filter.invoke(next, invocation);
                }

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

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

        }
    }
    return last;
}

这里:

  • invoker:之前创建出来的AbstractProxyInvoker实例;
  • key:service.filter
  • group:provider
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);

这句代码是根据keyurl中获取相应的Filtervalues,再根据这个valuesgroup去获取类上带有@Active注解的满足要求的Filter集合。这里具体的代码逻辑可以查看Dubbo SPI @Activate注解分析这篇文章。最终会获取到8个Filter,关于Filter,后续阐述。

4.InjvmProtocol.export(Invoker<T> invoker)
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    // 返回InjvmExporter实例. 调用过程为: ProtocolListenerWrapper-->ProtocolFilterWrapper-->InjvmProtocol
    return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}

class InjvmExporter<T> extends AbstractExporter<T> {

    // 服务配置的键
    // 服务关键字([group/]interface[:version])
    private final String key;

    // 服务关键字([group/]interface[:version]) ---> InjvmExporter实例
    private final Map<String, Exporter<?>> exporterMap;

    // 创建InjvmExporter实例
    // @param invoker 被Filter进行递归包裹后的Invoker
    // @param key 服务关键字
    // @param exporterMap  服务关键字([group/]interface[:version]) ---> InjvmExporter实例
    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
        super(invoker);
        this.key = key;
        this.exporterMap = exporterMap;
        exporterMap.put(key, this);
    }

    @Override
    public void unexport() {
        super.unexport();
        exporterMap.remove(key);
    }

}

最终的InjvmExporter实例:

  • key = "com.alibaba.dubbo.demo.DemoService"
  • exporterMap: { "com.alibaba.dubbo.demo.DemoService" -> 当前的InjvmExporter实例 }
  • Invoker<T> invoker = 被Filter进行递归包裹后的Invoker

最终的ServiceConfigexporters列表:

  • List<Exporter<?>> exporters = [ ListenerExporterWrapper实例(包装InjvmExporter实例) ]

三、需要本地暴露的原因

同一个jvm进程中的服务提供者和消费者,消费者调用提供者不需要通过远程注册中心,但是又想使用Filter链,可以使用本地暴露。

http://dubbo.incubator.apache.org/zh-cn/docs/user/demos/local-call.html

“本地调用使用了 injvm 协议,是一个伪协议,它不开启端口,不发起远程调用,只在 JVM 内直接关联,但执行 Dubbo 的 Filter 链。”