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

【Dubbo专题】Dubbo SPI 之扩展点自动包装 Wrapper 类

非普通程序员 2018-09-27
1407

之前的文章我们分析了 Dubbo
的扩展点自适应机制。 DubboSPI
主要思想也是来自于 JDK
原生的 SPI
机制。框架定义好扩展点接口,服务提供者实现接口。框架可以通过 SPI
机制动态的将服务提供商切入到应用中。使我们的程序可以面向接口,对扩展开放。

Dubbo SPI 与 JDK SPI

JDK SPI
需要在 classpath
META-INF/services
目录下创建以扩展点接口全限定名命名的文件,里面内容为实现类的名称(完整包名),多个实现类换行分隔。 文件名称:

JDK加载扩展点实现类的方式:

  1. Iterator<Registry> registryImpls = ServiceLoader.load(Registry.class).iterator();

结论 JDK SPI
会加载 classpath
下的所有 META-INF/services
下的所有接口对应的实现类。如果该实现类在 classpath
存在,则会通过 ServiceLoader
加载出来。如果有多个实现类,多个实现类都会被加载出来,用户则会选择使用哪个。

DubboSPI
改进

  • 不一次性实例化扩展点所有实现,再用到的时候,再选择性加载,可以减少资源浪费。

  • 扩展点加载失败异常跑出更明确的信息

  • 提供扩展点实现类与实现类之间的Wrapper 操作,可以用来聚合公共部分逻辑。

  • 提供 IOC
     和 AOP
     等功能。

Dubbo 扩展点包装 Wrapper 类

一个典型的Wrapper类如下:

  1. public class CommonRegistry implements Registry {

  2.    // 持有 扩展点接口

  3.    private Registry registry;

  4.    // 构造器注入

  5.    public CommonRegistry(Registry registry) {

  6.        this.registry = registry;

  7.    }


  8.    @Override

  9.    public String register(URL url, String msg) {

  10.        // doSomething

  11.       return  registry.register(url,msg);

  12.    }


  13.    @Override

  14.    public String discovery(URL url, String content) {

  15.       // doSomething

  16.       return  registry.register(url,msg);

  17.    }

  18. }

分析上面的包装类,我们得出 Dubbo
认为的包装类需要满足的两个条件

  • 1.持有扩展点接口对象属性,并通过构造器方式初始化该属性

  • 2.这个类也要实现扩展点接口类,并在实现方法中进行增强操作

Wrapper 包装类实战

上一篇文章 [ DubboSPIAdaptive自适应类
] 我们介绍过两个注册中心的实现。这里我们有一个新的需求,需要对每一个注册中心的注册服务和发现服务统计一下耗时,增加一些共有逻辑。所以我们定义一个 Wrapper
类。

  1. public class CommonRegistry implements Registry {

  2.    private Logger logger = LoggerFactory.getLogger(CommonRegistry.class);

  3.    // 持有 扩展点接口

  4.    private Registry registry;

  5.    // 构造器注入

  6.    public CommonRegistry(Registry registry) {

  7.        this.registry = registry;

  8.    }


  9.    @Override

  10.    public String register(URL url, String msg) {

  11.        long begin = System.currentTimeMillis();

  12.        String register = registry.register(url, msg);

  13.        long end = System.currentTimeMillis();

  14.        logger.info("register method 处理耗时 cost: {} ms", end - begin);

  15.        return register;

  16.    }


  17.    @Override

  18.    public String discovery(URL url, String content) {

  19.      //...实现同上

  20.    }

  21. }

META-INF/dubbo/com.maple.spi.Registry
里面增加 wrapper
类的信息:

  1. common=com.maple.spi.impl.CommonRegistry

测试 Main

  1. public static void main(String[] args) {

  2.     URL url = URL.valueOf("test://localhost/test")

  3.                  .addParameter("service", "helloService")

  4.                  .addParameter("registry","etcd");

  5.                  //.addParameter("registry","zookeeper");


  6.     Registry registry = ExtensionLoader.getExtensionLoader(Registry.class)

  7.                                        .getAdaptiveExtension();


  8.     System.out.println(registry.register(url, "maple"););

  9. }

分别尝试使用 etcd
zookeeper
,控制台打印如下:

  1. //Etcd

  2. 09-26 00:55:16 707 main INFO - 服务: helloService 已注册到 Etcd 上,备注: maple

  3. 09-26 00:55:16 709 main INFO - register method 处理耗时 cost: 2 ms

  4. Etcd register already!

  5. // zookeeper

  6. 09-26 00:56:17 282 main INFO - 服务: helloService 已注册到zookeeper上,备注: maple

  7. 09-26 00:56:17 284 main INFO - register method 处理耗时 cost: 2 ms

  8. Zookeeper register already!

我们看到了 registermethod处理耗时cost:2ms
这条日志,说明 CommonRegistry
的逻辑已经运行了。程序先构造 CommonRegistry
,从它的构造器中传入的是扩展点实现类,程序会先调用wrapper类对应的方法, 然后在方法内部再调用扩展点实现类的对应方法。类似于装饰器模式,为扩展点实现类增强了功能。 通过这种设计模式,我们可以将多个扩展点实现共用的公共逻辑都移到此类中来。

Wrapper 类不属于候选的扩展点实现

Wrapper
类不属于扩展点实现,我们可以通过如下代码进行验证:

  1. Set<String> extens = ExtensionLoader

  2.                        .getExtensionLoader(Registry.class)

  3.                        .getSupportedExtensions();

  4. //结果

  5. [etcd, zookeeper]

通过 getSupportedExtensions
可以获取扩展点接口 Registry
当前所有的服务扩展实现的 key
值。控制台的结果只有 etcd
zookeeper
。 因此, wrapper
不属于 扩展点实现,同理 上一篇文章介绍的自适应类 Adaptive
, 也不属于扩展点实现。

总结

通过本文总结 JDK SPI
原理和使用方式,然后和 Dubbo SPI 进行对比。

Dubbo 扩展点自动包装Wrapper类,类似与AOP,为扩展点实现增加更多前置或者后置功能模块。实现原理采用装饰器设计模式,将真正的扩展点实现包装在Wrapper类中。扩展点的Wrapper可以有多个,可以根据需求新增。




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

评论