diff --git a/confs/README.md b/confs/README.md index 686d31d..3e3e74a 100644 --- a/confs/README.md +++ b/confs/README.md @@ -227,7 +227,7 @@ except RpcError as e: ## 集成关系 -- 由 `core`(`PMC`)通过 `init.txt` 中的 `bash /opt/micro_servers/confs/_run.sh` 拉起并守护(在 db 之后、ai_review 之前启动) +- 由 `core`(`PMC`)通过 `init.txt` 中的 `bash /opt/micro_servers/confs/_run.sh` 拉起并守护(需先于 db 启动,db 对 confs 有强依赖) - 各内部服务通过 pymsg `RpcClient` 调 `get` 路由按需获取配置,替代本地散落的 ini/json/yaml - 自身日志通过 `log_client.py` 异步推送至 `db:8005` 的 Logs 表(与其他服务一致) @@ -237,7 +237,7 @@ except RpcError as e: |------|---------|---------| | [static](../static/) | [static.ini](configs/static.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 本地 `config.ini` 回退 | | [dnfs](../dnfs/) | [dnfs.ini](configs/dnfs.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 环境变量覆盖 | -| [db](../db/) | [db.ini](configs/db.ini) | 三层 fail-safe:内置默认值 → confs(优先)→ 环境变量覆盖(最高优先级,Docker 兼容) | +| [db](../db/) | [db.ini](configs/db.ini) | confs 强依赖(confs 不可用时 db 直接退出)→ 环境变量覆盖(最高优先级,Docker 兼容) | static 的接入实现见 [static/fsys.py](../static/fsys.py): - `_fetch_from_confs()` 通过 `RpcClient` 调 `get` 路由取 `static.ini`,3 秒超时 @@ -251,10 +251,12 @@ dnfs 的接入实现见 [dnfs/dnfs.py](../dnfs/dnfs.py): - Docker 环境通过环境变量传入服务名(db/ai_review),覆盖 confs 中的 127.0.0.1 默认值 db 的接入实现见 [db/dbv2.py](../db/dbv2.py): -- 三层加载顺序:内置默认值 → confs(db.ini)→ 环境变量覆盖(优先级最高) -- 配置项涵盖:`[server]` host/port/workers、`[database]` url、`[logs]` retention_days/max_records/cleanup_interval、`[confs]` host/port +- 两层加载顺序:confs(db.ini,强依赖)→ 环境变量覆盖(优先级最高) +- confs 连接失败或 db.ini 缺失必需配置项时,db 直接 `sys.exit(1)` 终止,不使用默认值 +- 配置项涵盖:`[server]` host/port/workers、`[database]` url、`[logs]` retention_days/max_records/cleanup_interval +- confs 连接地址为引导配置:环境变量 `CONFS_HOST`/`CONFS_PORT` 优先,默认 `127.0.0.1:8010` - 支持的环境变量:`LISTEN_ADDR`/`LISTEN_PORT`/`WORKERS`/`DATABASE_URL`/`CONFS_HOST`/`CONFS_PORT` -- 注意:db 先于 confs 启动(启动顺序 db → confs → ai_review/...),db 启动时若 confs 不可用会静默 fail-safe 使用默认值,配置来源会打印到控制台 +- 启动顺序:confs 必须先于 db 启动(confs → db → ai_review/...) - 独立运维工具 `db_tools.py`(backup/export/import 等)仍直接读 `DATABASE_URL` 环境变量,不依赖 confs,避免运维脚本依赖服务就绪 其他服务(gateway / waf / ai_review / smtp)尚未接入,仍读各自本地配置。 diff --git a/db/README.md b/db/README.md index 0397c2f..cc3da2d 100644 --- a/db/README.md +++ b/db/README.md @@ -31,11 +31,10 @@ ## 配置管理 -db 已接入 [confs](../confs/) 统一配置服务,采用三层 fail-safe 加载模式(与 static/dnfs 一致): +db 已接入 [confs](../confs/) 统一配置服务,confs 是 db 启动的**强依赖**: -1. **内置默认值** — 硬编码在 `dbv2.py` 中,保证服务在任何环境都能启动 -2. **confs 统一配置服务**(优先)— 通过 pymsg RpcClient 从 `confs` 拉取 `db.ini` -3. **环境变量覆盖**(最高优先级)— Docker/部署环境通过环境变量覆盖,保持向后兼容 +1. **confs 统一配置服务** — 必须成功从 confs 拉取 `db.ini`,否则 db 启动失败直接退出(exit code 1) +2. **环境变量覆盖**(最高优先级)— Docker/部署环境通过环境变量覆盖 confs 中的值 ### confs 配置文件(db.ini) @@ -54,13 +53,15 @@ url = sqlite:///database.db retention_days = 30 max_records = 1000000 cleanup_interval = 3600 - -[confs] -host = 127.0.0.1 -port = 8010 ``` -### 环境变量(最高优先级) +### 引导配置(confs 连接地址) + +confs 自身的连接地址不通过 confs 获取(避免循环依赖),通过以下方式确定(优先级从高到低): +- 环境变量 `CONFS_HOST` / `CONFS_PORT` +- 默认值 `127.0.0.1:8010` + +### 环境变量(最高优先级,覆盖 confs 配置值) | 环境变量 | 说明 | 对应 confs 配置项 | |---------|------|------------------| @@ -68,16 +69,19 @@ port = 8010 | `LISTEN_PORT` | 监听端口 | `[server] port` | | `WORKERS` | 回调并发线程数 | `[server] workers` | | `DATABASE_URL` | 数据库连接 URL | `[database] url` | -| `CONFS_HOST` | confs 服务地址 | `[confs] host` | -| `CONFS_PORT` | confs 服务端口 | `[confs] port` | +| `CONFS_HOST` | confs 服务地址(引导配置) | — | +| `CONFS_PORT` | confs 服务端口(引导配置) | — | -### 启动顺序注意 +### 启动顺序要求 -根据 micro_servers 的启动顺序:**db → confs → ai_review/...**,db 先于 confs 启动。因此: -- db 启动时若 confs 尚未就绪,`_fetch_from_confs()` 连接失败会静默 fail-safe,使用内置默认值 + 环境变量正常启动 -- 启动时控制台会打印配置来源:`配置来源: confs 统一配置服务 (db.ini)` 或 `警告: confs 不可用,使用内置默认值`,最后 `start()` 前会再次打印最终来源 -- 独立运维工具 `db_tools.py`(backup/export/import/vacuum 等)仍直接读 `DATABASE_URL` 环境变量,不依赖 confs 服务就绪 -- Docker 环境通过 `docker-entrypoint.sh` 传入环境变量(见 [Dockerfile](Dockerfile)),保持与之前完全兼容,无需额外配置 +**confs 必须先于 db 启动**(启动顺序调整为:confs → db → ai_review/...)。若 confs 不可用: +- db 打印致命错误到 stderr(包含 confs 地址和具体错误原因),以 exit code 1 终止 +- 不会静默使用默认值启动 +- 配置完整性校验:若 db.ini 中缺少必需的 section/key 且无对应环境变量覆盖,同样报错退出 +- 启动成功时控制台打印:`配置来源: confs 统一配置服务 (db.ini @ host:port)` + +> 独立运维工具 `db_tools.py`(backup/export/import/vacuum 等)仍直接读 `DATABASE_URL` 环境变量,不依赖 confs 服务就绪。 +> Docker 环境通过 `docker-entrypoint.sh` 传入环境变量(见 [Dockerfile](Dockerfile)),需确保 `CONFS_HOST`/`CONFS_PORT` 指向 confs 服务地址。 ## 协议与 API diff --git a/db/dbv2.py b/db/dbv2.py index ccd2121..b0123af 100644 --- a/db/dbv2.py +++ b/db/dbv2.py @@ -48,19 +48,25 @@ import sys as _sys _sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from pymsg import RpcServer # noqa: E402 -# ── 配置加载(三层 fail-safe:内置默认值 → confs → 环境变量覆盖)────── -# 加载顺序:内置默认值 → confs 统一配置服务(db.ini,优先); -# confs 不可用时 fail-safe 使用默认值;环境变量优先级最高(Docker 兼容)。 +# ── 配置加载(两层:confs 强依赖 → 环境变量覆盖)────────────────────── +# confs 是 db 启动的强依赖:必须成功从 confs 拉取 db.ini,否则直接退出。 +# 环境变量优先级最高(Docker 兼容),可覆盖 confs 中的配置值。 +# confs 自身的连接地址(host/port)通过引导配置确定:环境变量 CONFS_HOST/CONFS_PORT +# 优先,缺省使用 127.0.0.1:8010(与其他服务一致的约定)。 +# 引导配置:confs 连接地址(不依赖 confs 本身,可通过环境变量覆盖) +_confs_host = os.environ.get('CONFS_HOST', '127.0.0.1') +_confs_port = int(os.environ.get('CONFS_PORT', '8010')) + +# 白名单结构:_merge_ini 仅覆盖此处列出的 section/key,防止意外字段 _db_config = { - 'server': {'host': '0.0.0.0', 'port': '8005', 'workers': '4'}, - 'database': {'url': 'sqlite:///database.db'}, + 'server': {'host': '', 'port': '', 'workers': ''}, + 'database': {'url': ''}, 'logs': { - 'retention_days': '30', - 'max_records': '1000000', - 'cleanup_interval': '3600', + 'retention_days': '', + 'max_records': '', + 'cleanup_interval': '', }, - 'confs': {'host': '127.0.0.1', 'port': '8010'}, } @@ -76,26 +82,32 @@ def _merge_ini(text): def _fetch_from_confs(): - """从 confs 统一配置服务拉取 db.ini 全文,失败返回 None(fail-safe)。""" + """从 confs 统一配置服务拉取 db.ini 全文,失败直接终止进程。""" + from pymsg import RpcClient, RpcError # noqa: E402 try: - from pymsg import RpcClient - host = os.environ.get('CONFS_HOST', _db_config['confs']['host']) - port = int(os.environ.get('CONFS_PORT', _db_config['confs']['port'])) - data = RpcClient(host, port, 'db', timeout=3).call( - 'get', {'name': 'db.ini'}) + client = RpcClient(_confs_host, _confs_port, 'db', timeout=5) + data = client.call('get', {'name': 'db.ini'}) return data.get('content') - except Exception: - return None + except (ConnectionRefusedError, ConnectionError, OSError) as e: + print(f'[dbv2] 致命错误: 无法连接 confs 服务 ({_confs_host}:{_confs_port}): {e}', + file=_sys.stderr) + print(f'[dbv2] 请确认 confs 服务已启动并监听 {_confs_host}:{_confs_port}', + file=_sys.stderr) + _sys.exit(1) + except RpcError as e: + print(f'[dbv2] 致命错误: 从 confs 获取 db.ini 失败: {e}', + file=_sys.stderr) + _sys.exit(1) + except Exception as e: + print(f'[dbv2] 致命错误: confs 配置拉取异常: {e}', + file=_sys.stderr) + _sys.exit(1) _confs_text = _fetch_from_confs() -_config_source = 'built-in defaults' -if _confs_text is not None: - _merge_ini(_confs_text) - _config_source = 'confs (db.ini)' - print(f'[dbv2] 配置来源: confs 统一配置服务 (db.ini)') -else: - print(f'[dbv2] 警告: confs 不可用,使用内置默认值') +_merge_ini(_confs_text) +_config_source = 'confs (db.ini)' +print(f'[dbv2] 配置来源: confs 统一配置服务 (db.ini @ {_confs_host}:{_confs_port})') # 环境变量覆盖(最高优先级,保持 Docker 兼容) if os.environ.get('LISTEN_ADDR'): @@ -107,6 +119,19 @@ if os.environ.get('WORKERS'): if os.environ.get('DATABASE_URL'): _db_config['database']['url'] = os.environ['DATABASE_URL'] +# 配置完整性校验 +_required = { + 'server': ['host', 'port', 'workers'], + 'database': ['url'], + 'logs': ['retention_days', 'max_records', 'cleanup_interval'], +} +for section, keys in _required.items(): + for key in keys: + if not _db_config[section][key]: + print(f'[dbv2] 致命错误: 配置项 [{section}] {key} 缺失(db.ini 中未配置且无环境变量覆盖)', + file=_sys.stderr) + _sys.exit(1) + # 解析为最终常量 DATABASE_URL = _db_config['database']['url'] DEFAULT_RETENTION_DAYS = int(_db_config['logs']['retention_days'])