mc::app service 应用服务框架
Header:
#include <mc/app.h>Namespace:mc::app
概述
mc::app::service 是应用层服务框架的核心类,继承自引擎层 mc::engine::service,在其基础上增加了面向业务的完整生命周期管理、D-Bus 传输自动搭建、MicroComponent 标准 D-Bus 对象、IPMI 命令注册以及 service_context 驱动的配置注入。
mc::app::service 的核心职责是在 openUBMC 架构中作为“微组件”宿主——每个微组件对应一个服务实例,框架通过 service_registry 管理服务类型注册与工厂创建,通过 service_plan / service_definition 描述部署拓扑,并通过 dependency_tracker 管理服务间的启动依赖顺序。
与引擎层 mc::engine::service 的核心差异在于:
| 维度 | mc::engine::service |
mc::app::service |
|---|---|---|
| 生命周期 | init → start → stop → cleanup,无显式状态机 |
configure → start → stop,7 态显式状态机 |
| 传输层 | 依赖外部注入 service_proto/service_backend |
内置 D-Bus connection,configure 阶段自动搭建 |
| 路径 | 无 path 概念 | 持有 m_path(D-Bus 对象路径) |
| 扩展能力 | 无 | 持有 micro_component_object、remote_persist_bridge、task::TaskMgmt、IPMI 命令注册 |
| 配置注入 | init(dict) 直接传参 |
configure(service_context, dict) 注入完整上下文 |
主要特性
- 7 态显式生命周期:
created → configured → starting → running → stopping → stopped → failed,框架严格校验状态迁移 - 生命周期钩子:
on_start()和on_stop()为虚函数(默认实现返回true),子类按需覆盖 - D-Bus 传输自动管理:
configure阶段自动调用bring_up_dbus_transport(),stop阶段调用tear_down_dbus_transport() - MicroComponent 标准对象:每个服务自动挂载
micro_component_object,对外暴露健康检查、配置管理、调试、重启/重置等标准 D-Bus 接口 - IPMI 命令注册:通过
register_ipmi_cmd/unregister_ipmi_cmd动态添加/移除 IPMI 命令处理 - 跨服务查找:通过
service_context::find_service()在同应用内按名称查找其他服务实例 - 类型安全注册:
service_registry模板方法register_service<ServiceType>()自动推导工厂、校验器和配置描述符 - 全局静态注册宏:
MC_APP_REGISTER_GLOBAL_SERVICE(ServiceType, "TypeName")编译期注册,无需显式调用 - 依赖跟踪:
dependency_tracker提供required/optional依赖记账与就绪回调调度,支持拓扑排序
基本用法
示例 1:定义自定义服务
继承 mc::app::service,按需覆盖 on_start() 和 on_stop():
#include <mc/app.h>
#include <iostream>
class SensorService : public mc::app::service {
public:
explicit SensorService(mc::string name) : mc::app::service(std::move(name)) {}
protected:
bool on_start() override {
// 初始化硬件、注册 D-Bus 对象等
std::cout << "SensorService[" << name() << "] started, path="
<< context().definition().path << std::endl;
return true;
}
bool on_stop() override {
// 清理资源、解注册 D-Bus 对象等
std::cout << "SensorService[" << name() << "] stopped" << std::endl;
return true;
}
bool on_configure() override {
// 可选:校验配置属性
const auto& props = properties();
if (!props.contains("poll_interval_ms")) {
return false;
}
return true;
}
};
// 静态全局注册,使框架能通过类型名 "SensorService" 创建实例
MC_APP_REGISTER_GLOBAL_SERVICE(SensorService, "SensorService")
示例 2:通过 service_plan 配置并启动服务
#include <mc/app.h>
#include <iostream>
int main()
{
mc::app::service_plan plan;
plan.application.name = "bmc-app";
mc::app::service_definition sensor_def;
sensor_def.name = "sensor";
sensor_def.path = "/bmc/kepler/sensor";
sensor_def.type = "SensorService";
sensor_def.properties = mc::dict{{"poll_interval_ms", int32_t{500}}};
plan.services.push_back(sensor_def);
mc::app::service_registry registry;
registry.import_global_services();
for (const auto& def : plan.services) {
if (!def.enabled) continue;
auto svc = registry.create_service(def.type, def.name);
if (!svc) continue;
mc::app::service_context ctx(/* application& */ /* ... */, plan, def);
if (!svc->configure(ctx, def.properties)) continue;
if (!svc->start()) continue;
std::cout << "Service " << def.name << " state: "
<< static_cast<int>(svc->state()) << std::endl;
}
return 0;
}
示例 3:注册 IPMI 命令
#include <mc/app.h>
#include <iostream>
class IpmiService : public mc::app::service {
public:
explicit IpmiService(mc::string name) : mc::app::service(std::move(name)) {}
protected:
bool on_start() override {
mc::app::ipmi_cmd cmd{
.netfn = 0x30, .cmd = 0x96, .name = "GetCustomInfo",
.decode = "b", .encode = "{s:s, s:i}",
};
register_ipmi_cmd(cmd, [](mc::dict request, mc::dict ipmi_ctx) -> mc::dict {
mc::dict response;
response["status"] = mc::string("ok");
response["value"] = int32_t{42};
return response;
});
return true;
}
bool on_stop() override { return true; }
};
MC_APP_REGISTER_GLOBAL_SERVICE(IpmiService, "IpmiService")
示例 4:使用 dependency_tracker 管理服务启动依赖
#include <mc/app/dependency_tracker.h>
#include <mc/app.h>
#include <iostream>
class DependentService : public mc::app::service {
public:
explicit DependentService(mc::string name) : mc::app::service(std::move(name)) {}
protected:
bool on_start() override {
auto entry_id = m_tracker.add_entry(
{"/bmc/kepler/sensor"}, {},
[this](const std::vector<mc::string>&) {
std::cout << "Dependency satisfied" << std::endl;
});
std::cout << "Entry registered, id=" << entry_id << std::endl;
return true;
}
bool on_stop() override { return true; }
void on_dependency_ready(mc::string_view ref) {
m_tracker.mark_satisfied(ref);
}
private:
mc::app::dependency_tracker m_tracker;
};
MC_APP_REGISTER_GLOBAL_SERVICE(DependentService, "DependentService")
API 详细说明
service 类
// service.h:38
enum class service_state { created, configured, starting, running, stopping, stopped, failed };
// service.h:40
class MC_API service : public mc::engine::service {
public:
static constexpr mc::string_view bus_name_prefix() noexcept; // "bmc.kepler."
static mc::string_view to_short_name(mc::string_view) noexcept;
mc::string_view short_name() const noexcept;
explicit service(mc::string name);
~service() override;
// 禁止拷贝/移动
service(const service&) = delete;
service& operator=(const service&) = delete;
service(service&&) = delete;
service& operator=(service&&) = delete;
// ...
};
using service_ptr = mc::shared_ptr<service>;
| 方法 | 描述 | 异常 |
|---|---|---|
explicit service(mc::string name) |
构造服务,初始状态为 created,name 作为服务标识。允许空 name(两阶段构造、后续定名,进入 start() 时才要求合法);非空但非法的 D-Bus 名抛异常 |
mc::exception(名称非法);std::bad_alloc |
static constexpr mc::string_view bus_name_prefix() noexcept |
app 层 D-Bus 服务名前缀 "bmc.kepler."(命名空间契约,集中维护) |
不抛出 |
static mc::string_view to_short_name(mc::string_view full_name) noexcept |
从完整 D-Bus 名提取组件短名:"bmc.kepler.network_adapter" → "network_adapter";未带前缀或空串原样返回 |
不抛出 |
mc::string_view short_name() const noexcept |
等价 to_short_name(name()),用于 hwdiscovery owner、日志模块名等以组件名为粒度的场景 |
不抛出 |
~service() |
析构,清理 D-Bus 传输和微组件对象 | 不抛出 |
const mc::string& path() const noexcept |
返回 D-Bus 对象路径,由 set_path 或 configure 设置 |
不抛出 |
service_state state() const noexcept |
返回当前生命周期状态 | 不抛出 |
const mc::dict& properties() const noexcept |
返回 configure 时注入的配置属性 |
不抛出 |
bool configure(const service_context& context, mc::dict properties) |
注入服务上下文和配置属性,自动搭建 D-Bus 传输;状态从 created → configured。context 为空(operator bool() == false)时失败 |
std::bad_alloc(D-Bus 连接创建失败) |
bool start() |
启动服务,依次调用 bring_up_dbus_transport()(name() 非空时)和 on_start();状态 configured → starting → running。非 configured 状态调用返回 false |
on_start() 中异常向外传播;D-Bus 传输搭建失败时状态进入 failed |
bool stop() |
停止服务,调用 tear_down_dbus_transport() 和 on_stop();状态 running → stopping → stopped。非 running 状态调用返回 false |
on_stop() 中异常向外传播 |
void set_path(mc::string path) |
设置 D-Bus 对象路径;通常在 configure 前由框架调用 |
std::bad_alloc(字符串内部分配失败) |
mc::dbus::connection& connection() noexcept |
获取 D-Bus 连接引用;仅在 D-Bus 传输搭建后有效 | 不抛出;D-Bus 未搭建时行为未定义 |
app_proto* proto() noexcept |
获取协议对象指针 | 不抛出 |
bool has_dbus() const noexcept |
检查 D-Bus 传输是否已建立 | 不抛出 |
micro_component_object* micro_component() noexcept |
获取微组件对象(可变指针) | 不抛出 |
const micro_component_object* micro_component() const noexcept |
获取微组件对象(只读指针) | 不抛出 |
void set_remote_persist_bridge(std::unique_ptr<remote_persist_bridge> bridge) |
注入远程持久化桥接对象;同一 class 的对象共享不可变持久化配置 | std::bad_alloc(移动赋值内部分配失败) |
remote_persist_bridge* get_remote_persist_bridge() noexcept |
获取远程持久化桥接对象指针 | 不抛出 |
task::TaskMgmt& task_mgmt() |
获取本 service 的任务管理组件(惰性创建,生命周期随 service,stop() 时连同全部任务释放);详见 task.md |
std::bad_alloc(首次创建分配失败) |
void register_ipmi_cmd(const ipmi_cmd& cmd, ipmi_process_handler handler) |
注册 IPMI 命令:创建 ipmi_cmd_object 并注册到服务对象树 |
std::bad_alloc(对象创建失败);命令已存在时行为取决于实现 |
void unregister_ipmi_cmd(const ipmi_cmd& cmd) |
注销 IPMI 命令:从对象树移除对应 ipmi_cmd_object |
命令不存在时静默返回 |
受保护方法(子类钩子):
| 方法 | 描述 | 异常 |
|---|---|---|
virtual bool on_init(mc::dict properties) override |
初始化钩子,先调用引擎层 on_init 再进行 D-Bus 传输搭建。子类通常无需覆盖 |
引擎层初始化或 D-Bus 搭建失败时返回 false |
virtual bool on_configure() |
配置校验钩子,在 configure() 末尾调用。默认返回 true。子类覆盖用于校验 properties() 的完整性 |
校验失败返回 false 会导致 configure() 失败 |
virtual bool on_start() override |
服务启动钩子,默认实现返回 true。子类覆盖执行启动逻辑(初始化硬件、注册对象等) |
异常向外传播,导致 start() 失败 |
virtual bool on_stop() override |
服务停止钩子,默认实现返回 true。子类覆盖执行停止逻辑(清理资源、解注册对象等) |
异常向外传播,导致 stop() 失败 |
const service_context& context() const |
获取服务上下文引用;仅在 configure() 成功调用后有效 |
configure() 未调用时行为未定义 |
void set_state(service_state state) noexcept |
子类内部状态迁移;通常由 start()/stop() 自动管理,子类仅在异常路径使用 |
不抛出 |
service_context 类
// service_context.h:29
class MC_API service_context {
public:
service_context() = default;
service_context(application& app, const service_plan& plan,
const service_definition& definition);
explicit operator bool() const noexcept;
// ...
};
service_context 封装服务运行时依赖:所属 application、service_plan 和 service_definition。通过 operator bool() 校验完整性。
| 方法 | 描述 | 异常 |
|---|---|---|
service_context() |
默认构造空上下文,operator bool() 返回 false |
不抛出 |
service_context(application&, const service_plan&, const service_definition&) |
完整上下文构造,绑定三项依赖 | 不抛出 |
explicit operator bool() const noexcept |
当 m_app、m_plan、m_definition 均为非空时返回 true |
不抛出 |
application& app() const |
获取所属 application 引用 |
上下文为空时行为未定义 |
mc::runtime::runtime_context& runtime() const |
获取运行时上下文 | 上下文为空时行为未定义 |
const service_plan& plan() const |
获取服务计划 | 上下文为空时行为未定义 |
const application_definition& application_config() const |
获取应用级配置(线程数、模块列表等) | 上下文为空时行为未定义 |
const service_definition& definition() const |
获取当前服务定义 | 上下文为空时行为未定义 |
service_ptr find_service(mc::string_view name) const |
跨服务查找:在同应用内按服务名查找另一服务实例 | 未找到返回 nullptr |
mc::runtime::any_executor get_default_executor() const |
获取默认执行器(基于 application_config 的线程配置) |
std::bad_alloc(执行器创建失败) |
mc::runtime::any_executor create_io_strand() const |
创建 IO 串行执行器(strand) | std::bad_alloc(执行器创建失败) |
mc::runtime::any_executor create_work_strand() const |
创建工作串行执行器(strand) | std::bad_alloc(执行器创建失败) |
void quit(int exit_code = 0) const |
请求整个应用退出 | 不抛出 |
service_registry 类
// service_registry.h:174
class MC_API service_registry {
public:
void register_descriptor(service_descriptor, service_creator,
service_property_validator = {});
template<typename ServiceType>
void register_service(mc::string type_name, bool enabled_by_default = false);
// ...
};
| 方法 | 描述 | 异常 |
|---|---|---|
register_descriptor(descriptor, creator, validator) |
原始注册:手动传入描述符、工厂函数和校验器 | std::bad_alloc(内部 map 插入分配失败) |
register_service<ServiceType>(type_name, enabled_by_default) |
模板注册:从 ServiceType 推导工厂(make_shared<ServiceType>(name))、校验器(反射检查 config_type)和配置描述符(反射推导默认属性和 schema) |
std::bad_alloc;ServiceType 不是 service 的子类时编译失败 |
bool has_service(mc::string_view type_name) const |
检查是否已注册该类型 | 不抛出 |
const service_descriptor* find(mc::string_view type_name) const |
按类型名查找描述符 | 未找到返回 nullptr |
service_ptr create_service(type_name, name) |
创建服务实例,调用对应工厂函数 | std::bad_alloc;类型未注册时返回 nullptr |
bool validate_service_properties(type_name, dict&) |
通过反射校验属性是否匹配 config_type 的字段签名 |
类型注册了 config_type 且属性不匹配时返回 false |
mc::array<mc::string> get_service_types() const |
列出所有已注册服务类型名 | std::bad_alloc(返回数组内部分配失败) |
void import_global_services() |
导入所有通过 MC_APP_REGISTER_GLOBAL_SERVICE 静态注册的服务 |
std::bad_alloc |
全局注册宏:
#define MC_APP_REGISTER_GLOBAL_SERVICE(ServiceType, ServiceName)
在编译单元中声明后,ServiceName 类型的服务在 import_global_services() 调用时自动注册。宏展开为匿名命名空间内的静态初始化 lambda。
关联类型:
| 类型 | 描述 |
|---|---|
service_descriptor |
{ type_name, enabled_by_default, config } — 服务类型元信息 |
service_config_descriptor |
{ version, default_properties, schema } — 配置元信息(反射推导) |
service_creator |
mc::small_function<service_ptr(mc::string), 64> — 工厂函数 |
service_property_validator |
mc::small_function<bool(const mc::dict&), 64> — 属性校验器 |
service_plan / service_definition / application_definition
// service_plan.h
struct application_definition {
mc::string name;
mc::string config_file;
mc::array<mc::string> modules;
std::size_t io_threads{2};
std::size_t work_threads{2};
};
struct rpc_admission_config {
std::size_t max_inflight_per_pool{32};
std::size_t max_queued_per_pool{0};
std::size_t default_budget_ms{0};
std::size_t max_budget_ms{0};
};
struct service_definition {
mc::string name; // 服务实例名称
mc::string path; // D-Bus 对象路径
mc::string type; // 服务类型名(对应 service_registry 中的 type_name)
bool enabled{true};
mc::dict properties; // 服务属性
};
struct service_plan {
const service_definition* find_service(mc::string_view service_name) const;
application_definition application;
mc::array<service_definition> services;
bool has_logging_config{false};
};
升级兼容性:rpc_entry_max_inflight_per_pool 默认值为 32,应用未配置该字段时会为每个实际线程池启用入口准入,
容量耗尽的外部 RPC 将在对应池的有界队列中等待并按预算过期。IO 与 Work 使用独立线程池时,进程总入口额度是两池额度之和;
work_threads 为 0 时 Work 复用 IO 池,不会重复计算。依赖旧版直接派发语义的应用必须显式配置
rpc_entry_max_inflight_per_pool: 0;这会关闭准入保护,同时恢复未配置准入时的派发路径。生产
环境未设置 rpc_entry_max_queued_per_pool,或该值为 0、负数时,按并发额度推导排队上限,默认额度为 512。需要其他上限时再显式配置正整数。
rpc_admission_config 独立于 service_plan,用于编程式调用 initialize_with_plan(plan, config);配置文件中的
同名 rpc_* 键由 mcapp 在启动时解析并放入该独立配置。这样新增准入字段不会扩展既有计划对象布局。
当应用接管一个已经由宿主启动的 mc::runtime::runtime_context 时,宿主拥有线程池和准入配置,
application 计划中的上述字段不会覆盖它;应用只使用宿主提供的运行时。需要应用计划生效时,
应由宿主在 application 启动前完成相同配置,或在 application 启动前停止并重置 runtime,再调用
initialize_with_plan()。
| 方法/字段 | 描述 | 异常 |
|---|---|---|
service_plan::find_service(name) |
线性扫描 services 数组,按名称查找服务定义 |
不抛出;未找到返回 nullptr |
dependency_tracker 类
注意:
dependency_tracker不在mc/app.h中,需单独#include <mc/app/dependency_tracker.h>。
// dependency_tracker.h:35
class MC_API dependency_tracker {
public:
using ready_callback = std::function<void(const std::vector<mc::string>& satisfied_optional)>;
dependency_tracker();
~dependency_tracker();
// 禁止拷贝/移动
dependency_tracker(const dependency_tracker&) = delete;
dependency_tracker& operator=(const dependency_tracker&) = delete;
dependency_tracker(dependency_tracker&&) = delete;
dependency_tracker& operator=(dependency_tracker&&) = delete;
// ...
};
dependency_tracker 提供 required/optional 依赖记账与就绪回调调度。不直接触达 IPC,依赖标记由外部驱动(如 D-Bus InterfacesAdded 信号或对象表 on_object_added)。
| 方法 | 描述 | 异常 |
|---|---|---|
dependency_tracker() |
构造空的依赖跟踪器 | std::bad_alloc(内部实现分配失败) |
~dependency_tracker() |
析构,清理所有未触发的 entry | 不抛出 |
std::uint64_t add_entry(required, optional, callback) |
登记依赖:全部 required 满足后触发 callback(单次调用后自动移除);optional 仅回吐不阻塞。required 为空时同步触发 callback。返回非零 entry id |
std::bad_alloc(内部分配失败) |
void cancel(std::uint64_t entry_id) |
取消尚未触发的 entry | 不抛出;id 无效时静默忽略 |
void mark_satisfied(mc::string_view ref) |
标记引用 ref 已满足,驱动所有相关 entry 的依赖检查 |
不抛出 |
void mark_unsatisfied(mc::string_view ref) |
反向标记:将 ref 重新加入尚未触发 entry 的 required 集合。已触发的 entry 不受影响 |
不抛出 |
std::size_t satisfied_size() const |
当前已满足的 ref 数量快照(诊断/测试用) | 不抛出 |
std::size_t pending_size() const |
当前未触发的 entry 数量快照(诊断/测试用) | 不抛出 |
最佳实践
✅ 使用 MC_APP_REGISTER_GLOBAL_SERVICE 静态注册
在服务实现文件的命名空间作用域使用宏注册,确保 import_global_services() 能自动发现:
// sensor_service.cpp
#include <mc/app.h>
class SensorService : public mc::app::service { /* ... */ };
MC_APP_REGISTER_GLOBAL_SERVICE(SensorService, "SensorService")
优点:无需在 main() 中手动调用 registry.register_service<SensorService>(...),减少启动代码耦合。
✅ 在 on_configure 中校验必需配置项
on_configure() 在 configure() 末尾调用,此时 properties() 已填充。在此校验配置完整性是最佳时机:
class MyService : public mc::app::service {
public:
explicit MyService(mc::string name) : mc::app::service(std::move(name)) {}
protected:
bool on_configure() override {
const auto& props = properties();
if (!props.contains("interval_ms")) {
return false; // configure() 将返回 false
}
if (props["interval_ms"].as<int32_t>() <= 0) {
return false;
}
return true;
}
bool on_start() override { return true; }
bool on_stop() override { return true; }
};
✅ 通过 service_context 获取执行器而非自行创建线程
使用 context().create_io_strand() / create_work_strand() 创建执行器,享受框架统一的线程池管理:
bool on_start() override {
auto strand = context().create_work_strand();
mc::runtime::post(strand, [this]() { do_work(); });
return true;
}
❌ 不要在 configure() 之前调用 start()
start() 要求当前状态为 configured。若在 configure() 之前调用,状态检查失败返回 false:
auto svc = mc::make_shared<MyService>("mysvc");
// 错误:状态为 created,start() 返回 false
bool ok = svc->start(); // ok == false
// 正确流程
mc::app::service_context ctx(/* ... */);
svc->configure(ctx, mc::dict{{"interval_ms", int32_t{100}}});
svc->start(); // 状态迁移:configured → starting → running
❌ 不要忘记 on_start() 和 on_stop() 必须返回 bool
on_start() 和 on_stop() 签名返回 bool。返回 false 将导致 start() / stop() 返回 false,且框架不会自动将状态置为 running / stopped:
// 错误:返回 void
bool on_start() override {
init_hardware(); // 假设此函数返回 void
// 忘记 return true; —— 编译警告,运行时未定义
}
// 正确:显式返回
bool on_start() override {
if (!init_hardware()) {
return false;
}
return true;
}
❌ 不要在 on_start() 中执行长时间阻塞操作
on_start() 由框架在主线程或控制线程同步调用,长时间阻塞会延迟整体启动。应将阻塞操作投递到 work strand 异步执行:
// 错误:阻塞启动
bool on_start() override {
std::this_thread::sleep_for(std::chrono::seconds(5)); // 阻塞整个启动流程
return true;
}
// 正确:异步初始化
bool on_start() override {
auto strand = context().create_work_strand();
mc::runtime::post(strand, [this]() {
heavy_init(); // 在 work 线程上执行
});
return true;
}
❌ 不要在服务析构后继续持有 context() 返回的引用
service_context 持有的 application&、service_plan&、service_definition& 均为引用语义,生命周期由调用方管理。服务析构后继续使用这些引用导致悬垂引用:
const mc::app::service_definition* g_def = nullptr;
void bad_capture(mc::app::service& svc) {
g_def = &svc.context().definition(); // 危险:svc 析构后 g_def 悬垂
}