RustBill 运维手册
RustBill 的生产部署与运维参考手册。目标读者:生产环境中的系统管理员。
1. 系统架构概览
RustBill 是基于 Rust 的云服务器分销计费与开通平台,采用 Cargo workspace 多 crate 结构:
| Crate | 职责 |
|---|---|
rustbill-proto | protobuf 定义 + tonic 代码生成(公共契约) |
rustbill-core | 领域模型 + 全部 trait(插件/存储抽象) |
rustbill-store-pg | PostgreSQL 存储实现(sqlx) |
rustbill-server | gRPC 服务端(tonic),插件加载,Session+JWT 双认证,内嵌 Admin SPA |
rustbill-cli | 最高权限 CLI 管理工具(直接 DB 访问),含 TUI 交互模式 |
rustbill-provider-* | 服务器资源供应商插件 |
rustbill-gateway-* | 支付网关插件 |
rustbill-notifier-* | 通知渠道插件 |
1.1 运行环境依赖
- Rust 1.80+(编译);运行时仅需 glibc 2.35+(ubuntu-22.04 构建产物兼容绝大多数现代 Linux 发行版)
- PostgreSQL 16+(数据存储,9 个迁移文件:
001_initial至009_plugins_table) - Redis(可选,用于 session 缓存、分布式锁、跨实例速率限制;不可用时自动降级)
- Node.js 22+ + npm(前端构建;运行时不需要)
1.2 服务端口与协议
| 端口 | 协议 | 说明 |
|---|---|---|
50051 | gRPC (HTTP/2) | tonic 服务端主端口 |
80/443 | HTTP/HTTPS | Caddy/Nginx 反向代理对外暴露(含 gRPC-Web 路径) |
/health | HTTP GET | 健康检查端点(Circuit-breaker 短路后续所有中间件层,直接返回 200 OK) |
/admin/* | HTTP GET | 内嵌 Admin SPA(通过 Tower Layer 嵌入 tonic server,SPA fallback 到 index.html) |
1.3 Tower 中间件栈
ConcurrencyLimitLayer(30) → HealthRouteLayer(/health 短路) → RateLimitLayer
→ AdminUiLayer → CorsLayer → SessionAuthLayer → JwtAuthLayer
→ ApiKeyAuthLayer → LogContextLayer → GrpcWebLayer → gRPC services
每个认证中间件只处理自己的认证类型,无凭证时直接放行(pass-through),不做 401 拦截。
2. 配置文件参考
配置文件 config.toml(复制自 config.example.toml),支持 config.local.toml 覆盖(Figment 层叠合并)。数组替换而非追加。
2.1 [server] — 服务器配置
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
host | string | "127.0.0.1" | gRPC 服务监听地址。生产环境反向代理前置时保持 127.0.0.1 |
port | integer | 50051 | gRPC 服务监听端口 |
max_concurrency | integer | 30 | 入口并发上限,超过排队(FIFO backpressure) |
notify_email | string | "" | 系统通知接收邮箱(订单通知等) |
2.2 [db] — 数据库配置
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url | string | (必填) | PostgreSQL 连接字符串。格式:postgres://user:pass@host:5432/rustbill |
max_connections | integer | 20 | 连接池最大连接数 |
min_connections | integer | 2 | 连接池最小连接数(启动时预建立) |
idle_timeout_secs | integer | 300 | 空闲连接超时秒数 |
max_lifetime_secs | integer | 1800 | 连接最大生命秒数(到期后回收重建) |
acquire_timeout_secs | integer | 5 | 获取连接超时秒数 |
test_before_acquire | boolean | true | 取连接前执行 SELECT 1 剔除死连接 |
2.3 [jwt] — JWT 配置(Customer 认证)
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
secret | string | (必填) | JWT 签名密钥。必须 >= 32 字符。为空或为 "change-me-in-production" 时启动拒绝 |
expiry_hours | integer | 24 | access_token 过期小时数 |
2.4 [session] — Session 配置(Admin 认证)
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
cookie_name | string | "rustbill_session" | httpOnly Session Cookie 名称 |
idle_timeout_minutes | integer | 1440 | 空闲超时分钟数(24 小时) |
absolute_timeout_hours | integer | 168 | 绝对超时小时数(7 天),无论是否活跃 |
cookie_secure | boolean | false | Cookie Secure 标志(生产环境经 HTTPS 代理时设为 true) |
cookie_same_site | string | "Lax" | Cookie SameSite 策略(Strict / Lax / None) |
2.5 [bootstrap] — 初始管理员配置
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
admin_username | string | "admin" | 初始管理员用户名 |
admin_password | string | (必填) | 初始管理员密码(bcrypt 哈希存储,无默认值) |
2.6 [rate_limit] — 速率限制配置
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled | boolean | true | 是否启用速率限制 |
login_per_sec | integer | 5 | 每秒登录请求上限 |
register_per_sec | integer | 1 | 每秒注册请求上限 |
2.7 [redis] — Redis 配置(可选)
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled | boolean | false | 是否启用 Redis。启用后提供 Session 缓存 + 分布式锁 + 跨实例速率限制 |
url | string | "redis://127.0.0.1:6379" | Redis 连接 URL |
max_connections | integer | 10 | Redis 连接池大小 |
prefix | string | "rustbill" | Redis key 前缀(避免多实例/多环境 key 冲突) |
2.8 [plugins] — 插件配置
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
plugins_dir | string | "./plugins" | Rune 脚本插件目录。PluginScanner 扫描此目录下 *.rn 文件并同步到数据库 |
3. 构建与部署
3.1 从源码构建
# 克隆仓库
git clone https://github.com/zyxisme/rustbill.git
cd rustbill
# 编译服务端(Release,二进制约 15-25MB stripped)
cargo build --release -p rustbill-server
# 编译 CLI 管理工具
cargo build --release -p rustbill-cli
# 跨平台编译(ARM64)
cargo build --release --target aarch64-unknown-linux-gnu
3.2 构建前端
# Admin SPA(内嵌于 rustbill-server)
cd web-admin
npm install
npm run build
# 产物:dist/ → 复制到 rustbill-server/admin-dist/
# Consumer SPA(独立部署)
cd web-consumer
npm install
npm run build
# 产物:dist/ → 部署到 Web 服务器(Caddy/Nginx/Apache)
3.3 Consumer 前端配置
Consumer 运行时配置位于 web-consumer/public/config.json:
{
"grpcEndpoint": "https://your-domain.com",
"appTitle": "RustBill",
"adminUrl": "https://admin.your-domain.com"
}
| 字段 | 说明 |
|---|---|
grpcEndpoint | gRPC 服务完整 origin(跨域部署时必填,同源可省略) |
appTitle | 页面标题(浏览器标签页显示) |
adminUrl | 管理后台外部链接(前台 Dashboard 点击跳转) |
品牌配置:web-consumer/brand.yaml(品牌名/Logo/强调色/导航/集群节点),npm run build 时 vite-plugin-brand.ts 将颜色和导航编译进 JS bundle。
3.4 PostgreSQL 准备
# 创建数据库
sudo -u postgres createdb rustbill
# 创建用户
sudo -u postgres psql -c "CREATE USER rustbill WITH PASSWORD 'your-password';"
sudo -u postgres psql -c "GRANT ALL ON DATABASE rustbill TO rustbill;"
# 赋予 schema 权限(迁移由应用启动时自动执行)
sudo -u postgres psql -d rustbill -c "GRANT ALL ON SCHEMA public TO rustbill;"
迁移由应用启动时 leader 实例自动执行,无需手动 sqlx migrate run。单实例部署时该实例即为 leader。
3.5 systemd Service
# /etc/systemd/system/rustbill.service
[Unit]
Description=RustBill gRPC Server
After=network.target postgresql.service redis.service
Wants=postgresql.service
[Service]
Type=simple
User=rustbill
Group=rustbill
WorkingDirectory=/opt/rustbill
ExecStart=/opt/rustbill/rustbill-server
Restart=on-failure
RestartSec=5
# 安全加固
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/opt/rustbill /var/log/rustbill
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
# 资源限制
MemoryHigh=512M
MemoryMax=1G
CPUQuota=200%
# 日志
StandardOutput=journal
StandardError=journal
SyslogIdentifier=rustbill
# 环境变量
Environment="RUST_LOG=info"
Environment="RUST_LOG_FORMAT=json"
[Install]
WantedBy=multi-user.target
# 启用并启动
sudo systemctl daemon-reload
sudo systemctl enable rustbill
sudo systemctl start rustbill
sudo systemctl status rustbill
3.6 日志轮转
# /etc/logrotate.d/rustbill
/var/log/rustbill/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
copytruncate
dateext
dateformat -%Y%m%d
}
如果使用 systemd journal(StandardOutput=journal),日志由 journald 管理,无需额外 logrotate 配置。
3.7 反向代理 (Caddy)
# /etc/caddy/Caddyfile
your-domain.com {
# gRPC-Web
handle /rustbill.* {
reverse_proxy 127.0.0.1:50051 {
transport http {
versions h2c
}
}
}
# Admin 管理后台(内嵌于 rustbill-server)
handle /admin* {
reverse_proxy 127.0.0.1:50051
}
# Consumer 前台(独立部署的静态文件)
handle {
root * /var/www/rustbill-consumer
file_server
try_files {path} /index.html
}
}
3.8 Docker Compose
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: rustbill
POSTGRES_USER: rustbill
POSTGRES_PASSWORD: change-me
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redisdata:/data
ports:
- "6379:6379"
restart: unless-stopped
rustbill:
build:
context: .
dockerfile: Dockerfile
ports:
- "50051:50051"
volumes:
- ./config.toml:/opt/rustbill/config.toml:ro
- ./config.local.toml:/opt/rustbill/config.local.toml:ro
- ./plugins:/opt/rustbill/plugins
depends_on:
- postgres
- redis
restart: unless-stopped
volumes:
pgdata:
redisdata:
Dockerfile(多阶段构建):
# 构建阶段
FROM rust:1.80-bookworm AS builder
WORKDIR /app
COPY . .
RUN cargo build --release -p rustbill-server
# 运行阶段
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates libssl3 && rm -rf /var/lib/apt/lists/*
WORKDIR /opt/rustbill
COPY --from=builder /app/target/release/rustbill-server .
COPY config.toml .
COPY plugins/ plugins/
EXPOSE 50051
CMD ["./rustbill-server"]
4. 数据库管理
4.1 迁移状态
# CLI 查看迁移状态
cargo run -p rustbill-cli -- db status
# 手动 psql 查看
psql -d rustbill -c "SELECT version, description, installed_on FROM _sqlx_migrations ORDER BY version;"
当前迁移清单(9 个):
| 迁移文件 | 说明 |
|---|---|
001_initial.sql | 初始基线(所有核心表、FK、CHECK 约束、索引) |
002_operation_logs.sql | 操作日志表 |
003_config_schema.sql | plugin_interfaces 新增 config_schema 列 |
004_citus_prepare.sql | Citus 兼容准备(customer_id NOT NULL) |
005_api_keys.sql | API Key 表 |
006_customer_api_key_flag.sql | customer 表 can_create_api_keys 标志 |
007_instance_detail_sections.sql | 实例详情自定义段 |
008_scriptable_plugins.sql | 脚本化插件系统迁移 |
009_plugins_table.sql | 独立 plugins 定义表 |
4.2 备份
# 完整备份(pg_dump 自定义格式)
pg_dump -Fc -d rustbill -f /backup/rustbill_$(date +%Y%m%d_%H%M%S).dump
# crontab 自动化每日备份(凌晨 2:00)
# crontab -e
0 2 * * * pg_dump -Fc -d rustbill -f /backup/rustbill_$(date +\%Y\%m\%d).dump && find /backup -name 'rustbill_*.dump' -mtime +30 -delete
4.3 恢复
# 停止服务后再恢复
sudo systemctl stop rustbill
# 恢复
pg_restore --clean --if-exists -d rustbill /backup/rustbill_20260526.dump
# 重新启动
sudo systemctl start rustbill
4.4 重建数据库
# 破坏式重建(开发/测试环境)
dropdb rustbill
createdb rustbill
# 启动服务后自动运行迁移
5. CLI 与 TUI 管理
5.1 CLI 命令
# 查看帮助
cargo run -p rustbill-cli -- --help
# 用户管理
cargo run -p rustbill-cli -- user list
cargo run -p rustbill-cli -- user create --username admin2 --password pass123 --role admin
# 插件列表
cargo run -p rustbill-cli -- plugin list
# 数据库状态
cargo run -p rustbill-cli -- db status
5.2 TUI 交互模式
cargo run -p rustbill-cli -- tui
8 个标签页:Dashboard / Admin Users / Cust Users / Customers / Products / Plugins / Instances / DB Status
| 快捷键 | 功能 |
|---|---|
q / Esc | 退出 |
Tab / Shift+Tab | 切换标签页 |
1-8 | 直接跳转到对应标签页 |
↑↓ / jk | 列表导航 |
Enter | 查看详情 |
r | 刷新数据 |
d | 删除/终止(确认弹窗) |
e / x | 启用/禁用插件 |
y / n | 确认/取消弹窗 |
Backspace | 关闭详情面板 |
TUI 完全复用服务端数据库连接,零额外连接。安全退出保证终端恢复(TerminalGuard Drop guard)。
6. 日常运维操作
所有操作均可通过 Admin UI 或 CLI 完成。以下为 CLI 常用命令及对应 Admin UI 页面。
6.1 商品管理 (Products)
Admin UI: 商品管理 → Products
- 创建商品:指定名称、描述、所属分组、动态规格(JSONB)、计费周期(monthly/quarterly/yearly)、价格
- 编辑/删除商品
- 从上游同步商品(Upstream 页 → 上游商品标签 → 勾选 → ImportUpstreamProducts)
- 批量定价(Upstream 页 → 定价管理标签 → 按加价比例批量更新售价:
cost_price × markup_ratio) - 商品规格(specs JSONB)支持动态键值对,包括 cpu_cores / memory_gb / disk_gb / bandwidth_mbps / region / os 等
6.2 客户管理 (Customers)
Admin UI: 客户管理 → Customers
- 创建客户:姓名/联系人/邮箱/电话/信用额度
- 编辑客户信息
- 余额管理:充值/扣款(
MyBalance页面可查看交易流水) - 启用 API Key 权限(
can_create_api_keys标志)
6.3 订单管理 (Orders)
Admin UI: 交易管理 → Orders
订单状态流转:
pending → paid → provisioning → active
├→ cancelled(开通失败退款)
├→ suspended
├→ refunded
└→ completed
- 创建订单:选择客户 + 商品 + 支付网关 + 计费周期
- 支付订单(余额扣减)
- 开通实例(自动由 event_worker 处理异步开通)
- 取消/退款
- 支付超时(24 小时自动取消)
6.4 实例管理 (Instances)
Admin UI: 基础设施 → Instances
实例状态:provisioning / running / stopped / suspended / terminated / error
- 启动/停止/重启/终止实例
- 查看实例详情(含插件自定义 section/iframe)
- 终止是最终操作,不可恢复
6.5 支付管理 (Payments)
Admin UI: 交易管理 → Payments
- 查看支付记录
- 银行转账人工确认
- 退款(全额/部分)
- 支付回调幂等性(SELECT FOR UPDATE 原子化)
6.6 工单管理 (Tickets)
Admin UI: 系统管理 → Tickets
工单状态:pending → processing → resolved → closed
- 创建/分配工单
- 回复(含内部备注
is_internal=true,对客户不可见) - 分配处理人变更时自动通知新旧处理人
6.7 发票管理 (Invoices)
Admin UI: 交易管理 → Billing
发票状态:draft → issued → paid / overdue / cancelled
- 手动创建发票
- 批量生成(按客户+周期)
- 标记为已付款
6.8 插件管理 (Plugins)
Admin UI: 系统管理 → Plugins
插件列表页分为两部分:
- 已安装插件(
.rn定义按 type+id 去重)- 只读列表 - 接口实例 — 可创建/启用/禁用/编辑配置 JSON/执行健康检查
当前已实现的 7 个插件:
| 类型 | 插件 ID | 说明 |
|---|---|---|
| Notifier | notifier-webhook | Webhook HTTP POST 通知 |
| Notifier | notifier-email | SMTP 邮件通知 |
| Gateway | gateway-banktransfer | 银行转账(本地逻辑) |
| Gateway | gateway-yipay | 易支付聚合支付(HTTP + MD5 签名) |
| UpstreamProvider | provider-rustbill | RustBill 上游分销(gRPC-Web) |
| FirstPartyProvider | provider-incus | Incus 虚拟化(mTLS REST API) |
插件脚本存储在数据库 plugin_interfaces.script_source 列中,通过 Admin UI 编辑。修改后由 ScriptEngine.evict() 自动热重载。
7. 日志与监控
7.1 日志格式
RustBill 使用 tracing-subscriber。
# 开发环境 — 彩色文本
RUST_LOG=rustbill_server=debug cargo run -p rustbill-server
# 生产环境 — JSON 格式(日志采集)
RUST_LOG=info RUST_LOG_FORMAT=json cargo run -p rustbill-server
# 特定模块调试
RUST_LOG=rustbill_server::plugin_scanner=debug,rustbill_server::services=info
7.2 健康检查
# 基本健康检查
curl -s http://localhost:50051/health
# 预期响应: 200 OK(空 body)
# 就绪检查(含 DB 连接池验证)
curl -s http://localhost:50051/ready
# 200 就绪,503 未就绪
7.3 关键日志事件
| 事件 | 日志级别 | 说明 |
|---|---|---|
| 服务启动 | INFO | 实例身份注册、leader 选举结果、插件扫描完成 |
| Leader 选举 | INFO | 当选/续期/释放 leader 角色 |
| 数据库迁移 | INFO | 迁移执行状态 |
| 插件扫描 | INFO / WARN | 新增/更新插件定义,文件解析异常 |
| gRPC 请求 | DEBUG | 请求路径、耗时、状态码 |
| 认证失败 | WARN | session/JWT/API key 验证失败 |
| 支付回调 | INFO | 支付网关回调处理 |
| 事件消费 | DEBUG | event_worker 处理订单事件 |
| 熔断器 | WARN | CircuitBreaker 开/半开/关状态变更 |
| Redis 降级 | WARN | Redis 不可用时自动降级通知 |
| 实例心跳 | TRACE | 每 10s 心跳更新(生产环境建议不输出) |
7.4 metrics 端点
# OpenTelemetry OTLP 导出(需要配置 OTLP exporter endpoint)
# 导出指标:gRPC 请求计数/延迟、DB 连接池状态、熔断器状态、事件队列深度
8. 升级流程
8.1 标准升级(单实例)
# 1. 拉取最新代码
cd /opt/rustbill
git pull origin main
# 2. 编译新版本
cargo build --release -p rustbill-server
# 3. 构建前端(如有变更)
cd web-admin && npm install && npm run build
cp -r dist ../rustbill-server/admin-dist/
# 4. 停止服务
sudo systemctl stop rustbill
# 5. 替换二进制
cp target/release/rustbill-server /opt/rustbill/
# 6. 启动服务
sudo systemctl start rustbill
# 7. 验证
sudo systemctl status rustbill
curl -s http://localhost:50051/health
8.2 零停机升级(多实例 + 反向代理)
# 前提:多实例部署,反向代理轮询
# 1. 逐个从负载均衡摘除实例 → 升级 → 加回
# 2. Leader 选举自动切换:关闭实例的 leader role 自动过期
# 3. 最后验证所有实例正常
8.3 数据库迁移
迁移在 leader 实例启动时自动运行。如果迁移失败,leader 持有 migration 角色锁阻止其他实例启动。
手动查看迁移状态:
psql -d rustbill -c "SELECT version, description, installed_on, success FROM _sqlx_migrations ORDER BY version;"
9. 故障排查
9.1 数据库连接失败
错误: PoolTimedOut / connection refused
- 检查 PostgreSQL 是否运行:
sudo systemctl status postgresql - 验证
pg_hba.conf允许应用用户连接:sudo -u postgres psql -c "SHOW hba_file;" - 使用
config.toml中的连接参数测试:psql "postgres://rustbill:password@localhost:5432/rustbill" -c "SELECT 1;" - 检查防火墙规则:
sudo ufw status - 连接池耗尽:增大
[db].max_connections或缩短idle_timeout_secs
9.2 端口被占用
# 查找占用端口的进程
sudo lsof -i :50051
# 或
sudo ss -tlnp | grep 50051
# 停止占用进程
sudo kill -TERM <PID>
9.3 systemd 启动失败
# 查看最近日志
sudo journalctl -u rustbill -n 50 --no-pager
# 持续跟踪
sudo journalctl -u rustbill -f
# 常见原因
# - 配置文件缺失或格式错误(config.toml)
# - JWT secret 未设置或过短(< 32 字符)
# - bootstrap admin_password 未设置
# - 数据库 URL 错误或数据库不存在
# - 端口已被占用
9.4 Admin 管理后台白屏
- 硬刷新浏览器(
Ctrl+Shift+R) - 检查
admin-dist/index.html是否存在:ls /opt/rustbill/admin-dist/ - 检查 Nginx/Caddy 配置:
/admin*路径是否正确代理到localhost:50051 - 检查浏览器控制台是否有 JS 资源 404 错误
- 重新构建并部署 admin-dist:
cd web-admin && npm run build && cp -r dist /opt/rustbill/rustbill-server/admin-dist/
9.5 插件无法加载
- 检查 Admin UI → Plugins 页面中对应接口实例的
enabled状态 - 检查
plugins/目录下.rn文件是否存在 - 检查
plugin_interfaces表的script_source列是否为空 - 检查服务器日志中
PluginScanner的 WARN/ERROR 日志 - Rune 脚本语法错误:脚本编译失败但服务器仍可正常运行,错误仅影响对应插件
9.6 HTTPS 证书问题
# Caddy 自动管理证书,确认:
# 1. DNS 解析正确:dig your-domain.com
# 2. 防火墙开放 80/443 端口:sudo ufw allow 80/tcp && sudo ufw allow 443/tcp
# 3. Caddy 日志:sudo journalctl -u caddy -f
9.7 内存使用过高
# 查看当前内存使用
sudo systemctl show rustbill -p MemoryCurrent
# 调整 systemd 资源限制
sudo systemctl edit rustbill
# 添加或修改:
# [Service]
# MemoryHigh=768M
# MemoryMax=1.5G
sudo systemctl daemon-reload
sudo systemctl restart rustbill
9.8 gRPC 调用超时
# 1. 检查服务是否可达
grpcurl -plaintext localhost:50051 list
# 2. 检查请求是否进入(日志级别调整为 debug)
RUST_LOG=rustbill_server=debug cargo run -p rustbill-server
# 3. 检查 ConcurrencyLimit 是否已达上限(默认 30)
# 增大 max_concurrency:在 config.toml [server] 节设置 max_concurrency = 100
# 4. 检查数据库连接池是否耗尽
# 增大 [db].max_connections 或检查慢查询
9.9 gRPC-Web trailers-only 错误
浏览器收到 200 OK 但 grpc-status header 非 0。这是 gRPC 错误的标准传递方式。
- 检查
grpc-statusheader 值(gRPC 状态码) - 检查
grpc-messageheader 值(错误描述) - 常见原因:认证失败(session 过期/JWT 过期)、参数校验失败、权限不足
9.10 Redis 故障降级
当 Redis 不可用时,系统自动降级:
| 功能 | Redis 正常 | Redis 降级 |
|---|---|---|
| Session 缓存 | Redis cache-aside | 纯 DB 查询 |
| 速率限制 | 跨实例共享计数 | 单实例内存计数 |
| 分布式锁 | Redis SET NX | PG advisory lock |
降级时系统正常运行但跨实例协调能力下降(速率限制不跨实例、锁竞争通过 PG 完成)。日志中会出现 WARN 级别的 Redis 连接失败信息。
10. 环境变量参考
| 变量 | 说明 | 示例 |
|---|---|---|
RUST_LOG | tracing 日志级别过滤器 | info / rustbill_server=debug / warn |
RUST_LOG_FORMAT | 日志输出格式 | 不设置=彩色文本 / json=JSON 格式 |
DATABASE_URL | PostgreSQL 连接(覆盖 config.toml) | postgres://user:pass@host/db |
CONFIG_FILE | 主配置文件路径 | /etc/rustbill/config.toml |
LOCAL_CONFIG_FILE | 本地覆盖配置路径 | /etc/rustbill/config.local.toml |
PROTOC | protoc 二进制路径(编译时需要) | /usr/local/bin/protoc |
CARGO_BUILD_JOBS | Cargo 并行编译任务数 | 1(低内存环境限制) |
11. 安全检查清单
-
config.toml中[jwt].secret已设置为 >= 32 字符的随机字符串(非"change-me-in-production") -
[bootstrap].admin_password已设置为强密码 - 生产环境
[session].cookie_secure已设为true(经 HTTPS 代理) - 生产环境
CorsLayer已配置allowed_origins白名单(非 permissive) - 防火墙仅开放必要端口(80/443,禁止 50051 直接暴露公网)
- systemd 安全加固已启用(
NoNewPrivileges=yes、ProtectSystem=strict) - 数据库用户密码不为默认值
-
config.toml和config.local.toml文件权限为600(仅 owner 可读写) - 备份 cron 任务已配置并验证
-
.claude/目录在.gitignore中(防止 Agent worktree 被提交)
12. 常见部署拓扑
12.1 最小部署(单机)
Client → Caddy (:443) → rustbill-server (:50051) → PostgreSQL (:5432)
↓ (Admin SPA 内嵌)
↓ (Consumer SPA 静态文件由 Caddy 直接 serve)
12.2 带 Redis 的中型部署
Client → Caddy (:443) → rustbill-server (:50051) → PostgreSQL (:5432)
→ Redis (:6379)
12.3 多实例高可用部署
┌→ rustbill-server-1 (:50051) ──┐
Client → Caddy LB ─┼→ rustbill-server-2 (:50051) ──┼→ PostgreSQL (Citus 多 coordinator)
└→ rustbill-server-3 (:50051) ──┘→ Redis (实例间不共享)
多实例模式下:
- Leader 选举通过 PG
leader_role表锁进行(迁移/事件消费/计费各独立角色) - 支付回调通过
SELECT FOR UPDATE原子化防重 - 事件队列通过
FOR UPDATE SKIP LOCKED并发消费 - 熔断器按 host 独立维护
- 速率限制在 Redis 启用时跨实例共享,降级后单实例独立计数
13. 插件脚本热更新
编辑插件脚本后无需重启服务:
- Admin UI → Plugins → 接口实例 → 编辑脚本源码
- 保存到数据库
plugin_interfaces.script_source ScriptEngine.evict(key)自动清除缓存- 下次调用该插件时自动重新编译并执行新版本
# 也可以手动替换 plugins/*.rn 文件
# PluginScanner 每 5 分钟后台任务检测文件变化
# 文件系统版本 > DB 版本时自动更新 plugins 表
14. 金额精度说明
所有金额使用 DECIMAL(12,2) 数据库类型,Rust 端以 rust_decimal::Decimal 承载,序列化始终保留 2 位小数。
涉及金额的 6 张表:
| 表 | 金额列 |
|---|---|
products | price |
orders | total_amount |
invoices | amount、paid_amount |
payments | amount |
customers | balance、credit_limit |
balance_transactions | amount、balance_before、balance_after |