docs(db2): 添加 README
This commit is contained in:
1 parent
577386bff2
commit
fad749f9aa
1 file changed
+253
+253
@@ -0,0 +1,253 @@
|
|||||||
|
# db2 — C++20 数据库 RPC 服务(libmsg + SQLite)
|
||||||
|
|
||||||
|
db 模块的 C++20 重实现,接口与 Python 版完全兼容:相同的 `execute` 路由、相同的请求/响应格式、相同的 `where` 语法。gateway 的 `DbC::call_msg()` 以及所有使用 pymsg `execute` 路由的调用方**零改动**即可切换。
|
||||||
|
|
||||||
|
## 与 Python db 的关系
|
||||||
|
|
||||||
|
| 特性 | db(Python) | db2(C++) |
|
||||||
|
|------|-------------|-----------|
|
||||||
|
| 传输协议 | pymsg TCP 帧 | pymsg TCP 帧(libmsg) |
|
||||||
|
| `execute` 路由接口 | ✅ | ✅ 完全对齐 |
|
||||||
|
| where 运算符 | ✅ | ✅ 完全对齐 |
|
||||||
|
| 响应格式 | ✅ | ✅ 完全对齐 |
|
||||||
|
| `$now` 哨兵值 | ✅ | ✅ |
|
||||||
|
| confs 配置拉取 | ✅ | ✅ |
|
||||||
|
| Logs 自动建表 + 清理 | ✅ | ✅ |
|
||||||
|
| 数据库后端 | SQLite / PG / MySQL | SQLite(直接 C API) |
|
||||||
|
| 依赖 | Python 3 + SQLAlchemy | libmsg + boost::json + libsqlite3 |
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 编译
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./_set.sh # 等同于 make,产物为 build/db2
|
||||||
|
```
|
||||||
|
|
||||||
|
需要系统已安装:`libboost-json-dev`、`libsqlite3-dev`,以及 `libmsg` 已构建(`../libmsg/build/libus_msg.so`)。
|
||||||
|
|
||||||
|
### 启动
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./_run.sh # 读 confs 自动获取配置
|
||||||
|
./_run.sh --addr 127.0.0.1 --port 8005 --workers 4 # 覆盖监听参数
|
||||||
|
```
|
||||||
|
|
||||||
|
服务启动时强依赖 confs(拉取 `db.ini`)。confs 未就绪时自动重试,不要求启动顺序。
|
||||||
|
|
||||||
|
## 配置管理
|
||||||
|
|
||||||
|
与 Python db 完全一致:启动时从 confs 服务拉取 `db.ini`,失败则退出(exit code 1)。
|
||||||
|
|
||||||
|
### confs 配置文件(db.ini)
|
||||||
|
|
||||||
|
配置文件托管于 [confs/configs/db.ini](../confs/configs/db.ini):
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[server]
|
||||||
|
host = 127.0.0.1
|
||||||
|
port = 8005
|
||||||
|
workers = 4
|
||||||
|
|
||||||
|
[database]
|
||||||
|
url = sqlite:///database.db
|
||||||
|
|
||||||
|
[logs]
|
||||||
|
retention_days = 30
|
||||||
|
max_records = 1000000
|
||||||
|
cleanup_interval = 3600
|
||||||
|
```
|
||||||
|
|
||||||
|
### 引导配置(confs 连接地址)
|
||||||
|
|
||||||
|
| 来源 | 优先级 |
|
||||||
|
|------|--------|
|
||||||
|
| 环境变量 `CONFS_HOST` / `CONFS_PORT` | 最高 |
|
||||||
|
| 默认值 `127.0.0.1:8010` | 兜底 |
|
||||||
|
|
||||||
|
### 环境变量(最高优先级,覆盖 confs 配置值)
|
||||||
|
|
||||||
|
| 环境变量 | 说明 | 对应配置项 |
|
||||||
|
|---------|------|-----------|
|
||||||
|
| `LISTEN_ADDR` | 监听地址 | `[server] host` |
|
||||||
|
| `LISTEN_PORT` | 监听端口 | `[server] port` |
|
||||||
|
| `WORKERS` | 工作线程数 | `[server] workers` |
|
||||||
|
| `DATABASE_URL` | 数据库文件路径 | `[database] url` |
|
||||||
|
| `CONFS_HOST` | confs 地址(引导) | — |
|
||||||
|
| `CONFS_PORT` | confs 端口(引导) | — |
|
||||||
|
|
||||||
|
### 命令行参数(最高优先级)
|
||||||
|
|
||||||
|
```
|
||||||
|
--addr / -a 监听地址
|
||||||
|
--port / -p 监听端口
|
||||||
|
--workers / -w 工作线程数
|
||||||
|
```
|
||||||
|
|
||||||
|
## 协议与 API
|
||||||
|
|
||||||
|
与 Python db 完全一致,可直接参考 [db/README.md](../db/README.md)。以下为要点摘要。
|
||||||
|
|
||||||
|
### 传输协议(pymsg)
|
||||||
|
|
||||||
|
帧格式:`[4 字节大端长度][UTF-8 JSON]`,TCP 长连接。
|
||||||
|
|
||||||
|
### execute 路由 — 通用 CRUD
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_route": "execute",
|
||||||
|
"action": "select",
|
||||||
|
"table": "Users",
|
||||||
|
"columns": ["id", "username"],
|
||||||
|
"values": {"username": "bob", "password_hash": "..."},
|
||||||
|
"where": {
|
||||||
|
"is_active": 1,
|
||||||
|
"create_at": {"ge": "2026-01-01T00:00:00"}
|
||||||
|
},
|
||||||
|
"order_by": [{"column": "id", "desc": true}],
|
||||||
|
"limit": 10,
|
||||||
|
"offset": 0
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### where 条件运算符
|
||||||
|
|
||||||
|
| 运算符 | SQL | 示例 |
|
||||||
|
|--------|-----|------|
|
||||||
|
| 直接值 | `col = ?` | `"name": "Alice"` |
|
||||||
|
| `eq` | `=` | `"age": {"eq": 30}` |
|
||||||
|
| `ne` | `!=` | `"status": {"ne": "deleted"}` |
|
||||||
|
| `gt` | `>` | `"score": {"gt": 80}` |
|
||||||
|
| `ge` | `>=` | `"score": {"ge": 60}` |
|
||||||
|
| `lt` | `<` | `"price": {"lt": 100}` |
|
||||||
|
| `le` | `<=` | `"price": {"le": 200}` |
|
||||||
|
| `like` | `LIKE` | `"name": {"like": "%Smith%"}` |
|
||||||
|
| `in` | `IN (...)` | `"id": {"in": [1,2,3]}` |
|
||||||
|
|
||||||
|
多条件自动 **AND** 连接。
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
|
||||||
|
```json
|
||||||
|
// 成功
|
||||||
|
{"success": true, "data": [{"id": 1, "username": "Alice"}]}
|
||||||
|
|
||||||
|
// insert
|
||||||
|
{"success": true, "data": [{"lastrowid": 42}]}
|
||||||
|
|
||||||
|
// update / delete
|
||||||
|
{"success": true, "data": [{"rowcount": 3}]}
|
||||||
|
|
||||||
|
// 失败
|
||||||
|
{"success": false, "error": "table not found: Foo"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 日期时间列
|
||||||
|
|
||||||
|
列类型含 `DATETIME`/`DATE`/`TIME`,或列名含 `_at`/`time`/`date` 的列自动视为日期列:
|
||||||
|
|
||||||
|
| 写入值 | 行为 |
|
||||||
|
|--------|------|
|
||||||
|
| `"2026-07-28T22:30:00"` | 原样存储(UTC) |
|
||||||
|
| `"$now"` | 替换为服务端当前 UTC 时间 |
|
||||||
|
| 其他字符串 | 原样存储 |
|
||||||
|
|
||||||
|
## 使用示例(C++ gateway)
|
||||||
|
|
||||||
|
gateway 的 `DbC::call_msg()` 无需任何修改,直接指向 db2 端口即可:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// DbC::init("127.0.0.1", 8005); // 不变
|
||||||
|
|
||||||
|
// 查询用户
|
||||||
|
auto result = DbC::call_msg({
|
||||||
|
{"action", "select"},
|
||||||
|
{"table", "Users"},
|
||||||
|
{"where", {{"username", "alice"}}},
|
||||||
|
{"limit", 1},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新登录时间
|
||||||
|
DbC::call_msg({
|
||||||
|
{"action", "update"},
|
||||||
|
{"table", "Users"},
|
||||||
|
{"values", {{"last_login", "$now"}}},
|
||||||
|
{"where", {{"id", user_id}}},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例(Python pymsg)
|
||||||
|
|
||||||
|
```python
|
||||||
|
from pymsg.rpc import RpcClient
|
||||||
|
|
||||||
|
client = RpcClient("127.0.0.1", 8005, "myservice")
|
||||||
|
|
||||||
|
# 查询
|
||||||
|
rows = client.call("execute", {
|
||||||
|
"action": "select",
|
||||||
|
"table": "Users",
|
||||||
|
"where": {"is_active": 1},
|
||||||
|
"order_by": [{"column": "id", "desc": True}],
|
||||||
|
"limit": 20,
|
||||||
|
})
|
||||||
|
|
||||||
|
# 插入
|
||||||
|
result = client.call("execute", {
|
||||||
|
"action": "insert",
|
||||||
|
"table": "Notes",
|
||||||
|
"values": {"pid": 0, "owner": "alice", "title": "hello", "content": "world"},
|
||||||
|
})
|
||||||
|
print(result[0]["lastrowid"])
|
||||||
|
```
|
||||||
|
|
||||||
|
## 内置日志能力
|
||||||
|
|
||||||
|
db2 内置 Logs 表(启动时自动建表 + 索引),设计与 Python db 完全一致。
|
||||||
|
|
||||||
|
### Logs 表结构
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE IF NOT EXISTS Logs (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
ts TEXT NOT NULL, -- UTC ISO 8601
|
||||||
|
service TEXT NOT NULL,
|
||||||
|
level TEXT NOT NULL, -- DEBUG/INFO/WARNING/ERROR/CRITICAL
|
||||||
|
message TEXT NOT NULL, -- 写入时截断至 8192 字节
|
||||||
|
trace_id TEXT,
|
||||||
|
extra TEXT -- 可选 JSON 字符串,截断至 4096 字节
|
||||||
|
);
|
||||||
|
-- 索引: (service,ts) / (level,ts) / (ts) / (trace_id)
|
||||||
|
```
|
||||||
|
|
||||||
|
外部服务通过 `execute` 路由 INSERT 到 Logs 表即可。可复用 [db/example_log_client.py](../db/example_log_client.py)。
|
||||||
|
|
||||||
|
### 自动清理
|
||||||
|
|
||||||
|
后台线程每 `cleanup_interval` 秒(默认 3600)执行:
|
||||||
|
1. 删除超过 `retention_days` 天(默认 30)的记录
|
||||||
|
2. 若总条数超过 `max_records`(默认 100 万),删除最旧的超出部分
|
||||||
|
|
||||||
|
## 安全机制
|
||||||
|
|
||||||
|
- 表名仅允许字母、数字、下划线,防止 SQL 注入
|
||||||
|
- 所有 values / where 值通过 `sqlite3_bind_text` 参数绑定,无字符串拼接
|
||||||
|
- 仅操作已有表(`PRAGMA table_info` 反射),无法建表或删表
|
||||||
|
- RpcServer 异常自动脱敏(过滤内部路径),错误细节仅写 stderr
|
||||||
|
|
||||||
|
## 实现说明
|
||||||
|
|
||||||
|
- **per-thread 连接**:每个 worker 线程持有独立 `sqlite3*` 连接(`thread_local`),避免跨线程共享 prepared statement
|
||||||
|
- **列 schema 缓存**:`PRAGMA table_info` 结果加锁缓存,仅首次请求时反射
|
||||||
|
- **SQLite PRAGMA**:`journal_mode=WAL`、`synchronous=NORMAL`、`foreign_keys=ON`、`busy_timeout=5000`、`mmap_size=268435456`,对齐 Python 版配置
|
||||||
|
|
||||||
|
## 文件说明
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `src/db2.cpp` | 全部实现(配置/连接池/路由/日志/清理) |
|
||||||
|
| `Makefile` | 编译规则,链接 `libus_msg` + `boost_json` + `sqlite3` |
|
||||||
|
| `_set.sh` | 一键编译 |
|
||||||
|
| `_run.sh` | 启动服务 |
|
||||||
|
| `docker-entrypoint.sh` | 容器入口:初始化 schema + 启动 |
|
||||||
Reference in new issue
Block a user