dubbo源码阅读——Extension扩展机制

dubbo不仅功能强大,扩展性也非常强,在关键的位置,dubbo都留有扩展接口,比如:协议(Protocol)、集群策略(Cluster)、代理工厂(ProxyFactory)、序列化方式(Serialization)、线程池(ThreadPool)、负载均衡(LoadBalance)等等。每一种扩展都有多种实现,采用策略模式把实现写在/META-INF/dubbo/internal中,然后通过ExtensionLoader类来加载扩展,最终通过一系列的策略选择最正确的那个。这一系列的策略正是今天所讲的内容。

一、三个注解@SPI、@Adaptive、@Activate

@SPI 

标记为可扩展,并且可以设置默认值。用来定义的扩展接口,默认值即表示策略文件文中的某个key,那么这个key的value即是扩展的默认实现。比如:Protocol接口,

@SPI("dubbo")
public interface Protocol {

META-INF\dubbo\internal\com.alibaba.dubbo.rpc.Protocol文件中:

dubbo源码阅读——Extension扩展机制_第1张图片

均未指定protocol属性的话,则DubboProtocol就是默认的实现。

加载SPI的扩展时,SPI的默认值缓存在ExtensionLoader.cachedDefaultName中,以备使用。

@Adaptive是用的最多的扩展,可以用来标注类和方法。以下分别进行说明:

1、标记在类上表示默认的自适应类(cachedAdaptiveClass)

ExtensionLoader.getAdativeExtension(***.class) 先到缓存中查找是否已经存在@Adaptive标注的类。加载到ExtensionLoader.cachedAdaptiveClass中,该类只能有一个,有多个的话报异常。

比如AdaptiveCompiler,AdaptiveExtensionFactory

2、如果***.class接口下不存在@Adaptive标注的类,只有@Adaptive标注的方法。

则去需要生成一个***$Adpative的代理类,把所有标注@Adaptive的方法,都生成一个代理方法,经过编译,加载,创建对象返回,代理类继承***接口,调用时与真实的扩展实现无差异。此时最关键的是要弄明白***$Adpative这个代理类中做了什么。

根据结果查找过程,return的代码(以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的值,继续往上看:

code.append("\nString extName = ").append(getNameCode).append(";");

而getNameCode可能来自九个可能:

for (int i = value.length - 1; i >= 0; --i) {
                    if(i == value.length - 1) { //第一次循环
                        if(null != defaultExtName) { //@SPI的默认值不为空的情况
                            if(!"protocol".equals(value[i])) //@Adaptive的value不是protocol的情况
                                if (hasInvocation)  //方法参数含有Invocation类型
                                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                                else
                                    getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);
                            else
                                getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);
                        }
                        else {
                            if(!"protocol".equals(value[i]))
                                if (hasInvocation) 
                                    getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                                else
                                    getNameCode = String.format("url.getParameter(\"%s\")", value[i]);
                            else
                                getNameCode = "url.getProtocol()";
                        }
                    }
                    else {
                        if(!"protocol".equals(value[i]))
                            if (hasInvocation) 
                                getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);
                            else
                                getNameCode = String.format("url.getParameter(\"%s\", %s)", value[i], getNameCode);
                        else
                            getNameCode = String.format("url.getProtocol() == null ? (%s) : url.getProtocol()", getNameCode);
                    }
                }

1、Protocol是比较特别的扩展,实现非常之多,着重说一下:

Protocol扩展:@SPI默认值"dubbo"、@Adaptive无默认值(value被置为“protocol”),此时getNameCode值只可能为:

getNameCode = String.format("( url.getProtocol() == null ? \"%s\" : url.getProtocol() )", defaultExtName);

也就是说根据当前url(参数)进行获取,如果url的值为null,使用默认值dubbo

2、非Protocol的扩展,@SPI有默认值,@Adaptive有一个默认值(没有默认值会指定接口名),hasInvocation=false

getNameCode = String.format("url.getParameter(\"%s\", \"%s\")", value[i], defaultExtName);

优先从URL中获取属性名叫做value的属性值,如果不存在,则使用默认的defaultExtName

举例:集群策略(Cluster)

@SPI(FailoverCluster.NAME)
public interface Cluster {
    @Adaptive
    <T> Invoker<T> join(Directory<T> directory) throws RpcException;

}

@SPI默认值failover,但如果url中配置了属性cluster=failfast,如下图:

dubbo源码阅读——Extension扩展机制_第2张图片

那么返回的扩展实现就是 com.alibaba.dubbo.rpc.cluster.support.FailfastCluster

3、非Protocol的扩展,@SPI有默认值,@Adaptive有一个默认值(没有默认值会指定接口名),hasInvocation=true,则:

getNameCode = String.format("url.getMethodParameter(methodName, \"%s\", \"%s\")", value[i], defaultExtName);

先去从URL获取methodName.${value}属性的值,然后获取${value}属性的值,如果都没有,则使用SPI的默认值。

举例:负载均衡接口(LoadBalance)

@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {

    @Adaptive("loadbalance")
	 Invoker select(List> invokers, URL url, Invocation invocation) throws RpcException;

}

存在invocation参数,但并没有获取自适应方法,只从invokers中获取对负载均衡(loadbalance属性)的的值,如果不存在则默认使用随机策略(random)。

if (invokers != null && invokers.size() > 0) {
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
                    .getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
        } else {
            loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
        }

4,非Protocol的扩展,@SPI有默认值,@Adaptive有多个指定值,hasInvocation=false,则:

优先使用后面属性的值,如果后面的属性值不存在,则使用前面的,如果都不存在,则默认使用defaultExtName

举例:

@SPI(AllDispatcher.NAME)
public interface Dispatcher {
    @Adaptive({Constants.DISPATCHER_KEY, "dispather", "channel.handler"}) // 后两个参数为兼容旧配置
    ChannelHandler dispatch(ChannelHandler handler, URL url);

}

随着产品的迭代,会有些属性名变得不再恰当,轻易废弃或者修改的话就无法兼容旧版本,这种方式是可以达到向下的兼容的目的。本例中,优先使用channel.handler的属性值,不存在则使用dispather的属性值,还不存在则使用dispatcher(注意与dispather区别),还不存在则用@SPI的默认值all。

3、都不存在的情况

如果ExtensionLoader.getAdativeExtension(***.class)获取自适应扩展类时,既不存在@Adaptive标记的实现类,接口上也不存在@Adaptive标注的方法,则报异常。

@Activate 主要用于扩展Filter和Listener

看源码:

public @interface Activate {
    /**
     * Group过滤条件。
     * 包含{@link ExtensionLoader#getActivateExtension}的group参数给的值,则返回扩展。
     * 如没有Group设置,则不过滤。
     */
    String[] group() default {};

    /**
     * Key过滤条件。包含{@link ExtensionLoader#getActivateExtension}的URL的参数Key中有,则返回扩展。
     * 示例:
     * 注解的值 @Activate("cache,validatioin"),
     * 则{@link ExtensionLoader#getActivateExtension}的URL的参数有cacheKey,或是validatioin则返回扩展。
     * 如没有设置,则不过滤。
     */
    String[] value() default {};
    String[] before() default {};
    String[] after() default {};
    int order() default 0;
}

此处只关心group()和value()两个方法。group 表示该扩展用在哪个组中(comsumer、provider)。value表示关注的URL的key。

举例:

例一:限流过滤器(ExecuteLimitFilter)

@Activate(group = Constants.PROVIDER, value = Constants.EXECUTES_KEY)
public class ExecuteLimitFilter implements Filter {

group=provider说明是仅仅用在服务端,value=executes表示,当URL中的executes不为空值时才会使用此过滤器。

注:executes默认cpu+1,不手动设置也会有值的。

例二:缓存过滤器(CacheFilter)

@Activate(group = {Constants.CONSUMER, Constants.PROVIDER}, value = Constants.CACHE_KEY)
public class CacheFilter implements Filter {

该过滤器同时过滤服务端和消费端,且当URL中的属性cache的值不为空才使用。

二、Wrapper

ProtocolListenerWrapper,ProtocolFilterWrapper这写Wrapper都有两个共同特点:

1、实现被包装的接口

2、要有一个构造方法,构造方法的参数类型就是被包装的接口类型。

举例:ProtocolListenerWrapper,ProtocolFilterWrapper都是用来包装Protocol的,看他们的源码:

public class ProtocolFilterWrapper implements Protocol {

    private final Protocol protocol;

    public ProtocolFilterWrapper(Protocol protocol){
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }
    
public class ProtocolListenerWrapper implements Protocol {

    private final Protocol protocol;

    public ProtocolListenerWrapper(Protocol protocol){
        if (protocol == null) {
            throw new IllegalArgumentException("protocol == null");
        }
        this.protocol = protocol;
    }

在目标类被调用的时候,包装类会优先被调用,这个的实现方式也在ExtensionLoander中:

 
try {
    clazz.getConstructor(type);
    Set> wrappers = cachedWrapperClasses;
    if (wrappers == null) {
        cachedWrapperClasses = new ConcurrentHashSet>();
        wrappers = cachedWrapperClasses;
    }
    wrappers.add(clazz);
} catch (NoSuchMethodException e) {

在获取type的目标扩展类时,会调用createExtension方法,方法中把type所有的wrapperClass一层层包装到type的外层,至于顺序时不固定的:

Set> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
    for (Class wrapperClass : wrapperClasses) {
        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
    }
}
return instance;

wrapper中一般没有set方法,可以忽略injectExtension.这个方法是用来注入该扩展中的持有的其他扩展。

这样返回的instance其实是被wrapper包装过的(可能好几层)的对象,调用的时候会先调到wrapper的方法中。

wrapper中干了什么?

先说ProtocolListenerWrapper:

public  Exporter export(Invoker invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        return new ListenerExporterWrapper(protocol.export(invoker), 
                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                        .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
    }

    public  Invoker refer(Class type, URL url) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        return new ListenerInvokerWrapper(protocol.refer(type, url), 
                Collections.unmodifiableList(
                        ExtensionLoader.getExtensionLoader(InvokerListener.class)
                        .getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
    }

new ListenerInvokerWrapper和new ListenerExporterWrapper分别会在服务发现和服务暴漏时调用。看代码其实很简单,就是把与该服务相关的listener全部拿出来,先挨个调用一边。“先”的意思时发生在真正的调用之前。

再说ProtocolFilterWrapper:

public  Exporter export(Invoker invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }

    public  Invoker refer(Class type, URL url) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
    }
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
    Invoker<T> last = invoker;
    List 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;
}

这个更容易理解,就是把所有应该用到的Filter取出来,组织起一个链条,当服务真正被调用的时候,会先调用这些过滤器。

listenerWrapper和filterWrapper的相同点和区别:

相同点:都实现Protocol接口,都有一个参数为Protocol类型的构造器。都需要重写export和refer方法。

区别:listenerWrapper执行refer时,会通过new ListenerInvokerWrapper对Invoker进行封装,但ListenerInvokerWrapper中会把所有的listener全部执行一边,也就是listener被组织起来时已经被触发了。

filterWrapper执行refer时,仅仅是把所有的filter编制成一条链(单向链表),然后把链表的尾部返回。这样真正调用invoker.invoke时会从尾部调用filter.invoke,最后再调用真正的invoker.invoke。filter被组织起来并不会马上触发,而是伴随着invoke的调用才触发。

三、Filter和Listener

该说的第二节说完了

你可能感兴趣的:(我的技术博客,dubbo,框架)