当前位置: 首页 > news >正文

MITK中两种微服务的三层架构实现对比

两种微服务的三层架构实现对比

按 OSGi 规范的三层架构(模块层 / 生命周期层 / 服务层)对比
CppMicroServices(us::)与 CTK PluginFramework 的实现细节。

源码位置:

  • CppMicroServices:D:/MITK/Modules/CppMicroServices/core/src/
  • CTK:D:/MITK-superbuild-2023.12/ep/src/CTK/Libs/PluginFramework/

OSGi 规范定义三层:模块层(Module Layer,打包与依赖)、
生命周期层(Lifecycle Layer,安装/启动/停止)、
服务层(Service Layer,注册/发现)。两套实现对三层的完成度截然不同。


一、模块层(Module Layer)——“模块是什么、依赖怎么声明”

CppMicroServices(us::)CTK
模块单位普通动态库(.dll/.so),无特殊打包CTK 插件(带 META-INF/MANIFEST.MF 的 Qt 插件)
元数据可选manifest.json(版本、autoLoadDir)MANIFEST.MF 强制Plugin-SymbolicNamePlugin-VersionRequire-PluginPlugin-ActivationPolicyctkPluginConstants.h: L132-240
依赖声明没有——模块间依赖靠链接器(编译期)解决Require-Plugin: com.acme.test; plugin-version="[1.0,2.0)"——带版本区间的运行期依赖(L204-209)
依赖解析无解析过程checkRequirePlugin:递归解析 Require 链 + 版本区间匹配
资源系统编译进二进制的资源容器(usModuleResourceContainer资源存SQLitePLUGIN_RESOURCES_TABLE
身份注册ModuleRegistry: name → Module*(自增 id)ctkPlugins: location(URL) → QSharedPointer<ctkPlugin>(SQLite 持久化安装状态)

CTK 依赖解析实现(ctkPluginFrameworkContext.cpp: L269-300

voidctkPluginFrameworkContext::checkRequirePlugin(ctkPluginPrivate*plugin){QListIterator<ctkRequirePlugin*>i(plugin->require);// MANIFEST 里的 Require-Plugin 列表while(i.hasNext()){ctkRequirePlugin*pr=i.next();// 按名字 + 版本区间找候选插件QList<ctkPlugin*>pl=plugins->getPlugins(pr->name,pr->pluginRange);for(...每个候选 p2...){if(tempResolved.contains(p2))ok=p2;// 正在本轮解析中(处理循环依赖)elseif(RESOLVED_FLAGS&p2->state)ok=p2;// 已解析elseif(p2->state==ctkPlugin::INSTALLED)// 递归解析依赖的依赖(tempResolved 集合防环)...}}}

核心差异

us:: 的模块层是退化的——没有版本、没有依赖解析、没有安装概念,
模块 = 已被 OS 加载的库。CTK 实现了完整 OSGi 模块层——
版本区间依赖、解析失败可诊断、安装状态跨进程持久(SQLite)。


二、生命周期层(Lifecycle Layer)——“状态机与钩子”

CTK:完整六态状态机(ctkPlugin.h: L87-103

UNINSTALLED ← INSTALLED → RESOLVED → STARTING → ACTIVE ↑ │ └──── STOPPING ←─────┘
  • INSTALLED→RESOLVEDgetUpdatedStatectkPlugin_p.cpp: L174-198
    触发依赖解析,成功才置 RESOLVED 并广播事件——"装了"和"能用"是两个状态
  • 启动start0()ctkPlugin_p.cpp: L720-790):
ctkPluginException*ctkPluginPrivate::start0(){fwCtx->listeners.emitPluginChanged(ctkPluginEvent(STARTING,...));pluginLoader.load();// QPluginLoader 加载库pluginActivator=qobject_cast<ctkPluginActivator*>(pluginLoader.instance());// Qt 插件根对象即 ActivatorpluginActivator->start(pluginContext.data());// 回调 start()state=ctkPlugin::ACTIVE;...// 异常回滚路径(L780-789):// STOPPING → removePluginResources() → context invalidate → 退回 RESOLVED}
  • 每一步状态迁移都发 ctkPluginEvent(STARTING/STARTED/STOPPING/STOPPED)
  • 支持懒激活Plugin-ActivationPolicy: eager/lazy
  • start 中途被 uninstall 有专门检测(STATECHANGE_ERROR)

Activator 接口(ctkPluginActivator.h: L71-111):

classctkPluginActivator{virtualvoidstart(ctkPluginContext*context)=0;virtualvoidstop(ctkPluginContext*context)=0;};

CppMicroServices:二态(usModule.h: L64,166

未加载 ⇄ 已加载(IsLoaded() 一个 bool)
  • 加载即注册即启动:静态初始化(US_INITIALIZE_MODULE)→
    ModuleRegistry::RegisterModule::Start一气呵成,没有中间可停留的状态
  • Activator 获取用原生符号查找usModule.cpp: L133-135):
std::string activator_func="_us_module_activator_instance_"+d->info.name;void*sym=ModuleUtils::GetSymbol(d->info,activator_func.c_str());// dlsym 等价d->moduleActivator=activatorHook();d->moduleActivator->Load(d->moduleContext);
  • 事件只有 LOADING/LOADED/UNLOADING/UNLOADED 四种,无 RESOLVED 概念
  • 无异常回滚状态机Activator::Load抛异常直接向上传播
    (L152-156 注释明说"语义上视为 noexcept")

核心差异

CTK 的生命周期与 .so 加载解耦——库可以加载着但插件停在 RESOLVED 未激活;
BlueBerry ExtensionRegistry 正是利用这一点:插件 RESOLVED 就解析它的 plugin.xml,
不必 start(懒激活的基础)。us:: 的生命周期与 .so 加载同一
简单但无法表达"就绪未启动"。


三、服务层(Service Layer)——“几乎是同一份代码”

这一层两者都是 Knopflerfish 的完整移植,结构逐字段对应:

机制CppMicroServicesCTK差异
双表存储services+classServicesusServiceRegistry_p.h: L63-80同名双表(ctkServices_p.h: L64-71仅容器(STL vs QHash)
ranking 排序lower_bound有序插入,Getback()
服务本体InterfaceMap = std::map<string, void*>QObject*+QStringList接口名CTK 服务必须是 QObject,用qobject_cast校验接口
使用记账dependents: Map<Module*,int>dependents: QHash<QSharedPointer<ctkPlugin>,int>同构
作用域singleton / module / prototype 三种(SERVICE_SCOPEsingleton + ctkServiceFactoryus:: 多 prototype
事件分发回调接口 + 监听器分桶(objectclass/service.id 两级 cache)Qt 信号槽ctkServiceSlotEntry:receiver+slot 名)事件投递机制不同
LDAP 过滤usLDAPExprctkLDAPExpr同一算法
自动清理RemoveModuleResources:卸载时注销全部服务+归还引用removePluginResources():stop 时同样操作同构
跟踪器us::ServiceTracker(.tpp 模板)ctkServiceTracker同构

核心差异只有两点

  1. CTK 服务绑定 Qt 元对象系统:服务必须是 QObject、事件走信号槽、
    接口用Q_DECLARE_INTERFACE+qobject_cast;us:: 零 Qt 依赖靠模板。
  2. 锁粒度:CTK 部分用 QReadWriteLock,us:: 单 Mutex。

总结:一张表看三层完成度

OSGi 层CppMicroServicesCTK
模块层◐ 退化:无版本/无依赖解析,模块=已加载的库● 完整:MANIFEST + Require-Plugin 版本区间 + 递归解析 + SQLite 持久化
生命周期层◐ 二态(加载/未加载),静态初始化自动走完● 完整六态状态机 + 事件 + 懒激活 + 异常回滚
服务层● 完整(Knopflerfish 移植)● 完整(同一份 Knopflerfish 移植,Qt 化)

这解释了 MITK 的分工为什么合理:

  • Modules 层用 us::——算法库不需要"安装/解析/延迟启动"这些重量级语义,
    加载即用、零依赖最合适
  • Plugins 层用 CTK——UI 插件需要版本依赖、按需激活、
    RESOLVED 阶段读 plugin.xml,非完整生命周期不可

两者服务层同源同构,所以开发者跨层切换时 API 心智模型几乎不变。


关键源码文件索引

CppMicroServices(Modules/CppMicroServices/core/下)

内容文件关键行
模块层模块表src/module/usModuleRegistry.cppL39-73
模块层manifest.json 解析src/module/usModulePrivate.cppL53-68
生命周期层二态判定include/usModule.hL64, L166(IsLoaded)
生命周期层Start/符号查找src/module/usModule.cppL119-171
生命周期层自动注册宏include/usModuleInitialization.hL57-120
服务层服务双表src/service/usServiceRegistry_p.hL63-80
服务层注册私有体/dependentssrc/service/usServiceRegistrationBasePrivate.hL40-115
服务层监听器分桶src/service/usServiceListeners_p.hL43-65

CTK(ep/src/CTK/Libs/PluginFramework/下)

内容文件关键行
模块层MANIFEST 头常量ctkPluginConstants.hL132-240
模块层Require-Plugin 解析ctkPluginFrameworkContext.cppL269-300(checkRequirePlugin)
模块层插件表ctkPlugins_p.hL45-63
模块层SQLite 存储ctkPluginStorageSQL.cppL38-39
生命周期层六态状态机ctkPlugin.hL59-103
生命周期层RESOLVED 判定ctkPlugin_p.cppL174-198(getUpdatedState)
生命周期层start0/QPluginLoader/回滚ctkPlugin_p.cppL720-790
生命周期层Activator 接口ctkPluginActivator.hL71-111
服务层服务双表ctkServices_p.hL40-71
服务层信号槽事件ctkServiceSlotEntry_p.hL45-75

两种微服务的应用实例追踪

本文是《两种微服务的三层架构实现对比》的补充:各取一个真实 MITK 例子,
端到端描述具体实现过程。

  • 例一(CppMicroServices):InteractionEventObserver交互事件观察者
  • 例二(CTK):IDataStorageService数据仓库服务

例一 CppMicroServices:InteractionEventObserver

us:: 微服务在 MITK 里最典型的应用:鼠标交互事件如何广播给"不认识的"观察者(十字光标同步链路中的DisplayActionEventBroadcast正是靠它接入的)。

① 定义接口(Modules/Core)

Modules/Core/include/mitkInteractionEventObserver.h

structInteractionEventObserver{virtualvoidNotify(InteractionEvent*event,boolisHandled)=0;...};

只有头文件,没有任何实现依赖——消费方和提供方都只 include 它。

② 提供方注册服务(构造时)

Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp: L46-48——对象构造时把自己注册进服务表:

mitk::DisplayActionEventBroadcast::DisplayActionEventBroadcast(){us::ServiceProperties props;props["name"]=std::string("DisplayActionEventBroadcast");// 以 InteractionEventObserver 接口注册自身m_ServiceRegistration=us::GetModuleContext()->RegisterService<InteractionEventObserver>(this,props);}

内部发生的事:

  • RegisterServiceServiceRegistry::RegisterServiceusServiceRegistry.cpp: L87
  • 接口名us_service_interface_iid<InteractionEventObserver>()作为 key
    写入classServices
  • 立即广播ServiceEvent::REGISTERED
  • 析构时m_ServiceRegistration.Unregister()自动注销

③ 消费方用 ServiceTracker + LDAP 过滤器查找

Modules/Core/src/Interactions/mitkDispatcher.cpp: L40-49——每个渲染窗口的事件分发器构造时建跟踪器:

// 构造 LDAP 过滤器:只要 InteractionEventObserver 这个接口的服务std::string classInteractionEventObserver="("+us::ServiceConstants::OBJECTCLASS()+"="+us_service_interface_iid<InteractionEventObserver>()+")";// 再按属性过滤:全局的 或 专属本 renderer 的us::LDAPFilterfilter("(&(|"+specificRenderer+anyRenderer+")"+classInteractionEventObserver+")");m_EventObserverTracker=newus::ServiceTracker<InteractionEventObserver>(us::GetModuleContext(),filter);

LDAP 过滤器的实战用法:不只按接口,还按props里的属性筛选——同一接口的多个服务实例可以按属性精确圈定

④ 每次事件分发时动态取服务

mitkDispatcher.cpp: L198-213——每处理一个鼠标/键盘事件:

/* Notify InteractionEventObserver */conststd::vector<us::ServiceReference<InteractionEventObserver>>listEventObserver=m_EventObserverTracker->GetServiceReferences();// 拿当前时刻的全部匹配服务for(autoit:listEventObserver){InteractionEventObserver*observer=m_EventObserverTracker->GetService(it);if(observer&&observer->IsEnabled())observer->Notify(event,eventIsHandled);// 广播}

每次都现查——运行中途新加载一个带观察者的模块(比如测量工具模块注册了自己的观察者),下一个鼠标事件就会自动广播给它,Dispatcher 一行代码不用改

完整链路

DisplayActionEventBroadcast 构造 → RegisterService<InteractionEventObserver>(this) [提供方,Modules/Core] → classServices["...InteractionEventObserver"] += 本实例 ↓ 运行期解耦 QmitkRenderWindow 收到鼠标事件 → Dispatcher::ProcessEvent → ServiceTracker(LDAP过滤).GetServiceReferences() [消费方,互不 include 实现] → observer->Notify(event) → SetCrosshair/Scroll/... → 十字光标同步链路

例二 CTK:IDataStorageService

CTK 服务在 MITK 里最核心的应用:所有 View 如何拿到全局唯一的 DataStorage

① 定义接口(带 Qt 元对象声明)

Plugins/org.mitk.core.services/src/mitkIDataStorageService.h: L29-49

structMITK_CORE_SERVICES_PLUGINIDataStorageService{virtualIDataStorageReference::PointerGetDataStorage()const=0;virtualIDataStorageReference::PointerGetDefaultDataStorage()const=0;...};// CTK 服务必须做 Qt 接口声明——这是与 us:: 的关键差异Q_DECLARE_INTERFACE(mitk::IDataStorageService,"org.mitk.service.IDataStorageService")

② 提供方在插件 Activator::start 里注册

Plugins/org.mitk.core.services/src/internal/mitkPluginActivator.cpp: L90-91——org.mitk.core.services插件被 CTK启动(进入 ACTIVE 状态)时:

voidorg_mitk_core_services_Activator::start(ctkPluginContext*context){dataStorageService.reset(newDataStorageService());// 创建实现context->registerService<mitk::IDataStorageService>(dataStorageService.data());}

对比例一:us:: 的注册发生在对象构造/模块加载时;CTK 的注册发生在插件 start时——三层对比文档里"生命周期绑定点不同"在真实代码中的体现。

CTK 内部用qobject_cast校验实现确实携带Q_DECLARE_INTERFACE声明的接口,然后写入ctkServices的双表。

③ 消费方用 ctkServiceTracker

Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cpp

// L146: 私有类成员——跟踪器ctkServiceTracker<mitk::IDataStorageService*>m_DataStorageServiceTracker;// L51-57: 构造时打开跟踪QmitkAbstractViewPrivate(QmitkAbstractView*qq):m_DataStorageServiceTracker(QmitkCommonActivator::GetContext())// 插件上下文{m_DataStorageServiceTracker.open();// 开始跟踪(服务出现/消失自动感知)}// L390-400: 任何 View 调 GetDataStorage() 时mitk::DataStorage::PointerQmitkAbstractView::GetDataStorage()const{mitk::IDataStorageService*dsService=d->m_DataStorageServiceTracker.getService();if(dsService!=nullptr)returndsService->GetDataStorage()->GetDataStorage();returnnullptr;}

④ 自动清理

  • View 析构 →m_DataStorageServiceTracker.close()→ 停止跟踪
  • 插件 stop →removePluginResources()→ 注销服务 + 清理依赖计数
  • 服务注销时,所有持有引用的 View 自动收到serviceRemoved信号,getService()返回 nullptr

完整链路

org.mitk.core.services 插件启动(ACTIVE) → Activator::start() → registerService<IDataStorageService>(dataStorageService) [提供方] → ctkServices["org.mitk.service.IDataStorageService"] += 本实例 ↓ 运行期解耦 QmitkAbstractView 构造 → ctkServiceTracker.open() → 监听服务注册/注销信号 → 任何 View 调 GetDataStorage() → tracker.getService() → DataStorageService::GetDataStorage() → 全局唯一 DataStorage 实例

对比小结

维度CppMicroServices(InteractionEventObserver)CTK(IDataStorageService)
注册时机对象构造时(模块加载即注册)插件 start 时(ACTIVE 状态才注册)
接口声明纯 C++ 结构体,模板元编程Q_DECLARE_INTERFACE+ Qt 元对象
服务发现ServiceTracker+LDAPFilter(属性过滤)ctkServiceTracker(Qt 信号槽监听)
生命周期绑定与模块加载/卸载同步与插件状态机(INSTALLED→RESOLVED→ACTIVE)同步
典型场景跨模块事件广播(鼠标交互)插件间共享单例(全局 DataStorage)

这两个实例完整展示了三层架构对比在 MITK 中的实际应用:us:: 用于模块层的轻量事件广播,CTK 用于插件层的完整生命周期管理 + 服务共享。

http://www.cnnetsun.cn/news/3692279.html

相关文章:

  • 终极指南:如何用UAssetGUI轻松解锁Unreal Engine游戏资产的神秘面纱
  • 如何免费使用Cursor Pro完整功能:简单三步解锁AI编程助手无限潜力
  • 深度学习中的Dropout技术:原理、实现与应用指南
  • 水印不是“擦掉”而是“重写”——图像生成专家拆解Stable Diffusion微调去水印的7个隐藏层参数
  • EasyApplyJobsBot高级技巧:如何设置职位筛选,精准定位理想工作
  • 如何搭建Jellium Desktop播放进度同步服务:自建同步服务完整指南
  • BuildingAI框架:模块化设计与显式控制流实践
  • 【单片机毕业设计推荐】基于 STM32 或 51 单片机的人体生理参数监测报警装置设计与实现,基于 STM32 或 51 单片机的心率血氧体温采集与语音播报系统设计(024103)
  • 【单片机毕业设计推荐】基于 STM32/51 单片机的智能定时药盒系统设计与实现 ,基于 STM32/51 单片机的多分类智能服药提醒装置设计(024203)
  • Qwen-Image-2.0:多模态AI图像生成技术解析与应用
  • Redis-Search实战案例:Ruby China如何用它实现@用户提及自动补全
  • Linux用户组删除报错解决方案与原理详解
  • iOS应用安装终极指南:5分钟学会使用App Installer安装第三方应用
  • 虚拟化环境中的防火墙虚拟机是否也需要双机HA ?
  • MySQL 索引失效场景——EXPLAIN 分析避免踩坑
  • 鸿蒙应用开发从入门到实战(一):鸿蒙应用开发概述
  • 大模型科研能力评估:现状与未来突破方向
  • 基于华为云 FlexusX 四节点集群的云计算全栈实操(二):云虚拟机性能基准(sysbench CPU/内存深度体检)
  • SpringBoot+Vue微服务医疗挂号系统架构实战
  • 掌握Vue列表渲染:VueLearnNotes中的v-for与key属性深度解析
  • 第二章 c语言的基本元素--2.2 关键字
  • 如何在15分钟内免费搭建开源库存管理系统:中小企业物料跟踪终极指南
  • 深入解析LM628/LM629:经典运动控制芯片的原理、应用与PID整定实战
  • 机器学习交易实战:从零到实盘的完整解决方案
  • api2go与数据库集成:如何设计高效的存储层实现
  • Searx API完全手册:如何利用JSON/CSV接口构建自定义搜索应用
  • Maka Agent未来路线图:探索即将推出的7大令人期待的AI助手新功能
  • gh_mirrors/re/rebase常见问题解决:开发者必看的10个故障排除方案
  • 什么是纯净IP? 如何判断IP地址的纯净度?有哪些干净IP推荐?
  • 一种硬件缩放实现方式