写点什么

OSGi 中该使用 Blueprint 还是声明式服务?

  • 2013-12-04
  • 本文字数:2643 字

    阅读完需:约 9 分钟

在 OSGi 中,服务是实现 bundle 间交互和应用灵活性的基石。借助于服务,我们能够降低 bundle 之间的耦合,更加有利于软件的重用,通过强调面向接口编程,可以提高软件的灵活性与设计水平。

传统方式下,我们注册服务都是在 bundle 的激活器(Activator)中使用 BundleContext.registerService() 方法完成的。而服务的获取需要通过 BundleContext.getServiceReference() 获取 ServiceReference 实例,进而使用 BundleContext.getService() 得到真正的服务实例。这种方式虽然能够完成服务的发布与使用,但是有一定的不足,具体来讲:

  1. 产生较多的样板式代码。OSGi 的 bundle 是动态化的,伴随着 bundle 的安装和卸载,它所发布的服务也会动态地处于可用或不可用的状态,因此每次使用服务的时候,我们都需要借助 BundleContext 对象去服务注册中心查找,而不能通过一次查找,一劳永逸地持有服务对象的引用。尽管有 ServiceListener 和 ServiceTracker 帮助我们监听和跟踪服务的状态,但是总体而言这种方式较为繁琐且容易出错。
  2. 影响启动时间,服务在激活器中注册时,需要实例化所有要发布的服务对象,因为激活器的 start() 方法是同步调用的,所以会影响到整个应用的启动时间。
  3. 加大内存的占用,在激活器中注册服务时,我们需要实例化所有的服务对象,但是这些服务在应用运行期间,并不一定会用到,这在无形中加大了内存的占用。
  4. API 依赖引起的平台侵入性。使用传统方式注册和使用服务,会用到大量的 OSGi API,从而产生与 OSGi 平台的耦合,如果要将代码复用到非 OSGi 场景之中,需要较多的重构工作。

OSGi 通过声明式服务(Declarative Service)以及 Blueprint 规范来解决这些问题。声明式服务基于组件模型理论,最早出现在 R4 compendium 规范之中,而 Blueprint 规范来源于 Spring Dynamic Modules 项目,最早出现于 R4.2 企业规范之中。

这两种方式的实现原理与适用场景均有所不同,最近来自 Redhat 的首席软件工程师 Ioannis Canellos撰文对此进行了分析

Blueprint 是针对 OSGi 的依赖注入解决方案,用法非常类似 Spring。当使用服务的时候,Blueprint 会马上创建并注入一个代理(Proxy)。对这些服务进行调用时,如果服务在当前不可用的话,将会产生阻塞,直至能够获取到服务或超时。

声明式服务的处理方式有着较大的差异。声明式服务是一种组件模型,它简化了组件的创建过程,这些组件会发布和使用 OSGi 服务。Ioannis 并没有将声明式服务视为依赖注入的解决方案,而是将其视为具备依赖管理功能的组件模型。我们需要以声明的方式定义组件及其依赖,框架会基于依赖的满足情况来管理组件的生命周期。这意味着,只有组件的依赖完全满足的时候,才会处于激活(activated)状态,一旦依赖出现了缺失,组件就会处于停用(deactivated)状态。因此,声明式服务没有使用代理,但是能保证只要组件处于激活的状态,它的内部依赖就是已满足的。

从上面的介绍可以看出,两种方式的最大区别在于 Blueprint 采用了代理的方式,而声明式服务采用的是级联的方式(cascading),也就是激活或停用组件基于依赖是否能够满足。Ioannis 更倾向于级联的方式,因为代理的方式无法保证底层对象的状态以及可用性。级联的方式能够更好地处理 OSGi 框架的动态化特性。

在使用代理方式时,如果服务对象在运行期不存在了,将会导致错误。另外一个问题在于即便服务的依赖还没有得到满足,也是可以发布服务的。而调用时,将会导致挂起,代理会等待未满足的依赖,这个过程会一直持续,直到依赖满足或超时为止。

Ioannis 在文章中还举了一个现实中的例子来阐述这一过程。如下图:

此时应用由四部分组成,即展现层、Item Service、DataStore 以及数据库。在 OSGi 中展现层可以使用基于 HttpService 注册的 servlet,Item Service 为封装了逻辑的 OSGi 服务,而 DataStore 是用来与数据库交互的 OSGi 服务。Web 应用依赖于 Item Service,而 Item Service 又依赖于 DataStore。

当 DataStore 没有配置或不可用时,代理方式和级联方式分别会发生什么呢?在代理模式下,Item Service 将会被注入 DataStore 的代理。即便没有可用的 DataStore,Item Service 也会被注册到服务注册中心,发送到 Web 应用的请求将会阻塞,等待可用的 DataStore。而在声明式服务的级联场景下,情况会截然不同,Item Service 只有在 DataStore 存在的时候才会注册为服务,同样,只有 Item Service 可用时,Web 应用才会处于可用的状态。所以我们能够保证当 Web 应用可用的时候,它的依赖层级都是满足的。当 DataStore 可用的时候,Item Service 和 Web 应用会自动探测到这种变化,并使自身处于可用的状态。

总之,声明式服务是很强大的依赖管理工具,级联的方式对于构建健壮的动态化、模块化应用是很有价值的;而 Blueprint 简单易用,尤其是对于熟悉 Spring 的开发人员来说更是如此。Ioannis 认为当构建的组件没有服务依赖时或不会将自身导出为服务时,Blueprint 方式很适合;而在其他的情况下,“等待服务”的方案更为合适,如 shell 命令或 camel routes,因为在这里会有很长的依赖链,组件又是高度动态化的,声明式服务更好一些。

OSGi 的官方站点的介绍中,声明式服务、Blueprint 以及 Apache iPOJO,均被归类为组件模型。按照《OSGi 实战》一书的作者们看来,这两种组件模型的适用场景可以归结为:

  1. 声明式服务主要用于创建可快速启动的轻量级组件;
  2. Blueprint 主要用于创建高度可配置的企业级应用。

Blueprint 阻塞机制的一个好处在于能够应对在 bundle 更新期间服务取消和发布对框架的影响。除此之外,因为 Blueprint 方式使用了代理机制,因此服务必须要以接口的方式发布。

除了官方的两种组件模型外, Apache iPOJO 也是 OSGi 中常见的组件模型。它的实现机制与上面的两种方式又有所不同,iPOJO 也是基于代理的机制,但是会使用字节码生成机制,而不是 Java 的动态代理机制,这样的话,就解除了服务必须要实现接口的限制。另一方面,当服务不可用时,调用线程不会阻塞,而是会使用 null 对象来进行处理,这个 null 对象基于模拟对象模式创建,所有的方法不执行任何操作,根据方法的返回类型生成默认的返回值,如 null、0 或者 false。除此之外,iPOJO 还支持提供默认实现。

根据我们上面的分析,可以看出每种方式都有其优势和适用场景,我们在使用的时候,有必要对内部原理有一定的了解,只有这样,当遇到相关的问题时,才能快速地进行分析和定位。

在 Stackoverflow 上,也有很多关于这两种模式的讨论,感兴趣的读者,可以对这个话题进行进一步的研究。

2013-12-04 06:0510246

评论

发布
暂无评论
发现更多内容

Mike Cohn 解析:产品待办列表梳理

ShineScrum

PO 迭代 计划会议

Alpha系统联结大数据、GPT两大功能,助力律所管理降本增效

科技汇

OmniGen2重磅升级,统一图像生成再进化

智源研究院

图像生成

假如你从7月开始准备Java面试,如何秋招拿下offer?

程序员高级码农

java 程序员‘

AI英语听力APP开发

北京木奇移动技术有限公司

软件外包公司 AI英语学习 AI听力APP

软件测试丨Playwright测试工程师必备技能总结

测试人

软件测试

Taobao Agent Russia丨俄罗斯淘宝代购集运系统搭建指南

tbapi

淘宝代购系统 俄罗斯淘宝代购系统

计算架构,行业AI竞争的下一个分水岭

极客天地

全面加速!华能能源交通公司携手用友,开启现代供应链数智化新征程

用友BIP

CAD一键锁定坐标,图块批量插入快人N步!

在路上

cad 浩辰CAD CAD看图王

国产IT运维监控系统标杆之选:Gartner推荐的嘉为蓝鲸全栈智能可观测中心——IBM Tivoli国产化替代实践

嘉为蓝鲸

AIOPS Gartner 智能运维 可观测 国产IT运维监控系统

MCP客户端与服务端使用教程

测试人

软件测试

AI 背单词 App 的技术架构

北京木奇移动技术有限公司

软件外包公司 AI英语学习 AI背单词

在AI时代,挖掘新需求比实现功能更具挑战性

qife122

开发者工具 需求分析

Spring Boot 插件化开发模式,忒香了!

Geek_e3e86e

Java 编程

混合开发赋能快节奏业务增长:跨端生态构建与敏捷迭代实践

xuyinyin

森马服饰从 Elasticsearch 到阿里云 SelectDB 的架构演进之路

SelectDB

大数据 BI ES Doris 实时数仓架构

去中心化 AI 生态基于DePIN起飞

PowerVerse

AI DePIN

VKProxy新增CORS设置和http响应缓存

八苦-瞿昙

Proxy csharp

MyEMS能源管理系统后台配置-传感器管理

开源能源管理系统

开源 能源管理

ASP.NET Core 防伪令牌系统

qife122

ASP.NET Core 防伪令牌

三角偶极子天线(下)---MIMO系统, 包络相关系数ECC和分集增益DG

思茂信息

cst仿真软件 CST软件 CST Studio Suite

怎么修改网站域名的DNS服务器?

国科云

时序数据库 TDengine × Node-RED:打造开箱即用的工业数据自动化流程

TDengine

tdengine 时序数据库 国产时序数据库 时序数据库tdengine

MyEMS能源管理系统后台配置-数据源管理

开源能源管理系统

开源 能源管理

正品库拍照PWA应用的实现与性能优化|得物技术

得物技术

PWA

干货分享 | 一看就懂的电脑配置代理IP全流程

kookeey代理严选

代理商 住宅ip 海外IP 配置IP 跨境网络

MyEMS能源管理系统后台配置-计量表管理

开源能源管理系统

开源 能源管理

号码生成系统的创新实践:游戏周周乐幸运码设计

vivo互联网技术

redis 架构 后端 高并发 库存

搜索数据建设系列之数据架构重构

百度Geek说

spark 计算引擎 数仓模型设计 图灵

AI英语听力APP的核心功能

北京木奇移动技术有限公司

软件外包公司 AI听力学习 AI英语学习

OSGi中该使用Blueprint还是声明式服务?_Java_张卫滨_InfoQ精选文章