一、dubbo源碼從入門到放棄-SPI
@update:2016-06-28 00:44:46
@author:張三金 去哪兒網高級工程師
未完待續...
1.引
如果要講dubbo源碼,那么要從SPI開始(SPI自行google),因為dubbo所有的功能都是通過自己實現的一套SPI機制來擴展的,可以理解為dubbo的所有功能都拆分並且抽象為interface,通過SPI查找這些interface的實現,並且通過url組合起來,就成了一個完整的rpc框架.
2.ExtensionLoader
ExtensionLoader是dubbo內部的SPI實現,dubbo的所有組件都通過ExtensionLoader獲取,本文以最常見的interface
Filter為例,dubbo里有很多的filter,並且支持碼農自定義filter,dubbo的filter全是通過SPI查找的,所以非常易於擴展.
com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper#buildInvokerChain方法中拿到當前service所有的filter的實現,然后執行.
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
2.1getExtensionLoader
getExtensionLoader方法會為每一個.class new 一個ExtensionLoader實例,並緩存起來
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
2.2getAdaptiveExtension
在dubbo中,需要擴展的功能需要在其interface上打到@SPI注解
在dubbo SPI中,interface的一個實現叫做Extension,Extension實現了這個接口的功能,一個interface有許多的Extension
AdaptiveExtension叫做適配Extension,一個AdaptiveExtension的作用是從所有的Extension中,根據當前的url配置策略,選出指定的的Extension
上面的代碼通過getAdaptiveExtension()方法拿到當前interface的AdaptiveExtension
首先會拿到所有的接口實現
code:ExtensionLoader#loadExtensionClasses
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if (value != null && (value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
loadExtensionClasses
- 從interface上拿到注解@SPI,取出默認實現的名字
- 調用loadFile加載interface的實現類
loadFile會到3個目錄下把所有的接口實現類加載進來
- META-INF/dubbo/internal/
dubbo內部實現都放在這個目錄下 - META-INF/dubbo/
- META-INF/services/
例如下面為dubbo內部實現filter的SPI文件
file:META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter
echo=com.alibaba.dubbo.rpc.filter.EchoFilter
generic=com.alibaba.dubbo.rpc.filter.GenericFilter
genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter
token=com.alibaba.dubbo.rpc.filter.TokenFilter
accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter
activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter
context=com.alibaba.dubbo.rpc.filter.ContextFilter
consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter
compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter
timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter
monitor=com.alibaba.dubbo.monitor.support.MonitorFilter
validation=com.alibaba.dubbo.validation.filter.ValidationFilter
cache=com.alibaba.dubbo.cache.filter.CacheFilter
trace=com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter
future=com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter
2.3loadFile
SPI文件給出了所有實現的完全限定類名,那么可以通過反射機制加載對應的Class
Class<?> clazz = Class.forName(line, true, classLoader);
加載的Class會被歸為3類
- Extension,接口的實現,實現接口功能的類
由Holder<Map<String, Class<?>>> cachedClasses
持有其引用 - AdaptiveExtension,適配指定Extension,打上了@Adaptive注解的類會被加載為AdaptiveExtension,這個類只能有一個
由Class<?> cachedAdaptiveClass
持有其引用 - wrapper,用裝飾者模式包裝Extension的類,wrapper有且只有一個以interface為參數的構造函數
由Set<Class<?>> cachedWrapperClasses
持有其引用
加載AdaptiveExtension,通過判斷@Adaptive注解
if (clazz.isAnnotationPresent(Adaptive.class)) {
if(cachedAdaptiveClass == null) {
cachedAdaptiveClass = clazz;
} else if (! cachedAdaptiveClass.equals(clazz)) {
throw new IllegalStateException("More than 1 adaptive class found: "
+ cachedAdaptiveClass.getClass().getName()
+ ", " + clazz.getClass().getName());
}
}
加載wrapper extension
try {
clazz.getConstructor(type);
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
//不是wrapper extension
}
前面兩個都不是,那就是Extension,直接放入Holder<Map<String, Class<?>>> cachedClasses
3.注解
3.1@SPI
SPI注解標記在interface上,表示這個interface的實現通過SPI加載,value表示有多個實現時,默認使用指明的實現
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SPI {
/**
* 缺省擴展點名。
*/
String value() default "";
}
reference
interface Filter
@SPI
public interface Filter {
Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException;
}