Dubbo发布过程中,扩展点的加载

扩展点的功能

为什么Dubbo要引入扩展点加载功能呢?根本性上是从Dubbo的驱动方式上考虑出发的。Dubbo是基于URL驱动的。譬如我们发布一个Dubbo服务,提供了一个服务者,在一个提供服务接口的同时,我们会提供诸如使用什么协议(Dubbo)、什么注册中心RegistryProtocol(zookeeper)、集群方式cluster(failover)、代理方式ProxyFactory(javaassist),我们在配置服务的属性时,经过Dubbo的ServiceBean和ReferenceBean的处理,会将这些属性值拼装成一个URL对象,在调用关键节点时,会根据URL的配置属性动态的去加载扩展点下面配置的实现类,在动态代理类里面实现对具体实现类的调用。
下面粘贴几个实例代码

  • Protocol$Adaptive
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
    public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }
    public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
    }
    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);
    }
    public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg1 == null) throw new IllegalArgumentException("url == null");
        com.alibaba.dubbo.common.URL url = arg1;
        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.refer(arg0, arg1);
    }
}

  • Cluster$Adaptive
package com.alibaba.dubbo.rpc.cluster;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Cluster$Adaptive implements com.alibaba.dubbo.rpc.cluster.Cluster {
    public com.alibaba.dubbo.rpc.Invoker join(com.alibaba.dubbo.rpc.cluster.Directory arg0) throws com.alibaba.dubbo.rpc.RpcException {
        if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument == null");
        if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
        String extName = url.getParameter("cluster", "failover");
        if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.cluster.Cluster) name from url(" + url.toString() + ") use keys([cluster])");
        com.alibaba.dubbo.rpc.cluster.Cluster extension = (com.alibaba.dubbo.rpc.cluster.Cluster)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.cluster.Cluster.class).getExtension(extName);
        return extension.join(arg0);
    }
}

代码中的扩展点

在Dubbo服务发布的过程中,第一次出现扩展点的加载是在doExportUrlsFor1Protocol()方法中,加载ConfiguratorFactory配置工厂。

        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }

首先根据加载类类型获取扩展类加载器,然后根据扩展点名称获取具体的扩展点。先从缓存中获取对应类型的扩展器,如果缓存中不存在,则利用构造函数创建一个新的加载器,并放入到缓存中。

public class ExtensionLoader<T> {

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null)
            throw new IllegalArgumentException("Extension type == null");
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }

        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }


    public boolean hasExtension(String name) {
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        try {
            this.getExtensionClass(name);
            return true;
        } catch (Throwable t) {
            return false;
        }
    }

    private ExtensionLoader(Class<?> type) {
        this.type = type;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

    public T getAdaptiveExtension() {
        Object instance = cachedAdaptiveInstance.get();
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {
                        try {
                            instance = createAdaptiveExtension();
                            cachedAdaptiveInstance.set(instance);
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
            }
        }

        return (T) instance;
    }

    private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        } catch (Exception e) {
            throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
        }
    }
}

调用过程为:

getExtensionClass(String name)
  ->getExtensionClass(String name)

创建DubboProtocol的过程为,其中包含多个Wrapper的包装:

Dubbo是URL驱动的。

.getAdaptiveExtension():获取的是自适应类,是根据@Adaptive扩展点,动态生成class code,并经过编译生成的动态代理类。
另一个常用的方法是:getExtension:用来获取宽扩展类实例。

Protocol是Protocol$Adaptive,实际调用的是DubboProtocol
ProxyFactoty是 ProxyFactoty$Adaptive,实际调用的是JavassistProxyFactory,用来生成代理类和调用者。
此处代码意思是:使用DubboProtocol协议在本地暴露一个使用JavassistProxyFactory生成的代理类的调用者。

    private void exportLocal(URL url) {
        if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL)
                    .setHost(LOCALHOST)
                    .setPort(0);
            ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
            Exporter<?> exporter = protocol.export(
                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
        }
    }

injectExtension()方法,用来处理方法名带有set()的方法,动态的设置处理参数。例如:此处的协议为动态,根据URL中配置的参数而定,所以添加了一个注入的方法。injectExtension()方法中,判断到有set()开始的方法,通过Object object = objectFactory.getExtension(pt, property)得到一个动态代理类Type$Adaptive,然后将该动态代理对象设置到参数中

public class StubProxyFactoryWrapper implements ProxyFactory {
    public void setProtocol(Protocol protocol) {
        this.protocol = protocol;
    }
}
public class ExtensionLoader<T> {
    private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                for (Method method : instance.getClass().getMethods()) {
                    if (method.getName().startsWith("set")
                            && method.getParameterTypes().length == 1
                            && Modifier.isPublic(method.getModifiers())) {
                        Class<?> pt = method.getParameterTypes()[0];
                        try {
                            String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        } catch (Exception e) {
                            logger.error("fail to inject via method " + method.getName()
                                    + " of interface " + type.getName() + ": " + e.getMessage(), e);
                        }
                    }
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return instance;
    }
}

另一个常用的方法是:getActivateExtension(URL url, String[] values, String group):获取激活点

@SPI
public interface ExtensionFactory {

}

@SPI注解,用来提供获取ExtensionFactory的实现类:public class SpiExtensionFactory
其中的isWrapperClass判断规则是:实现类的构造函数是否是包含加载类参数。如果是,则判断为包装类,并将包装类添加到cachedWrapperClasses成员变量中,加载类中不添加。
@Activate注解:当某个类的上标注@Activate注解时,在loadClass的过程中,由于判断类上有@Activate注解,会将加载的类添加到cachedActivates成员变量中,作为在加载扩展点类时的返回对象。

注意 JavassistProxyFactory类中getInvoker()方法,final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);

@Activate注解在Filter中有使用案例:

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

在调用export()方法的过程当中,首先对Filter进行了加载。
阶段性总结export()过程:
在export()过程中,首先调用了包装DubboProtocol的Wrapper类,包括三个类,由外到内依次是:QosProtocolWrapper、ProtocolListenerWrapper,ProtocolFilter,由外层逐级调用内层,再调用到ProtocolFilterWrapper类的时候,调用了一个构建调用链的方法, protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER)),其中Constants.SERVICE_FILTER_KEY=service.filter;Constants.PROVIDER = provider,其中,在URL的参数中,service.filter的value为null,所以在加载了filter的所有实现类之后,根据Activate的条件对Filter进行过滤;其中此处的过滤条件为 :group为group;在此过程中符合条件的Filtle有8个:

0 = {ExceptionFilter@11068} 
1 = {ClassLoaderFilter@11268} 
2 = {EchoFilter@11442} 
3 = {MonitorFilter@11832} 
4 = {GenericFilter@11974} 
5 = {TimeoutFilter@11997} 
6 = {TraceFilter@12018} 
7 = {ContextFilter@12032} 

然后对Filter进行循环处理,循环创建invoker,并调用上一个invoker的invoker方法。
然后调用InjvmProtocol的export()方法进行服务暴露,构造了一个InjvmExporter对象

    InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) {
        super(invoker);
        this.key = key;//key=com.bail.user.service.IUserService:1.0.0
        this.exporterMap = exporterMap;
        exporterMap.put(key, this);//将InjvmExporter自己放入到了map容器中
    }

在exportLocal方法返回的Exporter为一个ListenerExporterWrapper类型的,并将该对象放入到exporters容器中,exporters为ServiceBean的一个成员变量。
ServiceConfig处的export,protocol值为registry.

接下来看DubboProtocol的发布过程:
构建DubboExporter
openServer

posted @ 2021-11-29 14:34  南宫煌_慧  阅读(153)  评论(0编辑  收藏  举报