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:
1 parent
bba7ee3eb4
commit
08df3929b2
18 files changed
+1144
-42
No files matched your search
+8
-3
@@ -4,7 +4,7 @@ APP_NAME := nidus
|
||||
|
||||
INSTL_DIRS := /usr/local/bin
|
||||
|
||||
CONF_NAME := init.txt
|
||||
CONF_NAME := services.yaml
|
||||
|
||||
CONF_DIR := /etc/Nidus
|
||||
|
||||
@@ -17,7 +17,7 @@ CPP:= g++
|
||||
INCS:= -I include/
|
||||
|
||||
MY_LIBS := -L/usr/lib -lus_th -lus_http -lus_httpclient -lus_msg -lcurl
|
||||
LIBS := -lpthread -lssl -lcrypto -lboost_program_options -lboost_json -lboost_thread -lboost_filesystem
|
||||
LIBS := -lpthread -lssl -lcrypto -lboost_program_options -lboost_json -lboost_thread -lboost_filesystem -lyaml-cpp
|
||||
|
||||
CPPFLAGS := --std=c++20 -O3 -pipe #-Wall -Werror
|
||||
|
||||
@@ -26,7 +26,7 @@ $(shell mkdir -p $(BUILD_DIR))
|
||||
|
||||
#--------
|
||||
all: ${APP_NAME}.out
|
||||
${APP_NAME}.out: $(BUILD_DIR)/main.o $(BUILD_DIR)/PPool.o $(BUILD_DIR)/ProcessTask.o $(BUILD_DIR)/HttpTask.o $(BUILD_DIR)/ConfsTask.o $(BUILD_DIR)/Tttask.o $(BUILD_DIR)/pmc_mtd.o $(BUILD_DIR)/subsys_call.o
|
||||
${APP_NAME}.out: $(BUILD_DIR)/main.o $(BUILD_DIR)/PPool.o $(BUILD_DIR)/ProcessTask.o $(BUILD_DIR)/HttpTask.o $(BUILD_DIR)/ConfsTask.o $(BUILD_DIR)/Tttask.o $(BUILD_DIR)/pmc_mtd.o $(BUILD_DIR)/subsys_call.o $(BUILD_DIR)/InitParser.o
|
||||
$(CPP) $^ $(MY_LIBS) $(LIBS) -o $@
|
||||
|
||||
$(BUILD_DIR)/main.o: src/main.cpp
|
||||
@@ -53,6 +53,9 @@ $(BUILD_DIR)/pmc_mtd.o: src/main/pmc_mtd.cpp
|
||||
$(BUILD_DIR)/subsys_call.o: src/main/subsys_call.cpp
|
||||
$(CPP) $(CPPFLAGS) -c $^ $(INCS) -o $@
|
||||
|
||||
$(BUILD_DIR)/InitParser.o: src/registry/InitParser.cpp
|
||||
$(CPP) $(CPPFLAGS) -c $^ $(INCS) -o $@
|
||||
|
||||
|
||||
|
||||
install: $(APP_NAME).out
|
||||
@@ -65,6 +68,7 @@ install: $(APP_NAME).out
|
||||
@echo "正在配置 $(APP_NAME)"
|
||||
@mkdir -p $(CONF_DIR)
|
||||
@cp $(CONF_NAME) $(CONF_DIR)/$(CONF_NAME)
|
||||
@cp init.txt $(CONF_DIR)/init.txt
|
||||
@cp $(SRV_NAME) $(SRV_DIR)/$(SRV_NAME)
|
||||
@systemctl daemon-reload
|
||||
@systemctl enable $(SRV_NAME)
|
||||
@@ -77,6 +81,7 @@ uninstall:
|
||||
@
|
||||
@rm -f $(INSTL_DIRS)/$(APP_NAME)
|
||||
@rm -f $(CONF_DIRS)/$(CONF_NAME)
|
||||
@rm -f $(CONF_DIRS)/init.txt
|
||||
@rm -f -d ${CONF_DIRS}
|
||||
@
|
||||
@systemctl stop $(SRV_NAME)
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
cd "$(dirname "$0")" || exit
|
||||
export LD_LIBRARY_PATH=/lib:$LD_LIBRARY_PATH
|
||||
./nidus.out -i init.txt --serve 5200 --confs 8010
|
||||
./nidus.out -i . --serve 5200 --confs 8010
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace qing::registry {
|
||||
|
||||
/* 服务启动配置项
|
||||
*
|
||||
* 从 services.yaml 或 init.txt 解析出的单个服务声明。
|
||||
* YAML 模式下包含完整的元数据;TXT 模式下仅 cmd 有值。
|
||||
*/
|
||||
struct ServiceConfig {
|
||||
std::string name; /* 服务唯一标识 */
|
||||
std::string cmd; /* 启动命令(如 bash /opt/.../_run.sh) */
|
||||
std::vector<std::string> args; /* 传递给 _run.sh 的命令行参数 */
|
||||
std::map<std::string, std::string> env; /* 启动前设置的环境变量 */
|
||||
std::vector<std::string> depends_on; /* 依赖的服务名列表(未来 DAG 排序用) */
|
||||
int sleep_before = 0; /* 启动前等待秒数 */
|
||||
|
||||
/* 拼接完整命令:cmd + " " + args... */
|
||||
std::string toCommand() const;
|
||||
};
|
||||
|
||||
/* 启动配置解析器
|
||||
*
|
||||
* 优先读 services.yaml(结构化声明),不存在则回退 init.txt(纯命令列表)。
|
||||
* 两种格式都输出 std::vector<ServiceConfig>,调用方无需关心来源。
|
||||
*/
|
||||
class InitParser {
|
||||
public:
|
||||
/* 入口:传入配置目录(如 /etc/Nidus)
|
||||
*
|
||||
* 查找顺序:
|
||||
* 1. <dir>/services.yaml → parseYaml()
|
||||
* 2. <dir>/init.txt → parseTxt()
|
||||
* 3. 都不存在 → 返回空 vector,调用方自行处理
|
||||
*/
|
||||
static std::vector<ServiceConfig> load(const std::string& dir);
|
||||
|
||||
/* 解析 services.yaml */
|
||||
static std::vector<ServiceConfig> parseYaml(const std::string& path);
|
||||
|
||||
/* 解析 init.txt(向后兼容,每行一条命令,# 开头为注释) */
|
||||
static std::vector<ServiceConfig> parseTxt(const std::string& path);
|
||||
};
|
||||
|
||||
} // namespace qing::registry
|
||||
@@ -0,0 +1,87 @@
|
||||
# core/services.yaml
|
||||
# 声明式服务启动配置(替代 init.txt)
|
||||
# 如果本文件存在则优先使用,否则回退 init.txt
|
||||
#
|
||||
# 字段说明:
|
||||
# name 服务唯一标识(用于日志和未来注册中心)
|
||||
# cmd 启动命令(与 init.txt 每行格式一致)
|
||||
# args 传递给 _run.sh 的命令行参数列表(可选)
|
||||
# env 启动前设置的环境变量(可选)
|
||||
# depends_on 依赖的服务名列表(未来 DAG 拓扑排序用,当前仅记录)
|
||||
# sleep_before 启动前等待秒数(替代散落在 _run.sh 中的 sleep)
|
||||
|
||||
services:
|
||||
# --- 数据层(最先启动) ---
|
||||
- name: db2
|
||||
cmd: bash /opt/micro_servers/db2/_run.sh
|
||||
args: []
|
||||
depends_on: []
|
||||
sleep_before: 0
|
||||
|
||||
# --- AI 审核 ---
|
||||
- name: ai_review
|
||||
cmd: bash /opt/micro_servers/ai_review/_run.sh
|
||||
args: []
|
||||
depends_on: [db2]
|
||||
sleep_before: 2
|
||||
|
||||
# --- 文件服务 ---
|
||||
- name: dnfs2
|
||||
cmd: bash /opt/micro_servers/dnfs2/_run.sh
|
||||
args: []
|
||||
depends_on: [db2, ai_review]
|
||||
sleep_before: 0
|
||||
|
||||
# --- 邮件服务 ---
|
||||
- name: smtp
|
||||
cmd: bash /opt/micro_servers/smtp/_run.sh
|
||||
args: []
|
||||
depends_on: []
|
||||
sleep_before: 2
|
||||
|
||||
# --- 前端静态资源 ---
|
||||
- name: fsys
|
||||
cmd: bash /opt/micro_servers/fsys/_run.sh
|
||||
args: []
|
||||
depends_on: []
|
||||
sleep_before: 0
|
||||
|
||||
# --- 安全链路 ---
|
||||
- name: waf
|
||||
cmd: bash /opt/micro_servers/waf/_run.sh
|
||||
args: [] # -c config.yaml 保留在 _run.sh 作为默认值
|
||||
depends_on: [ca, gateway]
|
||||
sleep_before: 0
|
||||
|
||||
- name: ca
|
||||
cmd: bash /opt/micro_servers/ca/_run.sh
|
||||
args: []
|
||||
depends_on: [db2]
|
||||
sleep_before: 2
|
||||
|
||||
- name: gateway
|
||||
cmd: bash /opt/micro_servers/gateway/_run.sh
|
||||
args: []
|
||||
depends_on: [db2]
|
||||
sleep_before: 2
|
||||
|
||||
# --- 验证码 ---
|
||||
- name: captcha2
|
||||
cmd: bash /opt/micro_servers/captcha2/_run.sh
|
||||
args: []
|
||||
depends_on: []
|
||||
sleep_before: 1
|
||||
|
||||
# --- AI 工具箱 ---
|
||||
- name: aibox
|
||||
cmd: bash /opt/micro_servers/aibox/_run.sh
|
||||
args: []
|
||||
depends_on: []
|
||||
sleep_before: 0
|
||||
|
||||
# --- 端口重定向(最后启动) ---
|
||||
- name: redirect80
|
||||
cmd: bash /opt/micro_servers/redirect80/_run.sh
|
||||
args: []
|
||||
depends_on: [waf]
|
||||
sleep_before: 0
|
||||
+1
-1
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
Reference in new issue
Block a user