dubbo AdaptiveExtension

时间:2023-03-09 21:29:46
dubbo AdaptiveExtension
AdaptiveExtension
自适应Extension,作者其实在使用Extension方和Extension之间插入AdaptiveExtension用来自适应,也可以说是适配。
所以,我们发现,这里其实有三种,
1,一种是自动生成的Adaptive根据url参数来选择具体实现;
2,一种是自己实现一个Adaptive,写自己的逻辑选择具体实现,比如AdaptiveExtensionFactory,Compiler。这种都是在ExtensionLoader中需要使用,一般是不会这么做的。
3,还有一种没有Adaptive,实现一个adapter,比如TelnetHandler。这种因为不能根据url参数来决定使用哪一个实现。
从结构上看AdaptiveExtension是很重要的角色,我们配置了一些插件,但是具体使用哪一个如果用硬编码自然是不可能的,而这种自适应的方式得益于dubbo的状态数据信息都可以通过URL来获取,被叫做以URL为总线的模式。而自适应即是根据这些信息来决定调用哪一个插件的实现。
ExtensionLoader是插件机制的核心类:
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
// 获得AdaptiveExtension入口
public T getAdaptiveExtension() {
// cachedAdaptiveInstance缓存
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 extenstion " + type + ", cause: " + e.getMessage(), e);
}
} private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
// 依然没有cachedAdaptiveClass 说明没有配置@Adaptive 那就自动生成一个
return cachedAdaptiveClass = createAdaptiveExtensionClass();
} private Class<?> createAdaptiveExtensionClass() {
// 组装java代码
String code = createAdaptiveExtensionClassCode();
ClassLoader classLoader = findClassLoader();
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
// 编译出来
return compiler.compile(code, classLoader);
} 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.set(classes);
}
}
}
return classes;
} 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;
}

以下方法都会触发loadFile,这个方法就会读取文件,解析出各个插件的class。有@Adaptive注解的会放入赋值给cachedAdaptiveClass。

下面以Transporter接口为例进行说明自动生成的代码:

@SPI("netty")
public interface Transporter {
@Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
Server bind(URL url, ChannelHandler handler) throws RemotingException; @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}

动态生成类:

public class Transporter$Adaptive implements Transporter {

    public Server bind(URL arg0, ChannelHandler arg1) throws RemotingException {
URL url = arg0;
String extName = url.getParameter("server",
url.getParameter("transporter", "netty"));
Transporter extension = (Transporter) ExtensionLoader
.getExtensionLoader(Transporter.class).getExtension(extName);
return extension.bind(arg0, arg1);
} }

Adaptive机制是一个很好的设计,很好的解决多方案实现的适配问题,如果你遇到类似的代码架构的时候,多想一下更加有扩展性的设计。