如何建立故障安全处理每天数百万笔交易
文章全文
1)失败安全对交易意味着什么
失灵安全是指任何失灵情况都会导致安全停止或可补偿状态而不会丢失金钱和数据。目标是:- "双重借记/积分"=0。
- 丢失的事务/事件=0。
- 可预测的潜伏期/递送SLO,清晰的降解模式和DR。
基础是货币不变性(一个位置的资产负债表真理),相等性,协调的事件传递。
2)建筑原理(简称)
1.单一真相来源:资产负债表和会计-在Ledger/Wallet。周围的服务保持流程状态而不是金钱。
2.Idempotency everywhere:所有"记录"操作都接受"Idempotency-Key";重复返回相同的结果。
3.交付保证事件:outbox/CDC、队列、DLQ、dedup。
4.传奇和补偿,而不是"手动编辑"。
5.后压和优先级:系统放慢但不会崩溃。
6.默认可观察性:结构化日志、跟踪、度量。
7.多区域和DR:资产/资产放电,定期演习。
3)参考拓扑
Edge/API GW ──Command API ──App Service (Sagas)
│           │
│         (Outbox TX)
RateLimit     Outbox Table ──Publisher ──Kafka/Pulsar ──Consumers
│                      │
WAF                     └─DLQ/Replay
│
└─Ledger/Wallet (ACID, idempotent debit/credit)
│
└─CDC/Changefeed ──DWH/BI/Recon关键位置:Outbox(团队的原子记录和事件的"草稿"),Publisher(正好是一次交付),Consumers(具有去除键),DLQ/Replay(受控重播)。
4)现金不变性和一致性
资产负债表的真相是Ledger(ACID,可序列化的事务或严格的计数排序)。
现金团队:"debit","credit","hold","commit","rollback"是偶然的。
组合过程构造为传奇:- "authorize → settle → credit"(存款/设置),"request → submit → settled/failed"(付款/输出),"refund/void"(补偿)。
- 没有直接编辑资产负债表绕过Ledger。
5)相似性: 密钥设计
密钥必须唯一标识业务操作:- `bet_id+amount+currency`, `payment_intent+capture_id`, `payout_id`, `chain_txid`.
- 按键存储结果(响应缓存)。具有相同键的重播→相同的车身/状态。
- 控制不匹配:具有不同金额的相同密钥→ "IDEMPOTENCY_MISMATCH"。
6)队列,顺序和背叛
Exactly once的效果不是通过运输来实现的,而是通过使用者+去势存储(LRU/Redis/DB c TTL)来实现的。
按键保持顺序(partition key='account_id/round_id/player_id")。
对于"异构"密钥,状态转换和交换机(state machine per entity)。
DLQ是强制性的:经过N的尝试-进入一个具有人为原因的孤立主题。
7) Outbox/CDC: 为什么事件"不会丢失"
作为一项交易的一部分,我们记录服务的DB 中的业务变更和outbox中的记录。
一个单独的出版商读取outbox并发布到确认总线。
或者-DB级别的CDC(更改数据捕获)(Debezium/复制日志)。
交易之前没有"事件日志"是损失的来源。
8)背靠背压力和优先事项
令牌罐和入口配额(per tenant/brand/region)。
优先排队:现金路径高于促销/遥测。
过载时:"no new sessions/requests"模式,冻结次要幻影,保存核心。
自动降解:减少背景任务的频率,动态扩展关键操作者。
9)多区域可持续性
API和队列的资产资产,本地Ledger(或具有区域/货币冲压的全局资产)。
数据驻留:没有明确的规则,金钱/PII/日志不会交叉。
区域间事件复制是异步的,带有"区域"标记。
RPO/RTO:瞄准RPO ≤ 5分钟,RTO ≤ 30分钟;定期检查。
10)SLO/SLI和dashbords
地标(示例):- p95 'authorize/debit/credit' <150-300 ms(内部路径)。
- p95端到端"总线中的komanda→sobytiye "<1-2 s。
- 网络图书/外部事件交付p99 <5分钟。
- "丢失/重复事务"=0(合同检查)。
指标:latency p50/p95/p99, error-rate (4xx/5xx/business), consumer/queue lag, retry storms, settle lag, webhook lag, DLQ大小,'IDEMPOTENCY_MISMATCH'频率。
11)可观察性和审计
带有"trace_id","idempotency_key",Business ID和错误代码的结构化JSON逻辑。
OpenTelemetry: HTTP/gRPC/DB/总线预告片, SAG.
WORM审核:不变的关键更改日志(限制、密钥、促销标志/大奖)。
掩盖PII/秘密,区域垃圾箱,RBAC/ABAC访问日志。
12)可靠性测试
合同测试:重播/复制,订单退出,平均水平,滞后。
负载:峰值轮廓(x 10),队列稳定性和DB。
混沌案例:Ledger/钱包掉落,排队/地区,CDC延迟,"风暴"撤退。
Game Days: MTTR测量的常规DR演习和事件。
13)存储和数据
金钱下的OLTP:交易DB(RPO≈0),严格的索引,按关键实体序列化的级别。
缓存(Redis)-仅用于加速,而不适用于"真相"。TTL+jitter, cache stampede防护。
OLAP/DWH-用于报告/分析。从CDC/总线流出,OLTP上没有负载。
数据方案被验证;无市区迁移(expand/contract)。
14)复古乐团
指数backoff+jitter,RPC上的截止日期/时间。
每个层(客户→服务→消费者)上的等效重复。
Retrai配额,保护自己免受"风暴"(适当的巡回决胜局,合格的要求)。
从DLQ返回到仅限速度的"安全"窗口。
15)运输安全
mTLS到处S2S,短寿命令牌(OAuth2 CC),车身签名(HMAC/EdDSA)用于webhook。
Vault/HSM中的秘密,轮换,按品牌/区域键。
政治家least privilege,"四眼"手动操作。
16)示例合同(碎片)
排位赛球队借记
POST /v1/wallet/debit
Headers: X-Idempotency-Key: debit_pi_001, X-Trace-Id: tr_a1b2
{
"account_id":"acc_42",  "amount":{"minor_units":5000,"currency":"EUR"},  "reason":"payout",  "reference_id":"po_001"
}
→ 200 { "status":"committed", "entry_id":"e_77" }
(重复→相同答复)来自outbox的事件
json
{
"event_id":"uuid",  "event_type":"wallet.debit.committed",  "occurred_at":"2025-10-23T16:21:05Z",  "account_id":"acc_42",  "amount_minor":5000,  "currency":"EUR",  "reference_id":"po_001",  "idempotency_key":"debit_pi_001",  "schema_version":"1.3.0"
}17)支票单
平台/操作员
- 资产负债表的真相是一位Ledger;没有解决办法。
- 所有带有"Idempotency-Key"的写作操作;按键存储响应。
- Outbox/CDC对所有域条目、DLQ和托管重置。
- 具有优先级、后压、降级模式的队列。
- Partition-keys是按业务密钥选择的;消费者是同等的。
- SLO-dashbords, OpenTelemetry, WORM审核。
- 定期DR/xaoc演习,合同/负载测试。
- 数据驻留、加密、Vault/HSM、密钥旋转。
提供商/集成商
- 发送"Trace-Id"/"Idempotency-Key",准备重新交付。
- Webhooks已签名并被重复数据消除。
- 电路/合同版本必须遵守(semver, deprecation)。
18)红旗(反模式)
在Ledger中没有团队的情况下,平衡随网络而变化。
缺乏相同能力→双重注销/贷款。
发布绕过outbox/CDC的事件。
没有后压的巨石:交通高峰压倒一切。
OLTP和报告的混合:BI击中了战斗DB。
缺少DLQ/反射;"安静"摄入错误。
没有PII/货币的区域隔离;共享多个品牌的密钥。
手动编辑DB中的平衡/状态。
19)结果
Fail-safe每天处理数百万个事务是关于不变性和纪律:单一的真相来源,相反的命令,传奇和outbox/CDC,队列中的顺序和祖先,可观察性和可管理的退化。添加访问任务、DR实践和定期演习-并获得一个系统,其中资金快速移动,只有一次,事件不会丢失,流量增加和中断成为风险驱动而不是惊喜。
