feat(core): services.yaml 启动配置 + _run.sh 统一参数透传

Step 1: init.txt → services.yaml
- 新增 InitParser (yaml-cpp), 支持 YAML/TXT 双模式解析, 向后兼容 init.txt
- 新增 services.yaml 声明式服务配置 (name/cmd/args/depends_on/sleep_before)
- 改造 pmc_mtd.cpp 集成 InitParser, 支持目录/.yaml/.txt 三种路径模式
- Makefile 添加 InitParser 编译目标 + -lyaml-cpp 链接
- setup 脚本添加 libyaml-cpp-dev / yaml-cpp-devel 依赖
- core/_run.sh 改为 -i . (目录模式, 自动查找 services.yaml → init.txt)

Step 2: 11 个 _run.sh 统一参数透传
- 8 个脚本改动: 追加 "$@" / 移除 sleep / 修引号
- waf 保留 -c config.yaml 作为脚本默认值
- services.yaml waf args 改为 [] 避免重复传递
- 开发方案文档: docs/reports/core_services_yaml_dev_doc.md
This commit is contained in:
qing committed 2026-08-10 03:16:28 +08:00
1 parent bba7ee3eb4
commit 08df3929b2
18 files changed
+1144 -42

No files matched your search

+1 -1
View File
@@ -149,7 +149,7 @@ int main(int argc, char** argv) {
("list", "list process all run") /* 列出运行中的进程 */
("kill", po::value<int>(), "kill a process") /* 杀死一个进程 */
/********* 子系统服务模式 ********/
("init,i", po::value<std::string>(), "self-init list") /* 输入自启动列表 */
("init,i", po::value<std::string>(), "self-init config (dir or .yaml/.txt file)") /* 输入自启动配置 */
("serve", po::value<int>(), "Run as http server") /* 启用http伺服器 */
("confs", po::value<int>(), "Run embedded confs rpc server"); /* 启用内嵌confs配置服务 */
+51 -21
View File
@@ -5,6 +5,12 @@
#include <boost/property_tree/json_parser.hpp>
#include "main/PPool.hh"
#include "main/pmc_mtd.hh"
#include "registry/InitParser.hh"
#include <unistd.h>
#include <thread>
#include <chrono>
#include <filesystem>
namespace qing {
@@ -87,37 +93,61 @@ catch(std::exception& exp) {
/* 解析自启动列表、添加自启动项
*
* 如果在解析过程中出错,只是打印异常? */
void parse_self_init_list(std::string &path)
* 兼容两种格式:
* - services.yaml(结构化声明,含 args/env/depends_on/sleep_before)
* - init.txt(纯命令列表,每行一条 bash 命令,# 开头为注释)
*
* path 可以是:
* - 目录路径 → 自动查找 services.yaml 或 init.txt
* - .yaml 文件 → 按 YAML 解析
* - 其他文件 → 按 TXT 解析(向后兼容 init.txt)
*
* 如果在解析过程中出错,只是打印异常 */
void parse_self_init_list(std::string &path)
try {
namespace fs = std::filesystem;
std::ifstream file(path);
if (!file.is_open()) { /* ---- 没能打开自启动文件 ---- */
std::cerr << "Failed to open self-init list" << std::endl;
std::vector<registry::ServiceConfig> services;
if (fs::is_directory(path)) {
/* 目录模式:InitParser::load 自动查找 services.yaml → init.txt */
services = registry::InitParser::load(path);
} else if (path.size() > 5 && path.substr(path.size() - 5) == ".yaml") {
/* YAML 文件模式 */
services = registry::InitParser::parseYaml(path);
} else {
/* TXT 文件模式(向后兼容 init.txt) */
services = registry::InitParser::parseTxt(path);
}
if (services.empty()) {
std::cerr << "parse_self_init_list: 没有可启动的服务" << std::endl;
return;
}
std::string row = "";
char ch;
while (file.get(ch))
if (ch == '\n') {
auto pos = row.find("#");
if (pos != -1)
row = row.substr(0, pos);
if (row != "") { /* trim() */
ppool.clr();
ppool.crtp(row); /* 创建进程 */
row = "";
}
for (const auto& svc : services) {
/* 启动前等待(替代散落在 _run.sh 中的 sleep) */
if (svc.sleep_before > 0) {
std::cerr << "[pmc] " << svc.name
<< " 等待 " << svc.sleep_before << "s..." << std::endl;
std::this_thread::sleep_for(
std::chrono::seconds(svc.sleep_before));
}
else row += ch;
/* 设置环境变量 */
for (const auto& [k, v] : svc.env)
setenv(k.c_str(), v.c_str(), 1);
file.close();
/* 清理已停止的进程,然后创建新进程 */
ppool.clr();
std::string full_cmd = svc.toCommand();
std::cerr << "[pmc] 启动: " << svc.name
<< " -> " << full_cmd << std::endl;
ppool.crtp(full_cmd);
}
}
catch (std::exception &exp) {
std::cerr << "Exception at: parse_self_init_list()" << std::endl;
std::cerr << "Exception type: " << typeid(exp).name() << std::endl;
+131
View File
@@ -0,0 +1,131 @@
#include "registry/InitParser.hh"
#include <yaml-cpp/yaml.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <filesystem>
namespace qing::registry {
/* -------- ServiceConfig -------- */
std::string ServiceConfig::toCommand() const {
std::ostringstream oss;
oss << cmd;
for (const auto& a : args) {
oss << ' ' << a;
}
return oss.str();
}
/* -------- InitParser -------- */
std::vector<ServiceConfig> InitParser::load(const std::string& dir) {
namespace fs = std::filesystem;
auto yaml_path = fs::path(dir) / "services.yaml";
auto txt_path = fs::path(dir) / "init.txt";
/* 优先 services.yaml */
if (fs::exists(yaml_path)) {
std::cerr << "[InitParser] 使用 services.yaml: " << yaml_path << std::endl;
return parseYaml(yaml_path.string());
}
/* 回退 init.txt */
if (fs::exists(txt_path)) {
std::cerr << "[InitParser] 回退 init.txt: " << txt_path << std::endl;
return parseTxt(txt_path.string());
}
std::cerr << "[InitParser] 未找到 services.yaml 或 init.txt 于 " << dir << std::endl;
return {};
}
std::vector<ServiceConfig> InitParser::parseYaml(const std::string& path) {
std::vector<ServiceConfig> result;
YAML::Node root;
try {
root = YAML::LoadFile(path);
} catch (const std::exception& e) {
std::cerr << "[InitParser] YAML 解析失败: " << e.what() << std::endl;
return {};
}
if (!root["services"] || !root["services"].IsSequence()) {
std::cerr << "[InitParser] services.yaml 缺少 services 序列" << std::endl;
return {};
}
for (const auto& node : root["services"]) {
ServiceConfig svc;
if (node["name"])
svc.name = node["name"].as<std::string>();
if (node["cmd"])
svc.cmd = node["cmd"].as<std::string>();
if (node["args"] && node["args"].IsSequence())
for (const auto& a : node["args"])
svc.args.push_back(a.as<std::string>());
if (node["env"] && node["env"].IsMap())
for (const auto& kv : node["env"])
svc.env[kv.first.as<std::string>()] = kv.second.as<std::string>();
if (node["depends_on"] && node["depends_on"].IsSequence())
for (const auto& d : node["depends_on"])
svc.depends_on.push_back(d.as<std::string>());
if (node["sleep_before"])
svc.sleep_before = node["sleep_before"].as<int>(0);
if (svc.cmd.empty()) {
std::cerr << "[InitParser] 跳过缺少 cmd 的服务: " << svc.name << std::endl;
continue;
}
result.push_back(std::move(svc));
}
std::cerr << "[InitParser] 解析完成,共 " << result.size() << " 个服务" << std::endl;
return result;
}
std::vector<ServiceConfig> InitParser::parseTxt(const std::string& path) {
std::vector<ServiceConfig> result;
std::ifstream file(path);
if (!file.is_open()) {
std::cerr << "[InitParser] 无法打开 init.txt: " << path << std::endl;
return {};
}
std::string line;
while (std::getline(file, line)) {
/* 去除注释(# 之后的内容) */
auto pos = line.find('#');
if (pos != std::string::npos)
line = line.substr(0, pos);
/* trim 空白 */
auto first = line.find_first_not_of(" \t\r\n");
if (first == std::string::npos)
continue; /* 空行 */
auto last = line.find_last_not_of(" \t\r\n");
line = line.substr(first, last - first + 1);
ServiceConfig svc;
svc.cmd = line;
/* init.txt 模式下 name/args/env/depends_on/sleep_before 均为空/默认 */
result.push_back(std::move(svc));
}
std::cerr << "[InitParser] init.txt 解析完成,共 " << result.size() << " 个服务" << std::endl;
return result;
}
} // namespace qing::registry