dubbo的扩展点重构

可扩展设计是框架要重点考虑的设计,因为它直接影响到框架的稳定性和功能的扩展,Dubbo扩展点重构、它在扩展性设计上踩过的坑,值得框架设计者借鉴学习。

第一步,微核心,插件式,平等对待第三方

即然要扩展,扩展点的加载方式,首先要统一,微核心+插件式,是比较能达到 OCP 原则的思路。

由一个插件生命周期管理容器,构成微核心,核心不包括任何功能,这样可以确保所有功能都能被替换,并且,框架作者能做到的功能,扩展者也一定要能做到,以保证平等对待第三方,所以,框架自身的功能也要用插件的方式实现,不能有任何硬编码。

通常微核心都会采用 Factory、IoC、OSGi 等方式管理插件生命周期。考虑 Dubbo 的适用面,不想强依赖 Spring 等 IoC 容器。自已造一个小的 IoC 容器,也觉得有点过度设计,所以打算采用最简单的 Factory 方式管理插件。

最终决定采用的是 JDK 标准的 SPI 扩展机制,参见:java.util.ServiceLoader,也就是扩展者在 jar 包的 META-INF/services/ 目录下放置与接口同名的文本文件,内容为接口实现类名,多个实现类名用换行符分隔。比如,需要扩展 Dubbo 的协议,只需在 xxx.jar 中放置文件:META-INF/services/com.alibaba.dubbo.rpc.Protocol,内容为 com.alibaba.xxx.XxxProtocol。Dubbo 通过 ServiceLoader 扫描到所有 Protocol 实现。

并约定所有插件,都必须标注:@Extension("name"),作为加载后的标识性名称,用于配置选择。

第二步,每个扩展点只封装一个变化因子,最大化复用

每个扩展点的实现者,往往都只是关心一件事,现在的扩展点,并没有完全分离。比如:Failover, Route, LoadBalance, Directory 没有完全分开,全由 RoutingInvokerGroup 写死了。

再比如,协议扩展,扩展者可能只是想替换序列化方式,或者只替换传输方式,并且 Remoting 和 Http 也能复用序列化等实现。这样,需为传输方式,客户端实现,服务器端实现,协议头解析,数据序列化,都留出不同扩展点。

拆分后,设计如下:

第三步,全管道式设计,框架自身逻辑,均使用截面拦截实现

现在很多的逻辑,都是放在基类中实现,然后通过模板方法回调子类的实现,包括:local, mock, generic, echo, token, accesslog, monitor, count, limit 等等,可以全部拆分使用 Filter 实现,每个功能都是调用链上的一环。 比如:(基类模板方法)

public abstract AbstractInvoker implements Invoker {  

    public Result invoke(Invocation inv) throws RpcException {
        // 伪代码
        active ++;
        if (active > max)
            wait();  

        doInvoke(inv);  

        active --;
        notify();
    }  

    protected abstract Result doInvoke(Invocation inv) throws RpcException  

}

改成:(链式过滤器)

public abstract LimitFilter implements Filter {  

    public Result invoke(Invoker chain, Invocation inv) throws RpcException {
         // 伪代码
        active ++;
        if (active > max)
            wait();  

        chain.invoke(inv);  

        active --;
        notify();
    }  

}

第四步,最少概念,一致性概念模型

保持尽可能少的概念,有助于理解,对于开放的系统尤其重要。另外,各接口都使用一致的概念模型,能相互指引,并减少模型转换,

比如,Invoker 的方法签名为:

Result invoke(Invocation invocation) throws RpcException;

而 Exporter 的方法签名为:

Object invoke(Method method, Object[] args) throws Throwable;

但它们的作用是一样的,只是一个在客户端,一个在服务器端,却采用了不一样的模型类。

再比如,URL 以字符串传递,不停的解析和拼装,没有一个 URL 模型类, 而 URL 的参数,却时而 Map, 时而 Parameters 类包装,

export(String url)
createExporter(String host, int port, Parameters params);

使用一致模型:

export(URL url)
createExporter(URL url);

再比如,现有的:Invoker, Exporter, InvocationHandler, FilterChain 其实都是 invoke 行为的不同阶段,完全可以抽象掉,统一为 Invoker,减少概念。

第五步,分层,组合式扩展,而不是泛化式扩展

原因参见:谈谈扩充式扩展与增量式扩展

泛化式扩展指:将扩展点逐渐抽象,取所有功能并集,新加功能总是套入并扩充旧功能的概念。

组合式扩展指:将扩展点正交分解,取所有功能交集,新加功能总是基于旧功能之上实现。

上面的设计,不自觉的就将 Dubbo 现有功能都当成了核心功能。上面的概念包含了 Dubbo 现有 RPC 的所有功能,包括:Proxy, Router, Failover, LoadBalance, Subscriber, Publisher, Invoker, Exporter, Filter 等, 但这些都是核心吗?踢掉哪些,RPC 一样可以 Run?而哪些又是不能踢掉的?基于这样考虑,可以将 RPC 分解成两个层次,只是 Protocol 和 Invoker 才是 RPC 的核心。其它,包括 Router, Failover, Loadbalance, Subscriber, Publisher 都不核心,而是 Routing。所以,将 Routing 作为 Rpc 核心的一个扩展,设计如下:

第六步,整理,梳理关系

整理后,设计如下:

http://dubbo.apache.org/books/dubbo-dev-book/principals/extension.html

原文地址:https://www.cnblogs.com/doit8791/p/9219390.html

时间: 2024-11-10 13:28:16

dubbo的扩展点重构的相关文章

容器和应用程序:扩展、重构或重建?

技术领域是不断变化的,因此,任何应用程序都可能在很短时间内面临过时甚至淘汰,更新换代的速度之快给人的感觉越来越强烈,我们如何使传统应用程序保持活力不落伍?工程师想的可能是从头开始重建传统应用程序,这与公司的业务目标和产品时间表通常是相悖的.如果现阶段正在运行的应用程序是正常工作的,这时候你很难找到正当而充分的理由让技术人员花六个月重写应用程序.代码债似乎注定意味着失败. 众所周知,产品开发向来都不是非黑即白那么简单,必须要权衡各方妥协折衷进行,虽然完全重写的可行性不大,但应用程序现代化的长远利益

聊聊Dubbo - Dubbo可扩展机制实战

摘要: 在Dubbo的官网上,Dubbo描述自己是一个高性能的RPC框架.今天我想聊聊Dubbo的另一个很棒的特性, 就是它的可扩展性. 1. Dubbo的扩展机制 在Dubbo的官网上,Dubbo描述自己是一个高性能的RPC框架.今天我想聊聊Dubbo的另一个很棒的特性, 就是它的可扩展性. 如同罗马不是一天建成的,任何系统都一定是从小系统不断发展成为大系统的,想要从一开始就把系统设计的足够完善是不可能的,相反的,我们应该关注当下的需求,然后再不断地对系统进行迭代.在代码层面,要求我们适当的对

聊聊Dubbo - Dubbo可扩展机制源码解析

摘要: 在Dubbo可扩展机制实战中,我们了解了Dubbo扩展机制的一些概念,初探了Dubbo中LoadBalance的实现,并自己实现了一个LoadBalance.是不是觉得Dubbo的扩展机制很不错呀,接下来,我们就深入Dubbo的源码,一睹庐山真面目. 在Dubbo可扩展机制实战中,我们了解了Dubbo扩展机制的一些概念,初探了Dubbo中LoadBalance的实现,并自己实现了一个LoadBalance.是不是觉得Dubbo的扩展机制很不错呀,接下来,我们就深入Dubbo的源码,一睹庐

理解 Dubbo SPI 扩展机制

写在前面 最近接触了 gRPC 体会到虽然众多 RPC 框架各有各的特点但是他们提供的特性和功能有很多的相似之处 , 这就说明他们面对同样的分布式系统带来的问题.从 2016 年左右开始接触到 dubbo ,基本停留在使用的层面,对 dubbo 的设计以及着重要解决的问题都没有系统的研究过,通过对 dubbo 和其他类似 RPC 产品的系统学习 ,学习分布式系统中面临的共同问题以及解决之道. 微内核架构 微内核架构 (Microkernel architecture) 模式也被称为插件架构 (P

Dubbo/jupiterSPI 扩展引用

ProviderTenantService providerResourceService = ExtensionLoader.getExtension(ProviderTenantService.class, <provider_name>);providerResourceService.setApplicationContext(applicationContext);

Dubbo源码分析系列---扩展点加载

扩展点配置: 约定: 在扩展类的jar包内,放置扩展点配置文件:META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔.(摘自dubbo文档) 示例: 假如我现在想使用自己定义的协议Myprotocol,在resources目录下新建META-INF/dubbo/com.alibaba.dubbo.rpc.Protocol目录文件,文件内容定义: myprotocol=com.selrain.MyProtocol 实现类内容: public cla

从ExtensionLoader理解Dubbo扩展机制

Dubbo的扩展机制是怎么实现的?最简单的回答就是@SPI. Dubbo的插件化思路来源于Java SPI. JAVA SPI 机制 SPI的全名为Service Provider Interface. 大多数人可能不了解,因为它是JDK针对插件或者厂商的.java spi机制的思想就是: 我们的系统的抽象模块(接口),往往有很多不同方案的实现.如日志模块,jdbc模块等.而在面向对象的设计里,我们一般都要做模块解耦,面向接口编程.但如果要切换接口的不同实现,就可能需要改动代码.为了实现模块的自

Dubbo源码分析系列-扩展机制的实现

Spring可扩展Schema 像标签dubbo:monitor.dubbo:service.dubbo:provider等怎么读的,读完之后怎么又是怎么解析的呢? 以上标签都是基于Spring可扩展Schema提供的自定义配置 下面举个例子 1)创建JavaBean 首先设计好配置项,通过JavaBean来建模,如类People public class People { private String name; private Integer age; } 2)编写XSD文件 XSD文件是X

Dubbo中SPI扩展机制解析

dubbo的SPI机制类似与Java的SPI,Java的SPI会一次性的实例化所有扩展点的实现,有点显得浪费资源. dubbo的扩展机制可以方便的获取某一个想要的扩展实现,每个实现都有自己的name,可以通过name找到具体的实现. 每个扩展点都有一个@Adaptive实例,用来注入到依赖这个扩展点的某些类中,运行时通过url参数去动态判断最终选择哪个Extension实例用. dubbo的SPI扩展机制增加了对扩展点自动装配(类似IOC)和自动包装(类似AOP)的支持. 标注了@Activat