四通道通讯
Control Plane 与 Server App 之间通过四条独立通道通讯,各自承担不同职责,互不干扰。
┌───────────────────┐
│ Control Plane │
│ App │
└──┬──┬──┬──┬───────┘
│ │ │ │
Channel ①─────┘ │ │ └─────Channel ④
Command(mTLS) │ │ OCSP(Plain HTTP)
:9091 │ │ :9092
│ │
Channel ②─────┘ └─────Channel ③
Event SSE DB Delta
:9091 :9091
│ │ │ │
┌──┴──┴──┴──┴───────┐
│ Server App │
│ (业务节点) │
└───────────────────┘Channel ① Command(mTLS HTTP)
方向:Server → CP(Server 主动调用)
传输:mTLS HTTP,默认端口 9091
用途:节点心跳、命令拉取、配置同步、结果上报。
Channel ① 是最核心的数据通道,Server 通过它完成所有与 CP 的结构化数据交互。每个端点职责单一,遵循 RESTful 风格。
端点清单
心跳与注册
POST /cp/v2/nodes/:id/checkin节点定期上报心跳,携带应用版本快照和运行指标(metrics)。CP 据此判断节点在线状态。
命令拉取
POST /cp/v2/nodes/:id/commands/pull?since=N增量拉取自 since 序号以来的新命令。命令由管理员通过后台下发(如重载配置、执行诊断等)。
命令结果上报
POST /cp/v2/nodes/:id/commands/:cmd_id/result按命令 ID 逐个上报执行结果,CP 据此更新命令状态。
配置拉取
POST /cp/v2/nodes/:id/configs/pull增量拉取新的配置项,包括 server_runtime_settings、app_policy、app_variables、app_client_ca_bundle、app_mtls_trust_bundle、cp_agent_runtime 以及 cloud_function manifest。
配置确认
POST /cp/v2/nodes/:id/configs/acks批量确认已成功应用的配置项。CP 采用单事务批量 upsert 处理确认,保证原子性。
配置错误上报
POST /cp/v2/nodes/:id/configs/errors批量上报配置应用失败的错误信息,便于管理员在后台查看和排查。
云函数拉取
GET /cp/v2/objects/cloud-functions/:name?app_id=按名称和 app_id 拉取云函数脚本体。节点本地缓存脚本,仅在 manifest 变更时重新拉取。
实现细节
Server 端由 ControlPlaneAgent(后台 strand)负责调度所有 Channel ① 请求。底层通过 co_await asio::post(cp_io) 将 blocking 的 httplib 调用派发到 cp_io_threads 池,完成后自动回到 BackgroundRuntime。
Channel ② Event SSE
方向:CP → Server(CP 主动推送)
传输:SSE(Server-Sent Events),共用 mTLS 端口 9091
用途:实时推送事件提示(hint),触发 Server 通过 Channel ① 拉取详细数据。
设计哲学
Event SSE 只传递轻量级提示(hint),不携带业务数据。Server 收到 hint 后通过 Channel ① 按需拉取完整数据。这种设计:
- 避免 SSE 消息过大
- 保证数据一致性(拉取是幂等的)
- SSE 降级时 Server 自动切换到轮询模式
连接端点
GET /cp/v2/nodes/:id/events事件类型
| 事件名 | 含义 | Server 响应动作 |
|---|---|---|
command.pending | 有新命令待执行 | 调用 commands/pull 拉取 |
config.pending | 有新配置待应用 | 调用 configs/pull 拉取 |
blacklist.changed | 黑名单发生变更 | 触发 Delta Puller 立即拉取 |
epoch.bumped | 认证 epoch 已递增 | 触发 Delta Puller 立即拉取 |
连接管理
- 每节点一连接:新连接到达时自动踢掉旧连接,避免重复推送
- Keep-alive:每 30 秒发送 ping 消息维持连接
- 健康探针:Server 端监控连接状态,用于判断是否降级
- 队列溢出策略:事件队列满时丢弃最旧的事件(新事件优先)
实现细节
CP 端使用 cp::event::EventBus(进程内 pub/sub)+ httplib chunked content provider 实现 SSE 流。
Server 端由 CpEventSubscriber(IBackgroundService 实现)在专用的 sse_pool(固定 1 线程)上运行。SSE 连接通过 blocking httplib::Get 持续数小时甚至数天,独占一个线程。这正是 sse_pool 与 cp_io_threads 物理隔离的原因——避免长连接占用短任务 burst 池的线程。
Channel ③ DB Delta
方向:Server → CP(Server 主动拉取)
传输:复用 Channel ① 的 mTLS HTTP 连接
用途:增量同步安全关键数据(认证 epoch 变更、黑名单变更)。
同步表
| 表名 | 内容 |
|---|---|
auth_epoch_changes | 认证 epoch 变更记录 |
server_blacklist_changes | 服务端黑名单变更记录 |
RuntimeSecurityDeltaPuller
RuntimeSecurityDeltaPuller 持续增量拉取上述变更表,将数据落入本地 Runtime DB 和内存缓存。
自适应拉取间隔
拉取间隔根据 Channel ② 的健康状态动态调整:
| Channel ② 状态 | 拉取间隔 |
|---|---|
| 健康(SSE 连接正常) | 5 秒 / 30 秒(不同数据类型) |
| 降级(SSE 连接断开) | 800 毫秒 / 5000 毫秒 |
当 SSE 连接健康时,关键变更由 blacklist.changed / epoch.bumped 事件实时触发拉取,定时轮询仅作为兜底。当 SSE 降级时,DeltaPuller 自动缩短轮询间隔以弥补实时性损失。
数据流向
CP Control DB → HTTP Response → DeltaPuller → Runtime DB + 内存缓存Server 的运行时黑名单以本地 Runtime DB 为单一真相来源,不依赖 CP 下发快照。
Channel ④ OCSP(Plain HTTP)
方向:Server → CP(Server 主动请求)
传输:Plain HTTP(非 TLS),独立端口,默认 9092
用途:为 SDK 客户端提供 OCSP Stapling 所需的证书吊销状态证据。
为什么是 Plain HTTP?
OCSP 协议自身已通过 RSA-SHA256 签名保护响应完整性(RFC 6960),传输层无需再加 TLS 加密。使用 Plain HTTP 有两个优势:
- 降低开销:免去 mTLS 握手成本
- 端口隔离:独立端口和线程池(
ocsp_thread_pool_count),与 Channel ①/② 的 mTLS 连接完全不争用 worker
OCSP 端点
POST /ocsp/遵循 RFC 6960 标准 OCSP 请求/响应格式。
OcspStaplingManager
Server 端的 OcspStaplingManager 负责周期性获取 OCSP 响应并缓存:
- 刷新周期:约 15 分钟,根据响应中的
nextUpdate字段自适应调整(取nextUpdate / 2) - 缓存策略:在
nextUpdate有效期内使用缓存的 OCSP 响应 - Staple 到握手:将 OCSP 响应附加到 TLS 握手过程,供 SDK 验证
CP 端 OCSP 响应器
CP 的 OcspResponder 对每个请求现场签发 OCSP 响应,不做服务端缓存。签名使用 RSA-SHA256。
吊销处理(Plan C 设计)
当节点证书被吊销时,OCSP 响应会返回 revoked 状态。此时 Server 的行为:
- 继续 staple revoked 响应给 SDK——SDK 的 must-staple 验证会拒绝握手,业务面 fail-closed
- 不自行关停——管控通道(mTLS
/cp/v2/*、SSE、Delta)保持存活 - 可恢复——管理员在后台 re-enable 证书后,节点自动恢复正常
这一设计确保了:
- 被吊销节点的业务立即中断(SDK 拒绝连接)
- 管控通道保持畅通,便于远程恢复
- 无需人工到场重启节点
SDK 端验证
SDK 在 TLS 握手阶段使用 ocsp_signer_ca.pem(即 tcp_server_ca 信任锚)验证 OCSP 响应:
- 验证 OCSP 响应签名
- 校验时间窗(maxsec=1800s)
- 检查
cert_status(good / revoked / unknown) - must-staple 策略:无 staple 时直接拒绝握手
通道间协作
四条通道协同工作的典型场景:
场景一:配置变更下发
1. Admin 修改应用策略
2. CP 写入 Control DB
3. CP 通过 Channel ② 推送 config.pending 事件
4. Server 收到事件,通过 Channel ① 调用 configs/pull
5. Server 应用配置,通过 Channel ① 调用 configs/acks 确认场景二:节点证书吊销
1. Admin 在后台吊销节点证书
2. CP 更新证书状态
3. Server 通过 Channel ④ 获取 OCSP 响应(revoked)
4. Server 继续 staple revoked 响应
5. SDK 连接时 must-staple 验证失败,拒绝握手
6. 管控通道(Channel ①②③)保持正常,等待恢复场景三:SSE 降级兜底
1. Channel ② SSE 连接断开
2. Channel ③ DeltaPuller 自动缩短轮询间隔(800ms/5000ms)
3. 安全关键数据仍能及时同步
4. SSE 重连后自动恢复正常间隔