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
dubbo的扩展点重构的更多相关文章
- 理解 Dubbo SPI 扩展机制
写在前面 最近接触了 gRPC 体会到虽然众多 RPC 框架各有各的特点但是他们提供的特性和功能有很多的相似之处 , 这就说明他们面对同样的分布式系统带来的问题.从 2016 年左右开始接触到 dub ...
- [转] 理解 Dubbo SPI 扩展机制
写在前面 最近接触了 gRPC 体会到虽然众多 RPC 框架各有各的特点但是他们提供的特性和功能有很多的相似之处 , 这就说明他们面对同样的分布式系统带来的问题.从 2016 年左右开始接触到 dub ...
- 聊聊Dubbo - Dubbo可扩展机制实战
1. Dubbo的扩展机制 在Dubbo的官网上,Dubbo描述自己是一个高性能的RPC框架.今天我想聊聊Dubbo的另一个很棒的特性, 就是它的可扩展性. 如同罗马不是一天建成的,任何系统都一定是从 ...
- C# 利用范型与扩展方法重构代码
在一些C#代码中常常可以看到 //An Simple Example By Ray Linn class CarCollection :ICollection { IList list; public ...
- Dubbo/jupiterSPI 扩展引用
ProviderTenantService providerResourceService = ExtensionLoader.getExtension(ProviderTenantService.c ...
- Dubbo源码分析系列---扩展点加载
扩展点配置: 约定: 在扩展类的jar包内,放置扩展点配置文件:META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔.(摘自dubbo文档) 示例: ...
- Dubbo中SPI扩展机制解析
dubbo的SPI机制类似与Java的SPI,Java的SPI会一次性的实例化所有扩展点的实现,有点显得浪费资源. dubbo的扩展机制可以方便的获取某一个想要的扩展实现,每个实现都有自己的name, ...
- Dubbo死磕之扩展点加载ExetnsionLoader
dubbo的SPI机制与JDK的SPI机制对比 dubbo一款阿里一款开源的RPC框架,他本身是一款非常复杂的系统,我们主要针对里边的一些核心点来展开分析,其中duboo里的一种核心机制 ...
- Dubbo学习笔记6:Dubbo增强SPI与SPI中扩展点自动包装的实现原理
在Dubbo整体架构分析中介绍了Dubbo中除了Service和Config层为API外,其他各层均为SPI,为SPI意味着下面各层都是组件化可以被替换的,也就是扩展性比较强,这也是Dubbo比较好的 ...
随机推荐
- jQuery源代码 框架分析
每个框架都有一个核心.全部的结构都是基于这个核心之上,结构建立好了之后,剩下的就是功能的堆砌. jQuery的核心就是从HTML文档中匹配元素并对其操作. 就跟一座大楼一样.让我们一步一步了解这座大厦 ...
- JavaScript读书笔记(6)-Array RegExp
1.Array类型 ECMAScript数组的每一项可以保存任何类型的数据,数组的大小也可以动态调整: (1) 创建数组 第一种方式:Array构造函数 var colors=new Array(); ...
- 手动删除引用nuget如何还原
1.不小心从项目的引用中删除了nuget安装的程序集; 2.从其他地方复制的packages.config到当前项目; 这两种情况 在解决方案中是无法通过还原nuget来还原程序集的,可以通过以下的方 ...
- iOS中用UIWebView的loadHTMLString后图片和文字失调解决方法
iOS中用UIWebView的loadHTMLString后图片和文字失调,图片过大,超过屏幕,文字太小.或者图片太小.文字太大,总之就是不协调. 我们的需求是让图片的大小跟着屏幕的变化而变化.就是动 ...
- WPF之DataGrid篇:DataGridComboBoxColumn
准备数据源 1 准备数据源.基类为Student,数据对象为Student3,数据集为StuList3. END 编辑DataGrid显示列 1 若要填充下拉列表,请首先使用下列选项之一设置 ...
- 获得手机的ip
本文转载至 http://blog.csdn.net/showhilllee/article/details/8746114 iosip手机 貌似ASI里获取ip地址的链接不可以了.也曾试过 ...
- 在调用Response.End()时,会执行Thread.CurrentThread.Abort()操作
在调用Response.End()时,会执行Thread.CurrentThread.Abort()操作. 如果将Response.End()放在try...catch中,catch会捕捉Thread ...
- myeclipse安装tomactserver图解
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/shaozucheng/article/details/36673227 选择标题栏中 Window- ...
- css集合--表示有未读消息小红点的解决
只需要一个<i>标签,放在需要的文本后面即可 ex:<span>待解决问题<i></i><span> i{ display:block; b ...
- python splinter chromedriver下载地址(国内可用)
http://chromedriver.storage.googleapis.com/index.html