
一、总体架构蓝图:可靠消息驱动的最终一致性
面对不可靠的外部回调,我们的核心设计思想是:不信任外部通知,以我方持久化的数据为准,主动求证。
为此,我们设计的总体方案是:通过“本地消息表”持久化支付状态,采用“事件驱动+定时兜底”的混合模式触发状态核对,通过“幂等回查与指数退避”策略与第三方交互,并借助“配置中心”实现动态调控,最后通过“监控告警”确保系统健康。
端到端数据流如下:
本地消息表是整个方案的核心。它将转瞬即逝的支付状态“物化”为一条持久化的数据库记录,成为后续所有操作的“唯一真相来源”(Single Source of Truth)。
精细化表结构设计:
CREATE TABLE payment_check_msg (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_sn VARCHAR(64) NOT NULL COMMENT '业务订单号,用于反查业务',
trade_no VARCHAR(128) NULL COMMENT '第三方支付流水号',
channel VARCHAR(20) NOT NULL COMMENT '支付渠道(WECHAT, ALIPAY)',
status TINYINT NOT NULL DEFAULT 0 COMMENT '0=PENDING, 1=IN_PROGRESS, 2=SUCCESS, 3=FAILED, 4=DEAD',
try_count INT NOT NULL DEFAULT 0 COMMENT '已尝试次数',
next_retry_at DATETIME NOT NULL COMMENT '下一次重试时间点',
result_text VARCHAR(512) NULL COMMENT '最后一次查询结果描述',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_order_sn (order_sn) COMMENT '防止重复插入',
INDEX idx_status_nextretry (status, next_retry_at) COMMENT '补偿任务高效查询的核心索引'
) ENGINE=InnoDB;设计要点解读:
如何触发补偿?传统的纯定时轮询要么延迟高,要么空耗资源。我们采用“实时+兜底”的混合策略。
多实例并发控制: 在分布式环境下,必须防止多个服务实例处理同一条消息。我们采用乐观锁思想的“抢占式更新”,而非长时间持有数据库行锁的 SELECT ... FOR UPDATE。
-- 步骤1: 抢占N条到期的任务,并将其状态置为IN_PROGRESS
UPDATE payment_check_msg
SET status = 1, -- 1=IN_PROGRESS
try_count = try_count + 1,
updated_at = NOW()
WHERE status = 0 -- 0=PENDING
AND next_retry_at <= NOW()
ORDER BY next_retry_at
LIMIT 200; -- 每次处理的批次大小
-- 步骤2: 查询刚刚被自己抢占到的任务进行处理
-- (需要一种方式识别是哪个实例抢占的,比如在UPDATE中加入实例ID,或在后续查询时使用时间戳窗口)这种方式将锁竞争降到最低,大大提升了系统的吞吐能力。
抢占到任务后,补偿线程会调用第三方支付的查询接口。
重试次数 | 延迟间隔 | 策略说明 |
|---|---|---|
第1次 | 60秒 | 快速响应,应对网络瞬时抖动 |
第2次 | 5分钟 | 给予第三方系统更多恢复时间 |
第3次 | 15分钟 | 进一步拉长间隔,降低无效调用 |
3次以上 | 标记为DEAD | 停止自动重试,触发告警,转入人工处理流程 |
一个无法被有效监控和管理的系统是脆弱的。我们通过以下手段赋予系统“可治理”的能力。
我们通过**“本地消息表”为不确定性建立了可靠的锚点,以“事件驱动+定时兜底”实现了高效与稳健的平衡,用“抢占式更新与指数退避”确保了并发安全与系统礼貌,最后通过“配置中心与立体化监控”**赋予了系统强大的运维治理能力。
这套体系化方案,本质上是在不可控的外部依赖面前,将系统的主动权牢牢掌握在自己手中。它将一个被动的、可能出错的回调流程,改造成了一个主动的、可控的、最终必然一致的健壮系统,为核心业务的稳定运行提供了坚实的保障。