暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Dubbo框架基石,SPI扩展点机制

阿斌Java之路 2022-05-21
369

Dubbo良好的扩展性与两个方面是密不可分的,一是整 个框架中针对不同的场景,恰到好处地使用了各种设计模式,二就是本章要介绍的加载机制。基于DubboSPI加载机制,让整个框架的接口和具体实现完全解耦,从而奠定了整个框架良好可扩展性的基础。

dubbo的扩展核心类ExtensionLoader可谓框架的基石。可以看到源码中大量引入的ExtensionLoader相关的扩展机制,了解了扩展机制是阅读后续源码的大前提。

ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();

ExtensionLoader<TelnetHandler> extensionLoader = ExtensionLoader.getExtensionLoader(TelnetHandler.class);

扩展点特性

  • 自动包装Wrapper
  • 自动装配
  • 自适应@Adaptive
  • 自动激活@Activate

先放上一张ExtensionLoader的工作流程图

img

自动包装Wrapper

自动包装扩展点的 Wrapper 类。ExtensionLoader 在加载扩展点时,如果加载到的扩展点有拷贝构造函数,则判定为扩展点 Wrapper 类。

比如dubbo中常用的包装类

filter=org.apache.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper

filter:过滤器调用链包装器;listener:协议监听器包装器;qos:在线运维服务包装器;

通过这些包装类,将listener织入到我们export和reffer的调用链路中,将 filter织入到我们invoke远程调用流程中,具体包装后的链路组装逻辑,可以在之后的文章中阅读源码查看。

public class ProtocolListenerWrapper implements Protocol {

private final Protocol protocol;

public ProtocolListenerWrapper(Protocol protocol) {
if (protocol == null) {
throw new IllegalArgumentException("protocol == null");
}
this.protocol = 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;
}
}

自动装配

除了在构造函数中传入其他扩展实例,我们还经常使用setter 方法设置属性值。如果某个扩展类是另外-一个扩展点类的成员属性,并且拥有setter方法,那么框架也会自动注入对应的扩展点实例。ExtensionLoader在执行扩展点初始化的时候,会自动通过setter方法注入对应的实现类。这里存在-一个问题,如果扩展类属性是一个接口,它有多种实现,那么具体注入哪一个呢?这就涉及第三个特性自适应。

@SPI
public interface CarMaker {
Car makeCar();
}
@SPI
public interface WheelMaker {
Wheel makeWheel();
}
public class RaceCarMaker implements CarMaker {
WheelMaker wheelMaker;

public void setWheelMaker(WheelMaker wheelMaker) {//这里装配了扩展类
this.wheelMaker = wheelMaker;
}

public Car makeCar() {
// ...
Wheel wheel = wheelMaker.makeWheel();//具体使用扩展类哪个实现类的方法
// ...
return new RaceCar(wheel, ...);
}
}

自适应@Adaptive

在Dubbo SPI中,我们使用@Adaptive注解,可以动态地通过URL中的参数来确定要使用哪个具体的实现类。从而解决自动加载中的实例注入问题。

@SPI
public interface Transporter {
@Adaptive({"server", "transport"})
Server bind(URL url, ChannelHandler handler) throws RemotingException;

@Adaptive({"client", "transport"})
Client connect(URL url, ChannelHandler handler) throws RemotingException;
}

对于 bind() 方法,Adaptive 实现先查找 server key,如果该 Key 没有值则找 transport key 值,来决定代理到哪个实际扩展点。

@SPI("dubbo")
public interface Protocol {

@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
}

如果@Adaptive没指定key,默认取类名小写protocol作为key,如果URL中没有protocol参与,那么会取@SPI("dubbo")标注的默认key作为value值dubbo。

这种动态寻找实现类的方式比较灵活,但只能激活-一个具体的实现类,如果需要多个实现类同时被激活,如Filter可以同时有多个过滤器;或者根据不同的条件,同时激活多个实现类,如何实现?这就涉及最后一个特性一自动激活。

自动激活@Activate

对于集合类扩展点,比如:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker 等,可以同时加载多个实现,此时,可以用自动激活来简化配置,如:

import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;

@Activate // 无条件自动激活
public class XxxFilter implements Filter {
// ...
}
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;

@Activate("cache") // 当配置了xxx参数,并且参数为有效值时激活,比如配了cache="lru",自动激活CacheFilter。
public class XxxFilter implements Filter {
// ...
}
public static void main(String[] args) {
ExtensionLoader<Filter> loader = ExtensionLoader.getExtensionLoader(Filter.class);
URL url = new URL("", "", 0);
// 在url中添加cache参数
url = url.addParameter("cache", "cache");
List<Filter> filters = loader.getActivateExtension(url, "cache");
System.out.println(filters);
}
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.Filter;

@Activate(group = "provider", value = "xxx") // 只对提供方激活,group可选"provider"或"consumer"
public class XxxFilter implements Filter {
// ...
}

源码阅读

有了这些前置知识,接下来可以深入查看源码,了解下dubbo是怎么实现这样在运行时动态适应的扩展类的。实际上就是用javassist生成一个新的类,在方法里去做动态判断。

参考资料:

官网扩展点介绍

官网spi源码介绍

官网自适应扩展实现源码

csdn:

《深度解析dubbo扩展技术dubbo spi(注解)》

《深度解析dubbo扩展技术dubbo spi(实现二)》

《深度解析dubbo扩展技术dubbo spi(自适应实现)》

《深度解析dubbo扩展技术dubbo spi(自动激活实现)》





END



后台回复关键词 dubbo 获取今日推荐资料

微信8.0新增了一万的好友数,之前没加上好友的可以加一下我的个人微信,再晚又满了,一起抱团取暖结伴内卷。



扫码拉群,学习打卡,交流经验


每周一读




文章转载自阿斌Java之路,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论