【Easylive】跨系统事务处理策略

发布于:2025-04-03 ⋅ 阅读:(15) ⋅ 点赞:(0)

【Easylive】项目常见问题解答(自用&持续更新中…) 汇总版

Q: 如果是一个事务里面要去1.写自己的数据库,2.去调接口写别的服务器上的数据库,在这种情况下,我应该先去写自己的数据库还是先去掉接口呢?如果调接口超时这种情况怎么办?

执行顺序建议:先本地后远程

在需要同时操作本地数据库和远程接口的事务中,我建议采用先本地后远程的执行顺序,原因如下:

  1. 性能考虑
    • 本地操作通常比远程调用更快完成
    • 先完成快速操作可以减少资源锁定时间

  2. 错误处理
    • 如果本地操作失败,可以避免不必要的远程调用
    • 本地操作失败后可直接回滚,不会留下部分完成的状态

  3. 数据一致性
    • 确保本地系统数据先处于一致状态
    • 远程调用作为后续补偿操作的基础

超时处理方案

1. 本地事务+异步通知(推荐方案)

@Transactional
public void processTransaction() {
    // 1. 先执行本地数据库操作
    localRepository.save(data);
    
    try {
        // 2. 尝试同步调用远程接口(设置合理超时时间)
        remoteService.callWithTimeout(remoteData, 3, TimeUnit.SECONDS);
    } catch (TimeoutException e) {
        // 3. 如果超时,将任务放入重试队列
        mqService.sendToRetryQueue(remoteData);
        // 本地事务仍然提交
    }
    
    // 其他本地操作...
}

配套措施
• 建立消息队列和重试机制
• 监控未完成的远程调用
• 提供人工干预接口

2. 两阶段提交模式(复杂但严格)

App LocalDB RemoteAPI 预提交(Prepare) 预提交成功 预提交请求 预提交确认 最终提交 最终提交 App LocalDB RemoteAPI

实现要点

  1. 本地数据库预提交(不实际生效)
  2. 远程服务预提交(资源预留)
  3. 确认都成功后最终提交
  4. 任一失败则回滚两边

超时具体处理策略

1. 短超时+快速失败

// 设置合理的超时时间(根据业务需求调整)
HttpClient client = HttpClient.create()
    .responseTimeout(Duration.ofSeconds(2));

2. 重试机制

RetryPolicy retryPolicy = new RetryPolicy()
    .withMaxAttempts(3)
    .withDelay(1, TimeUnit.SECONDS);
    
Failsafe.with(retryPolicy)
    .get(() -> remoteService.call(remoteData));

3. 补偿事务

@Transactional
public void mainOperation() {
    // 记录操作流水
    auditLogRepository.logOperationStart();
    
    // 本地数据库操作
    localRepository.update(data);
}

// 补偿任务(定时执行)
public void compensateOperation() {
    List<UnfinishedOperation> operations = auditLogRepository.findUnfinished();
    
    operations.forEach(op -> {
        try {
            remoteService.retry(op.getData());
            auditLogRepository.markAsCompleted(op.getId());
        } catch (Exception e) {
            // 报警通知人工处理
            alertService.notifyAdmin(op, e);
        }
    });
}

架构设计建议

  1. 最终一致性模式
    • 使用事件溯源(Event Sourcing)
    • 实现可靠事件总线

  2. 服务降级方案

    public void process() {
        try {
            remoteService.call(remoteData);
        } catch (Exception e) {
            // 降级处理:记录到本地待处理表
            fallbackRepository.savePendingTask(remoteData);
            // 触发告警
            monitoringService.alert("Remote call failed", e);
        }
    }
    
    
  3. 监控指标
    • 远程调用成功率
    • 平均响应时间
    • 待补偿事务数量
    • 补偿成功率

决策流程图

开始跨系统事务
是否强一致性要求?
考虑两阶段提交
先本地后远程
远程调用成功?
完成
进入补偿流程
记录失败状态
定时重试
重试成功?
人工干预

总结建议

  1. 常规场景:采用"先本地后远程+异步重试"方案
  2. 金融等严格场景:考虑两阶段提交或Saga模式
  3. 必须保证
    • 本地操作的可回滚性
    • 远程调用的幂等性
    • 完善的监控报警机制

通过这种设计,即使在远程调用超时的情况下,系统仍能保持最终一致性,同时避免长时间的资源锁定和用户等待。


网站公告

今日签到

点亮在社区的每一天
去签到