自适应扩展机制是Dubbo对SPI机制(Service Provider Interface)的增强实现版,和API相比,在使用SPI时,调用方无需指定接口的具体实现,而在程序运行时决定。
和原生SPI一样,在Dubbo的自适应扩展机制中,用META-INF目录下的配置文件指定接口实现类,配置文件名为接口的全限定类名。如下图,在dubbo-cluster包下的META-INF目录下,配置文件com.alibaba.dubbo.rpc.cluster.LoadBalance指定了四个接口实现,等号左边是实现类的name,等号右边是实现类的全限定类名。
// dubbo-cluster包中的配置文件com.alibaba.dubbo.rpc.cluster.LoadBalance
random=com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
roundrobin=com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
leastactive=com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
consistenthash=com.alibaba.dubbo.rpc.cluster.loadbalance.ConsistentHashLoadBalance
在Dubbo中,自适应扩展机制主要由类ExtensionLoader实现,对于接口T(本例中的LoadBalance),ExtensionLoader提供getAdaptiveExtension()方法,返回接口T的自适应实现类实例,该类实现了接口T,作为所有其它接口T实现类的入口。在对接口T中注解了@Adaptive的方法进行调用时,自适应实现类实例根据调用方法的参数获取实现类的name,并调用getExtension(String name)获取相应类实例,使用相应方法完成调用。
在上面的例子中,调用通过自适应类的接口方法select时,如果参数url中关于负载均衡的参数使用了"roundrobin",那么自适应实现类对象会获取到RoundRobinLoadBalance的实例,并通过该实例调用select方法,实现特定的负载均衡策略。
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
@Adaptive("loadbalance")
<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}
如上所述,Dubbo中的自适应扩展机制是通过ExtensionLoader
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.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);
}
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);
}
}
上面给出了一个T$Adaptive类的例子,该例中接口T为Protocol,Protocol$Adaptive实现了export和refer两个接口方法,实现的思路都是从export和refer的接口参数中获取Dubbo URL,然后根据URL中动态传递的protocol参数确定应用的Protocol实现类name,通过ExtensionLoader
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
ExtensionLoader
// ExtensionLoader
public T getExtension(String name) {
...
// 从缓存取, cachedInstances的key是类对应的name,value是实例
Holder<Object> holder = cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 缓存没有创建一个
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
createExtension(String name)代码如下,首先调用getExtensionClasses().get(name)获取名为name的类定义,然后以类定义为key从缓存EXTENSION_INSTANCES中取实例,有则返回,没有则创建一个,并用包裹类进行包裹。
// ExtensionLoader
private T createExtension(String name) {
// 取类定义
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
// 从缓存取, EXTENSION_INSTANCES的key是类定义,value是实例
// EXTENSION_INSTANCES 和 cachedInstances 都是实例的缓存,但是key不一样
// dubbo的spi配置文件中,多个name可能对应同一个实现类的定义
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);
// 用包裹类嵌套
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " +
type + ") could not be instantiated: " + t.getMessage(), t);
}
}
在getExtensionClasses中,根据指定的接口类型,扫描"META-INF/services/"、“META-INF/dubbo/”、"META-INF/dubbo/internal/"三个目录下关于该接口的实现类,加载到ExtensionLoader的成员cachedClasses中并返回,代码片段如下
// ExtensionLoader
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = loadExtensionClasses();
// 加载到cachedClasses
cachedClasses.set(classes);
}
}
}
return classes;
}
// ExtensionLoader
private Map<String, Class<?>> loadExtensionClasses() {
......
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
// 扫描META-INF/dubbo/internal/
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
// 扫描META-INF/dubbo/
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
// 扫描META-INF/services/
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
综上所述,对于接口T,ExtensionLoader提供getAdaptiveExtension()方法,返回动态生成类T$Adaptive的实例,T$Adaptive实现了接口T,作为所有其它接口T实现类的入口,在对接口T中注解了@Adaptive的方法进行调用时,T$Adaptive的实现根据参数调用getExtension (String name)获取相应类的实例,调用相应实现方法完成调用。
Dubbo的自适应扩展机制中,把SPI的实现类分为三类:
@Adaptive
public class AdaptiveExtensionFactory implements ExtensionFactory {
...
}
public class ProtocolListenerWrapper implements Protocol {
private final Protocol protocol;
public ProtocolListenerWrapper(Protocol protocol) {
if (protocol == null) {
throw new IllegalArgumentException("protocol == null");
}
this.protocol = protocol;
}
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return new ListenerExporterWrapper<T>(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
Collections.unmodifiableList(
ExtensionLoader.getExtensionLoader(InvokerListener.class)
.getActivateExtension(url, Constants.INVOKER_LISTENER_KEY)));
}
public List<T> getActivateExtension(URL url, String key, String group) {
String value = url.getParameter(key);
return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
}
在上面三种类型的实现类中:(1)在无法以固定模式动态生成接口T的自适应类T$Adaptive时,需要实现自适应实现类;(2)Wrap包裹类用于对接口实现类的功能进行增强,并不会通过getExtension(String name)接口直接获取;(3)普通实现类是基本功能的实现类,通过getExtension(String name)接口直接获取获取其实例,其中关于@Activate注解的用法可以参看Filter接口以及其实现类。
public class ExtensionLoader<T> {
// 接口T的类型
private final Class<?> type;
// 包裹类缓存,只保存包裹类型的实现类
private Set<Class<?>> cachedWrapperClasses;
// 自适应类缓存,只保存自适应类型的实现类,最多只能有一个
private volatile Class<?> cachedAdaptiveClass = null;
// 自适应类实例缓存,只保存自适应类型的实现类实例,最多只能有一个
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
// 实现类名称缓存,key为实现类的类型,value为实现类的name,只保存普通类型的实现类名称
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
// 实现类缓存,key为实现类的name,value为实现类,只保存普通类型的实现类
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
// cachedInstances是实现类实例缓存,key为实现类的名称,value为实例,只有普通类型的实现类才会产生实例
// EXTENSION_INSTANCES是实现类实例缓存,key为实现类的类型,value为实例,只有普通类型的实现类才会产生实例
// cachedInstances和EXTENSION_INSTANCES相比,因为多个name可能对应到同一个实现类,所以cachedInstances应该比EXTENSION_INSTANCES多
// cachedInstances中的多个value可能在EXTENSION_INSTANCES中是同一个value
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
// Activate注解缓存
private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
// 默认实现类名称,只能是普通类型的实现类
private String cachedDefaultName;
}
Dubbo对服务的引用是通过接口代理类实现的。
<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>
例如,按照上面xml进行配置,Dubbo会动态生成一个DemoService的接口代理类proxy0,该代理类的成员handler完成对所引用的实际服务的调用,包括本地调用和远程调用。同时,该代理类proxy0还实现了其它一些扩展接口,这些接口的也都通过handler的invoke方法实现。
public class proxy0 implements ClassGenerator.DC, EchoService, DemoService {
public static Method[] methods;
private InvocationHandler handler;
public proxy0(InvocationHandler invocationHandler) {
this.handler = invocationHandler;
}
public proxy0() {
}
public String sayHello(String string) {
// 将参数存储到 Object 数组中
Object[] arrobject = new Object[]{string};
// 调用 InvocationHandler 实现类的 invoke 方法得到调用结果
Object object = this.handler.invoke(this, methods[0], arrobject);
// 返回调用结果
return (String)object;
}
/** 回声测试方法 */
public Object $echo(Object object) {
Object[] arrobject = new Object[]{object};
Object object2 = this.handler.invoke(this, methods[1], arrobject);
return object2;
}
}
如下给出了InvocationHandler的完整定义,InvocationHandler中包含一个成员invoker,invoker是对最终调用接口DemoService的层层封装,通过这些封装,可以完成额外的工作,包括上述扩展接口的功能。
public class InvokerInvocationHandler implements InvocationHandler {
private final Invoker<?> invoker;
public InvokerInvocationHandler(Invoker<?> handler) {
this.invoker = handler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
...
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
}
下面贴出了生成接口代理类的代码
// JavassistProxyFactory
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
首先看一下Proxy,Proxy的代码贴出如下,Proxy是一个抽象类:它有一个抽象方法newInstance,和一个静态方法getProxy。getProxy实际返回一个Proxy子类的实例,该子类实现了抽象方法newInstance。因此,上面JavassistProxyFactory中生成接口代理类实例的getProxy方法可以看做分为两步实现:第一步调用Proxy.getProxy(interfaces)得到一个Proxy子类的实例,这个Proxy子类是动态生成的;第二步调用子类实例的newInstance方法得到接口代理类实例,这个接口代理类也是动态生成的。
// Proxy
public abstract class Proxy {
...
public static Proxy getProxy(ClassLoader cl, Class<?>... ics) {...}
...
abstract public Object newInstance(InvocationHandler handler);
...
}
public abstract class Proxy {
...
// Proxy的子类缓存
// 第一级Map的key是类加载器
// 第二级Map的key是接口字符串,例如"com.alibaba.dubbo.demo.DemoService;com.alibaba.dubbo.rpc.service.EchoService;"
// 第二级Map的可以理解为,可以生成某些接口代理类的Proxy子类
private static final Map<ClassLoader, Map<String, Object>> ProxyCacheMap = new WeakHashMap<ClassLoader, Map<String, Object>>();
...
}
抽象Wrapper类提供静态方法makeWrapper,该静态方法返回一个Wrapper子类的实例,该子类是动态生成的,它实现了Wrapper的抽象方法,通过这些抽象方法,可以方便获取类c的各种属性,也可以对c的方法进行反射调用。
// Wrapper
private static Wrapper makeWrapper(Class<?> c){
...
}
下面是Wrapper的抽象方法
public abstract class Wrapper {
abstract public String[] getPropertyNames();
abstract public Class<?> getPropertyType(String pn);
abstract public boolean hasProperty(String name);
abstract public Object getPropertyValue(Object instance, String pn) throws NoSuchPropertyException, IllegalArgumentException;
abstract public void setPropertyValue(Object instance, String pn, Object pv) throws NoSuchPropertyException, IllegalArgumentException;
abstract public String[] getMethodNames();
abstract public String[] getDeclaredMethodNames();
abstract public Object invokeMethod(Object instance, String mn, Class<?>[] types, Object[] args) throws NoSuchMethodException, InvocationTargetException;
}
本文参考Dubbo2.6.8版本