MITK微服务机制全景分析
CppMicroServices 微服务机制实现原理
基于
Modules/CppMicroServices/core/src/源码逐层分析
一、定位与基础思想
CppMicroServices 是 C++ 版的 OSGi 微服务规范实现(Apache License 2.0,独立开源项目)。
核心思想:
模块 A 注册一个接口的实现 → 模块 B 只凭接口类型查询 → 双方零编译依赖
与 CTK 插件框架完全无关——core/ 目录下没有任何 #include <ctk...> 引用,
可在纯命令行程序中独立运行。
二、核心数据结构:CoreModuleContext
所有状态集中在一个全局单例 CoreModuleContext(usCoreModuleContext_p.h):
class CoreModuleContext {
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)
class ModuleInitializer_XXX {
public:
ModuleInitializer_XXX() {
// 1. 通过自身地址获取 .so 的磁盘路径
moduleInfoPtr()->location = ModuleUtils::GetLibraryPath(moduleInfoSym);
// 2. 向全局 ModuleRegistry 注册
ModuleRegistry::Register(moduleInfo());
}
~ModuleInitializer_XXX() {
ModuleRegistry::UnRegister(moduleInfo()); // .so 卸载时自动注销
}
};
static ModuleInitializer_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 表
void ModuleRegistry::Register(ModuleInfo* info)
{
// 检查是否重载(同 location + name 的模块)
// 若是新模块:new Module() → 分配自增 id → 插入 modules 表
module->Init(coreModuleContext(), info);
module->Start(); // 触发 Activator::Load
}
3. Module::Start(usModule.cpp: L119)
void Module::Start()
{
d->moduleContext = new ModuleContext(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)
class MyActivator : public ModuleActivator {
void Load(ModuleContext* ctx) override {
// 在这里注册服务
ctx->RegisterService<IMyInterface>(new MyImpl(), props);
}
void Unload(ModuleContext* ctx) override { /* 服务自动注销 */ }
};
四、服务注册:ServiceRegistry
核心数据结构(usServiceRegistry_p.h: L63-80)
class ServiceRegistry {
mutable MutexType 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)
ServiceRegistrationBase ServiceRegistry::RegisterService(
ModulePrivate* module, const InterfaceMap& service, const ServiceProperties& properties)
{
// 1. 检查是否 ServiceFactory(工厂模式,按需创建实例)
bool isFactory = 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)
ServiceRegistrationBase res(module, service,
CreateServiceProperties(properties, classes, isFactory, ...));
// 4. 加锁写入两张表
MutexLock lock(mutex);
services.insert({res, classes});
for (auto& cls : classes) {
auto& s = classServices[cls];
s.insert(lower_bound(s, res), res); // 按 ranking 有序插入
}
// 5. 发出 REGISTERED 事件,通知所有匹配的监听器
ServiceEvent registeredEvent(ServiceEvent::REGISTERED, res.GetReference(""));
module->coreCtx->listeners.GetMatchingServiceListeners(registeredEvent, listeners);
module->coreCtx->listeners.ServiceChanged(listeners, registeredEvent);
return res;
}
五、服务查询:ModuleContext → ServiceRegistry
// ModuleContext::RegisterService(usModuleContext.cpp: L78)
ServiceRegistrationU ModuleContext::RegisterService(const InterfaceMap& service,
const ServiceProperties& properties)
{
return d->module->coreCtx->services.RegisterService(d->module, service, properties);
}
// ModuleContext::GetServiceReference(usModuleContext.cpp: L98)
ServiceReferenceU ModuleContext::GetServiceReference(const std::string& clazz)
{
return d->module->coreCtx->services.Get(d->module, clazz);
}
ServiceRegistry::Get(usServiceRegistry.cpp: L167):
ServiceReferenceBase ServiceRegistry::Get(ModulePrivate* module, const std::string& clazz) const
{
MutexLock lock(mutex);
std::vector<ServiceReferenceBase> srs;
Get_unlocked(clazz, "", module, srs); // 按接口名查 classServices 表
if (!srs.empty()) return srs.back(); // 返回 ranking 最高的(back = 最高 rank)
return ServiceReferenceBase(); // 未找到返回空引用
}
支持 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/Modify
IMyInterface* svc = tracker.GetService(); // 获取当前最高 rank 服务
tracker.Close();
ServiceTracker 内部实现了 ServiceTrackerCustomizer,三个回调:
AddingService:新服务出现时ModifiedService:服务属性变更时RemovedService:服务注销时
七、模块卸载:自动清理
Module::Stop(usModule.cpp: L173)→ ModulePrivate::RemoveModuleResources(usModulePrivate.cpp: L118):
void ModulePrivate::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.factory key,支持按需创建实例 |
| 与 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:256
registryServiceReg = context->registerService<IExtensionRegistry>(registry);
// berryWorkbenchPlugin.cpp:418
context->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 现在的做法,已经是最优分工。
更多推荐
所有评论(0)