MITK微服务机制全景分析
CppMicroServices 微服务机制实现原理
基于
Modules/CppMicroServices/core/src/源码逐层分析
一、定位与基础思想
CppMicroServices 是 C++ 版的OSGi 微服务规范实现(Apache License 2.0,独立开源项目)。
核心思想:
模块 A 注册一个接口的实现 → 模块 B 只凭接口类型查询 → 双方零编译依赖
与 CTK 插件框架完全无关——core/目录下没有任何#include <ctk...>引用,
可在纯命令行程序中独立运行。
二、核心数据结构:CoreModuleContext
所有状态集中在一个全局单例CoreModuleContext(usCoreModuleContext_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::Get(usServiceRegistry.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。
消费方可用ServiceTracker(usServiceTracker.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::Stop(usModule.cpp: L173)→ModulePrivate::RemoveModuleResources(usModulePrivate.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);}模块卸载时无需手动清理——框架自动注销该模块的全部服务和监听器。
八、完整流程时序图
九、设计要点小结
| 设计点 | 实现方式 |
|---|---|
| 零编译依赖 | 消费方只#include接口头文件,不#include实现头文件 |
| 自动注册 | US_INITIALIZE_MODULE宏生成静态对象,.so 加载即注册 |
| 全局单例上下文 | US_GLOBAL_STATIC(CoreModuleContext)进程级唯一 |
| 服务按 ranking 排序 | classServices中lower_bound有序插入,Get返回back() |
| 线程安全 | ServiceRegistry::mutex(MutexLock)保护所有读写 |
| 事件驱动 | 注册/注销自动触发ServiceEvent,ServiceTracker响应 |
| 自动清理 | 模块卸载时RemoveModuleResources自动注销服务和监听器 |
| LDAP 过滤 | GetServiceReferences(clazz, filter)支持属性表达式查询 |
| ServiceFactory | 注册时检测org.cppmicroservices.factorykey,支持按需创建实例 |
| 与 CTK 零关系 | core/目录无任何 CTK 头文件引用,可脱离插件框架独立运行 |
关键源码文件索引
| 功能 | 文件(Modules/CppMicroServices/core/src/下) | 关键行 |
|---|---|---|
| 全局上下文 | module/usCoreModuleContext_p.h | 全文 |
| 自动注册宏 | ../include/usModuleInitialization.h | L57-120 |
| 模块注册表 | module/usModuleRegistry.cpp | L39-74(数据结构);L75(Register) |
| 模块生命周期 | module/usModule.cpp | L119(Start);L173(Stop) |
| 模块资源清理 | module/usModulePrivate.cpp | L118-146 |
| 模块上下文 API | module/usModuleContext.cpp | L78(RegisterService);L98(GetServiceReference) |
| 服务注册表结构 | service/usServiceRegistry_p.h | L63-80 |
| 服务注册实现 | service/usServiceRegistry.cpp | L87(RegisterService);L167(Get) |
| LDAP 过滤器 | module/usLDAPExpr.cpp | - |
| 服务跟踪器 | ../include/usServiceTracker.h | L231 |
附录: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 | 绑定到动态库加载/卸载(静态初始化) |
| 注册 API | context->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::服务仍然存在,造成状态不一致 IExtensionRegistry、IDataStorageService这类框架级服务需要在特定插件激活后才有意义,用us::无法表达"插件级"的生命周期语义
结论:生命周期语义对不上,框架级服务会出现"服务存在但插件未激活"的问题。
正确结论:两套分工是最优设计
Modules 层 → us:: 服务 理由:零依赖、命令行可用、生命周期 = .so 生命周期 Plugins 层 → CTK 服务 理由:生命周期 = 插件 Start/Stop、框架级服务语义清晰在 Plugins 层统一用 CTK 服务(因为 Plugins 层本来就依赖 CTK),
同时保留 Modules 层的us::不动——这正是 MITK 现在的做法,已经是最优分工。
