常见问题
技术选型
为什么选择 C++23 而不是 Go 或 Rust?
三个核心原因:
- 极致性能要求:AuthNexus 的热路径(TCP mTLS 握手、Token 验证、心跳处理)需要亚毫秒级延迟。C++ 配合 asio 协程和零拷贝设计,在高并发场景下有最可控的性能表现。
- 生态成熟度:OpenSSL(TLS/PKI/OCSP)、asio(异步 IO)、Lua 5.4(云函数沙箱)、libpq/libpqxx(PostgreSQL)均为经过数十年生产验证的 C/C++ 库。
- 线程模型精确控制:AuthNexus 的六类线程域(IO / Logic / DB / Crypto / CloudFunction / Background)需要精确的 executor 绑定和隔离,C++ 的协程 + asio executor 体系提供了最灵活的控制能力。
C++23 的协程、std::expected、std::format 等特性显著改善了开发体验,代码可读性接近现代语言。
为什么用自定义二进制协议而不是 gRPC?
| 维度 | 自定义协议 | gRPC |
|---|---|---|
| 握手开销 | 直接走 mTLS TCP,无 HTTP/2 帧开销 | 需要 HTTP/2 连接建立 + HPACK 头压缩 |
| 心跳效率 | 几个字节的自定义包 | 至少包含 HTTP/2 帧头 + protobuf 包装 |
| TLS 控制 | 完全控制证书验证、OCSP stapling、CA 热替换 | 受限于 gRPC TLS 集成 |
| 推送模型 | Reader 线程持续读取服务端推送 | Server-streaming RPC,语义不同 |
| 协议演进 | 版本号字段,完全自主 | 受 protobuf 版本兼容性约束 |
总结:对于高频低延迟的认证心跳场景,自定义协议的每字节都有意义,gRPC 的通用性反而是负担。
为什么密码用 HMAC-SHA256 而不是 Argon2/bcrypt?
这是 有意为之的设计取舍,而非安全疏忽。
- 场景差异:Argon2/bcrypt/scrypt 针对的是密码哈希离线暴力破解场景,通过高计算成本抵抗攻击。AuthNexus 的场景是在线实时认证,每秒可能处理数千次登录请求。
- 性能优先:HMAC-SHA256 验证耗时 < 0.5ms,而 Argon2id 的推荐参数下验证耗时 50-200ms,差距两个数量级。
- 安全补偿:密码安全不仅靠哈希算法。AuthNexus 通过 mTLS(传输层)、OCSP(证书状态)、Epoch(全局失效)、黑名单(即时阻断)、速率限制(暴力破解防护)等多层机制共同保障。
格式:$authnexus-fast-hmac-sha256$v=1$<salt_hex>$<hash_hex>,16 字节 salt + 32 字节 hash + 域分离字符串。
部署与迁移
如何从 SQLite 迁移到 PostgreSQL?
SQLite 和 PostgreSQL 的 schema 保持对齐(schema/ 目录下两套 schema 文件并行维护),迁移步骤:
- 导出数据:使用
seed_tool或自定义脚本从 SQLite 导出数据 - 初始化 PG:使用
schema/postgres_control_plane_schema.sql初始化 PostgreSQL 数据库 - 导入数据:将导出数据插入 PG
- 修改配置:更新
control_plane_app启动参数,指定 PG 连接串 - 验证:运行基本功能测试确认数据完整性
TIP
由于两套 schema 对齐维护,字段名、约束、索引完全一致,数据迁移本质是行级拷贝。
如何水平扩展?
AuthNexus 的扩展模型:
┌─── server_app (节点 A)
SDK ──mTLS TCP──────┤
├─── server_app (节点 B)
│
└─── server_app (节点 C)
│
┌─────────┘
▼
control_plane_app (单实例)- server_app 水平扩展:部署多个
server_app节点,每个节点独立连接 CP,通过负载均衡器分发 SDK 连接 - control_plane_app 单实例:CP 为管理面,不在热路径上,单实例足以支撑大量节点
- 数据同步:各节点通过四通道机制与 CP 保持同步,本地缓存保证请求处理不依赖 CP 实时响应
Control Plane 断连时会发生什么?
server_app 节点具备离线自治能力:
| 功能 | CP 断连时行为 |
|---|---|
| 用户登录 | 正常(基于本地 Runtime DB) |
| Token 验证 | 正常(本地计算) |
| 心跳处理 | 正常 |
| 云函数执行 | 正常(脚本已缓存到本地) |
| 配置更新 | 暂停(无法拉取新配置) |
| Epoch 同步 | Delta Puller 切换到快速轮询(800ms/5000ms) |
| SSE 事件 | 断开,自动重连 |
| 新用户注册 | 取决于是否需要 CP 参与 |
设计原则:业务热路径不依赖 CP 实时在线。CP 断连影响的是管理操作和增量同步,不影响已缓存的业务数据服务。
安全相关
可以不使用 mTLS 吗?
不可以。mTLS 是 AuthNexus 安全模型的基石,不是可选项。
mTLS 在 AuthNexus 中承担的职责远超传输加密:
- 应用隔离:证书 SAN 中的
urn:authnexus:app:<id>在 TLS 层就实现了租户隔离 - 设备身份:客户端证书 = 设备身份凭证,与用户密码形成双因素
- 证书生命周期:通过 OCSP stapling + must-staple 实现实时证书吊销
- CA 热替换:generation 机制支持 CA 在线轮换
移除 mTLS 意味着丧失上述所有能力,安全模型会彻底崩塌。
如何调试 TLS 握手问题?
分步排查:
1. 开启调试日志(临时):
将日志级别调为 debug,查看完整的 TLS 握手过程。
2. 检查证书链:
# 查看证书信息
openssl x509 -in client.crt -text -noout
# 验证证书链
openssl verify -CAfile tcp_server_ca.pem client.crt
# 检查证书有效期
openssl x509 -in client.crt -dates -noout3. 常见问题清单:
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
certificate has expired | 证书过期 | 重新签发证书 |
unable to verify the first certificate | CA 链不完整 | 补全中间 CA 证书 |
unsupported certificate purpose | EKU 不匹配 | 确保包含 clientAuth EKU |
OCSP response not yet valid | 时钟偏差 | 同步系统时间 |
certificate revoked | 证书已被吊销 | 重新签发或恢复证书 |
| SAN 不匹配 | app_id 错误 | 检查证书的 urn:authnexus:app:<id> URI SAN |
4. 恢复日志级别:调试完毕后务必恢复 info 级别。
开发与扩展
如何添加新的配置类型?
- 注册配置类型:在
src/control_plane/service/config_registry.h中注册新类型名称 - 实现 CP 侧逻辑:
- Pull 端点:节点拉取配置内容
- ACK 端点:节点确认配置已应用
- Error 端点:节点上报应用错误
- 实现节点侧逻辑:
- 在
ControlPlaneAgent中添加配置拉取与应用逻辑 - 处理 SSE hint 触发拉取
- 在
- 更新 Schema:如需持久化,在 SQLite 和 PostgreSQL schema 中同步添加表/字段
现有配置类型可作为参考模板:server_runtime_settings、app_policy、app_variables 等。
如何添加新的测试?
- 在
tests/目录下创建新文件,命名为*_tests.cpp - 使用 GoogleTest 框架编写测试
- 重新运行 CMake configure(GLOB 重扫)
// tests/my_feature_tests.cpp
#include <gtest/gtest.h>
TEST(MyFeature, BasicCase) {
EXPECT_EQ(1 + 1, 2);
}# Reconfigure(让 GLOB 扫描到新文件)
cmake -B build -S .
# 构建并运行
cmake --build build --config Release --target unit_tests --parallel
ctest --test-dir build -C Release --output-on-failure -R "MyFeature"无需修改 tests/CMakeLists.txt,GLOB 自动收集。
性能相关
最大并发连接数是多少?
没有硬编码上限,取决于硬件和线程配置:
| 硬件规格 | 推荐线程配置 | 预期并发 |
|---|---|---|
| 2 核 4G | auto(最小档) | 500-1000 |
| 4 核 8G | auto | 2000-5000 |
| 8 核 16G | auto | 5000-10000 |
| 16+ 核 32G+ | auto 或手动调优 | 10000+ |
CLI 默认 --auto 模式按 CPU 核数自动规划线程数(4 档),也可通过参数手动覆盖:
# 手动指定线程数
.\server_app.exe -i 4 --logic-threads 4 -d 2 -c 2关键瓶颈通常在 TLS 握手(CPU 密集)和数据库操作(IO 密集),线程池隔离确保它们互不干扰。
Token 验证性能如何?
HMAC-SHA256 Token 验证耗时 < 0.5ms,在 crypto_threads 池中执行。实测在 8 核机器上可支撑每秒数万次验证,不构成性能瓶颈。
运维常见问题
节点注册后无法连接 CP 怎么办?
按顺序排查:
- 确认 CP 的南向端口(默认 9091)网络可达
- 确认节点 mTLS 客户端证书由
cp_node_client_ca签发 - 确认证书未过期且未被吊销
- 检查 CP 日志中是否有 TLS 握手拒绝记录
- 确认 CP 已完成 PKI 初始化(setup 流程)
OCSP 拉取失败但业务不受影响?
正常现象。OCSP Staple 有本地缓存机制:
- 缓存在
nextUpdate有效期内仍可用 - OCSP 通道(Channel 4)独立于 mTLS 通道,fetch 失败不影响已建立的连接
- 只有当缓存过期且 must-staple 开启时,新的 SDK 握手才会受影响
日志文件增长过快?
- 确认日志级别为
info(默认)。debug级别日志量是info的 10-100 倍 - 配置日志轮转(参见运维手册)
- 检查是否有持续的错误日志输出(如 TLS 握手失败循环)
- 生产环境严禁长期开启
debug或trace
如何验证四通道通讯是否正常?
| 通道 | 验证方法 |
|---|---|
| Channel 1 (Command) | 管理后台查看节点心跳时间是否更新 |
| Channel 2 (SSE) | 修改配置后检查节点是否在秒级内拉取 |
| Channel 3 (Delta) | Bump epoch 后检查节点是否同步 |
| Channel 4 (OCSP) | 检查节点日志中 OCSP fetch 状态 |
如果 Channel 2 SSE 断开,Channel 1 仍可工作(节点轮询拉取),Channel 3 Delta Puller 自动切换到快速轮询,业务不受影响,但配置下发延迟会增加。
Demo 模式的数据会影响生产环境吗?
不会。Demo 模式通过 MSW (Mock Service Worker) 在浏览器内拦截所有 /admin/v1 请求,请求根本不会发送到真实后端。Demo 相关代码(MSW、mocks、seed 数据)仅在 VITE_DEMO_MODE=true 时动态导入,不会出现在生产构建产物中。