MITK微服务机制全景分析

📅 2026/7/21 4:37:24 👁️ 阅读次数 📝 编程学习
MITK微服务机制全景分析

CppMicroServices 微服务机制实现原理

基于Modules/CppMicroServices/core/src/源码逐层分析


一、定位与基础思想

CppMicroServices 是 C++ 版的OSGi 微服务规范实现(Apache License 2.0,独立开源项目)。
核心思想:

模块 A 注册一个接口的实现 → 模块 B 只凭接口类型查询 → 双方零编译依赖

与 CTK 插件框架完全无关——core/目录下没有任何#include <ctk...>引用,
可在纯命令行程序中独立运行。


二、核心数据结构:CoreModuleContext

所有状态集中在一个全局单例CoreModuleContextusCoreModuleContext_p.h):

classCoreModuleContext{public:ServiceListeners listeners;// 所有服务事件监听器ServiceRegistry services;// 所有已注册服务ServiceHooks serviceHooks;// 服务钩子(过滤/拦截)ModuleHooks moduleHooks;// 模块钩子};

ModuleRegistry::coreModuleContext()是进程级静态单例(US_GLOBAL_STATIC),
所有模块共享同一个CoreModuleContext——这是"框架范围"服务可见性的基础。


三、模块生命周期:从 .so 加载到服务注册

1. 自动注册宏(usModuleInitialization.h: L57

每个参与微服务的动态库在源文件末尾放一行:

US_INITIALIZE_MODULE// 展开为一个文件作用域的静态对象

宏展开后生成一个ModuleInitializer_XXX类的静态实例,其构造函数在 .so 被 dlopen 时
由 C++ 运行时自动调用:

// 宏展开核心逻辑(usModuleInitialization.h: L57-120)classModuleInitializer_XXX{public:ModuleInitializer_XXX(){// 1. 通过自身地址获取 .so 的磁盘路径moduleInfoPtr()->location=ModuleUtils::GetLibraryPath(moduleInfoSym);// 2. 向全局 ModuleRegistry 注册ModuleRegistry::Register(moduleInfo());}~ModuleInitializer_XXX(){ModuleRegistry::UnRegister(moduleInfo());// .so 卸载时自动注销}};staticModuleInitializer_XXX _InitializeModule_XXX;// 文件作用域静态对象

关键:不需要任何显式调用——.so 加载即注册,卸载即注销。

2. ModuleRegistry::Register(usModuleRegistry.cpp: L75

// 全局模块表:name → Module*(US_UNORDERED_MAP_TYPE)US_GLOBAL_STATIC_WITH_DELETER(ModuleMap,modules,ModuleDeleter)US_GLOBAL_STATIC(Mutex,modulesLock)// 保护 modules 表voidModuleRegistry::Register(ModuleInfo*info){// 检查是否重载(同 location + name 的模块)// 若是新模块:new Module() → 分配自增 id → 插入 modules 表module->Init(coreModuleContext(),info);module->Start();// 触发 Activator::Load}

3. Module::Start(usModule.cpp: L119

voidModule::Start(){d->moduleContext=newModuleContext(this->d);// 创建该模块的上下文句柄// 通过符号查找 Activator 工厂函数(dlsym 等价)std::string activator_func="_us_module_activator_instance_"+d->info.name;void*activatorHookSym=ModuleUtils::GetSymbol(d->info,activator_func.c_str());// ...d->moduleActivator=activatorHook();// 获取 Activator 实例d->moduleActivator->Load(d->moduleContext);// 回调 Activator::Load// 发出 LOADED 事件d->coreCtx->listeners.ModuleChanged(ModuleEvent(ModuleEvent::LOADED,this));}

US_EXPORT_MODULE_ACTIVATOR(MyActivator)宏生成那个 C 符号函数,
使ModuleUtils::GetSymbol能找到它。

4. Activator::Load —— 服务注册的入口

// 典型 Activator(如 mitkDisplayActionEventBroadcast.cpp: L48)classMyActivator:publicModuleActivator{voidLoad(ModuleContext*ctx)override{// 在这里注册服务ctx->RegisterService<IMyInterface>(newMyImpl(),props);}voidUnload(ModuleContext*ctx)override{/* 服务自动注销 */}};

四、服务注册:ServiceRegistry

核心数据结构(usServiceRegistry_p.h: L63-80

classServiceRegistry{mutableMutexType mutex;// 服务对象 → 它注册的接口名列表MapServiceClasses services;// unordered_map<ServiceRegistration, vector<string>>// 接口名 → 按 ranking 排序的服务注册列表(最高 rank 在前)MapClassServices classServices;// unordered_map<string, vector<ServiceRegistration>>std::vector<ServiceRegistrationBase>serviceRegistrations;// 全量列表};

RegisterService 流程(usServiceRegistry.cpp: L87

ServiceRegistrationBaseServiceRegistry::RegisterService(ModulePrivate*module,constInterfaceMap&service,constServiceProperties&properties){// 1. 检查是否 ServiceFactory(工厂模式,按需创建实例)boolisFactory=service.count("org.cppmicroservices.factory")>0;// 2. 收集接口名列表(InterfaceMap 的 key)std::vector<std::string>classes;for(auto&i:service)classes.push_back(i.first);// 3. 创建 ServiceRegistration(含自增 SERVICE_ID、OBJECTCLASS、SERVICE_SCOPE)ServiceRegistrationBaseres(module,service,CreateServiceProperties(properties,classes,isFactory,...));// 4. 加锁写入两张表MutexLocklock(mutex);services.insert({res,classes});for(auto&cls:classes){auto&s=classServices[cls];s.insert(lower_bound(s,res),res);// 按 ranking 有序插入}// 5. 发出 REGISTERED 事件,通知所有匹配的监听器ServiceEventregisteredEvent(ServiceEvent::REGISTERED,res.GetReference(""));module->coreCtx->listeners.GetMatchingServiceListeners(registeredEvent,listeners);module->coreCtx->listeners.ServiceChanged(listeners,registeredEvent);returnres;}

五、服务查询:ModuleContext → ServiceRegistry

// ModuleContext::RegisterService(usModuleContext.cpp: L78)ServiceRegistrationUModuleContext::RegisterService(constInterfaceMap&service,constServiceProperties&properties){returnd->module->coreCtx->services.RegisterService(d->module,service,properties);}// ModuleContext::GetServiceReference(usModuleContext.cpp: L98)ServiceReferenceUModuleContext::GetServiceReference(conststd::string&clazz){returnd->module->coreCtx->services.Get(d->module,clazz);}

ServiceRegistry::GetusServiceRegistry.cpp: L167):

ServiceReferenceBaseServiceRegistry::Get(ModulePrivate*module,conststd::string&clazz)const{MutexLocklock(mutex);std::vector<ServiceReferenceBase>srs;Get_unlocked(clazz,"",module,srs);// 按接口名查 classServices 表if(!srs.empty())returnsrs.back();// 返回 ranking 最高的(back = 最高 rank)returnServiceReferenceBase();// 未找到返回空引用}

支持LDAP 过滤器usLDAPExpr.cpp):GetServiceReferences(clazz, "(key=value)")可按属性过滤。


六、服务事件与监听:ServiceListeners

注册服务时自动触发ServiceEvent::REGISTERED;注销时触发ServiceEvent::UNREGISTERING

消费方可用ServiceTrackerusServiceTracker.h: L231)自动跟踪:

// 典型用法(如 QmitkAbstractView 中的 DataStorage 服务跟踪)ServiceTracker<IMyInterface>tracker(context);tracker.Open();// 开始跟踪,自动处理 Add/Remove/ModifyIMyInterface*svc=tracker.GetService();// 获取当前最高 rank 服务tracker.Close();

ServiceTracker内部实现了ServiceTrackerCustomizer,三个回调:

  • AddingService:新服务出现时
  • ModifiedService:服务属性变更时
  • RemovedService:服务注销时

七、模块卸载:自动清理

Module::StopusModule.cpp: L173)→ModulePrivate::RemoveModuleResourcesusModulePrivate.cpp: L118):

voidModulePrivate::RemoveModuleResources(){coreCtx->listeners.RemoveAllListeners(moduleContext);// 移除所有监听器// 注销该模块注册的所有服务std::vector<ServiceRegistrationBase>srs;coreCtx->services.GetRegisteredByModule(this,srs);for(auto&sr:srs)sr.Unregister();// 释放该模块使用的所有服务引用coreCtx->services.GetUsedByModule(q,srs);for(auto&sr:srs)sr.GetReference("").d->UngetService(q,false);}

模块卸载时无需手动清理——框架自动注销该模块的全部服务和监听器。


八、完整流程时序图

消费模块ServiceListenersServiceRegistryModuleActivatorModuleModuleRegistry 全局表ModuleInitializer 静态对象操作系统 dlopen消费模块ServiceListenersServiceRegistryModuleActivatorModuleModuleRegistry 全局表ModuleInitializer 静态对象操作系统 dlopen查询服务.so 卸载.so 加载 C++静态初始化Register moduleInfonew Module InitStart 创建 ModuleContextGetSymbol activator_func dlsymactivatorHook 获取实例Load moduleContextRegisterService InterfaceMap properties写入 services 和 classServices 表 加锁ServiceChanged REGISTERED 事件通知 ServiceTracker AddingServiceGetServiceReference clazzclassServices 按接口名查找 返回最高 rankServiceReferenceGetService reference服务对象指针析构函数UnRegisterStopUnloadRemoveModuleResources 自动注销所有服务ServiceChanged UNREGISTERING 事件

九、设计要点小结

设计点实现方式
零编译依赖消费方只#include接口头文件,不#include实现头文件
自动注册US_INITIALIZE_MODULE宏生成静态对象,.so 加载即注册
全局单例上下文US_GLOBAL_STATIC(CoreModuleContext)进程级唯一
服务按 ranking 排序classServiceslower_bound有序插入,Get返回back()
线程安全ServiceRegistry::mutex(MutexLock)保护所有读写
事件驱动注册/注销自动触发ServiceEventServiceTracker响应
自动清理模块卸载时RemoveModuleResources自动注销服务和监听器
LDAP 过滤GetServiceReferences(clazz, filter)支持属性表达式查询
ServiceFactory注册时检测org.cppmicroservices.factorykey,支持按需创建实例
与 CTK 零关系core/目录无任何 CTK 头文件引用,可脱离插件框架独立运行

关键源码文件索引

功能文件(Modules/CppMicroServices/core/src/下)关键行
全局上下文module/usCoreModuleContext_p.h全文
自动注册宏../include/usModuleInitialization.hL57-120
模块注册表module/usModuleRegistry.cppL39-74(数据结构);L75(Register)
模块生命周期module/usModule.cppL119(Start);L173(Stop)
模块资源清理module/usModulePrivate.cppL118-146
模块上下文 APImodule/usModuleContext.cppL78(RegisterService);L98(GetServiceReference)
服务注册表结构service/usServiceRegistry_p.hL63-80
服务注册实现service/usServiceRegistry.cppL87(RegisterService);L167(Get)
LDAP 过滤器module/usLDAPExpr.cpp-
服务跟踪器../include/usServiceTracker.hL231

附录:CTK 也提供微服务机制 —— 两套服务框架对比

一、CTK 确实内置完整的微服务机制

CTK 是 OSGi 规范的完整 C++ 实现,服务机制是其插件框架的内置组成部分。
MITK 中的实际用法(源码实证):

注册服务ctkPluginContext::registerService):

// berryCTKPluginActivator.cpp:256registryServiceReg=context->registerService<IExtensionRegistry>(registry);// berryWorkbenchPlugin.cpp:418context->registerService<berry::IQtStyleManager>(styleManager.data());

跟踪/消费服务ctkServiceTracker):

// QmitkAbstractView.cpp:36,146#include<ctkServiceTracker.h>ctkServiceTracker<mitk::IDataStorageService*>m_DataStorageServiceTracker;

二、CTK 服务 vs CppMicroServices 对比

维度CTK 服务(ctkPluginContext)CppMicroServices(us::)
所在层Plugins 层(BlueBerry/CTK 插件框架内)Modules 层(独立于插件框架)
生命周期绑定绑定到 CTK 插件的 Start/Stop绑定到动态库加载/卸载(静态初始化)
注册 APIcontext->registerService<T>(impl)context->RegisterService<T>(impl)
跟踪器ctkServiceTracker<T>us::ServiceTracker<T>
依赖关系需要 CTK 插件框架运行零外部依赖,命令行程序可用
MITK 中用途框架级服务(IExtensionRegistry、IDataStorageService、IQtStyleManager)算法级服务(IO 读写器、InteractionEventObserver、渲染服务)

三、是否可以统一为一套机制

技术上可行,但不建议——两套机制的存在是有意为之的分层设计。

方案一:全部换成 CTK 服务

代价

  • Modules 层必须引入 CTK 头文件,打破"算法层不依赖插件框架"的分层原则
  • CoreCmdApps(纯命令行工具)无法运行,因为没有 CTK 插件框架
  • 每个 Module 都要变成 CTK 插件(有 plugin.xml、有 Activator),构建复杂度大幅上升

结论:破坏 MITK 最核心的设计原则——算法可脱离 GUI 复用。

方案二:全部换成 CppMicroServices

代价

  • CTK 插件的生命周期(Start/Stop)与us::模块的生命周期(dlopen/dlclose)不对齐——CTK 插件可以在 .so 已加载的情况下被 Stop,此时us::服务仍然存在,造成状态不一致
  • IExtensionRegistryIDataStorageService这类框架级服务需要在特定插件激活后才有意义,用us::无法表达"插件级"的生命周期语义

结论:生命周期语义对不上,框架级服务会出现"服务存在但插件未激活"的问题。

正确结论:两套分工是最优设计

Modules 层 → us:: 服务 理由:零依赖、命令行可用、生命周期 = .so 生命周期 Plugins 层 → CTK 服务 理由:生命周期 = 插件 Start/Stop、框架级服务语义清晰

在 Plugins 层统一用 CTK 服务(因为 Plugins 层本来就依赖 CTK),
同时保留 Modules 层的us::不动——这正是 MITK 现在的做法,已经是最优分工。