refactor(db): 删除 confs 连接失败时使用默认配置的 fail-safe 逻辑
- confs 改为 db 启动的强依赖:连接失败直接 sys.exit(1),不再静默使用默认值 - 配置加载从三层(默认值→confs→环境变量)改为两层(confs→环境变量覆盖) - _db_config 初始值改为空字符串白名单模板,移除内置默认值 - 新增配置完整性校验:confs 返回后检查所有必需 section/key 非空,缺失则报错退出 - 区分错误类型:连接拒绝/RpcError/通用异常分别打印明确的错误信息到 stderr - confs 连接地址改为引导配置(环境变量优先,默认 127.0.0.1:8010),不再从 db.ini 读取 - 更新 db/README.md 与 confs/README.md,说明启动顺序调整为 confs → db → ...
This commit is contained in:
1 parent
6ebd544619
commit
2cbb93b3b7
3 files changed
+77
-46
No files matched your search
+21
-17
@@ -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
|
||||
|
||||
|
||||
+49
-24
@@ -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'])
|
||||
|
||||
Reference in new issue
Block a user