You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@Async标注的回调方法执行顺序与调用顺序相反该如何解决?

回调方法执行顺序异常修复方案

根因分析

两个回调方法均添加了@Async注解,调用时会作为独立任务提交到Spring异步线程池,线程池任务调度顺序、两个回调的网络请求耗时均存在不确定性,因此会出现后调用的sendSuccessCallback先执行完成的问题。

可选修复方案

方案1:封装顺序逻辑到同一异步方法(推荐)

如果需要保留异步能力、避免阻塞支付主流程,可以将两个回调的顺序执行逻辑封装到同一个异步方法中,仅提交一个异步任务,任务内部按固定顺序执行两个回调:

// SessionsCallbackService 新增方法
@Async
public void sendSequentialCallback(String sessionSecret, BaseCallbackRequest params) {
    // 内部按顺序调用,保证update执行完成后再执行success
    sendUpdateCallback(sessionSecret, params);
    sendSuccessCallback(sessionSecret, params);
}

// 原有两个方法移除@Async注解
public void sendUpdateCallback(String sessionSecret, BaseCallbackRequest params) {
    log.error("sendUpdateCallback");
    String url = createCallbackRequestUrl(createSessionPath(sessionSecret) + "/update");
    sendSessionCallback(url, sessionSecret, params);
}

public void sendSuccessCallback(String sessionSecret, BaseCallbackRequest params) {
    log.error("sendSuccessCallback");
    String url = createCallbackRequestUrl(createSessionPath(sessionSecret) + "/success");
    sendSessionCallback(url, sessionSecret, params);
}

调用处调整为:

SessionSuccessCallbackRequest params = new SessionSuccessCallbackRequest(userId, status);
if (!StringUtils.isEmpty(sessionSecret)) {
    sessionsCallbackService.sendSequentialCallback(sessionSecret, params);
}

该方案实现成本低,既保留异步能力不阻塞主流程,也能100%保证执行顺序。


方案2:使用CompletableFuture异步编排

如果需要保留两个方法的独立异步调用能力,可以调整方法返回值为CompletableFuture,通过异步编排保证执行顺序:

// 调整回调方法返回值
@Async
public CompletableFuture<Void> sendUpdateCallback(String sessionSecret, BaseCallbackRequest params) {
    log.error("sendUpdateCallback");
    String url = createCallbackRequestUrl(createSessionPath(sessionSecret) + "/update");
    sendSessionCallback(url, sessionSecret, params);
    return CompletableFuture.completedFuture(null);
}

@Async
public CompletableFuture<Void> sendSuccessCallback(String sessionSecret, BaseCallbackRequest params) {
    log.error("sendSuccessCallback");
    String url = createCallbackRequestUrl(createSessionPath(sessionSecret) + "/success");
    sendSessionCallback(url, sessionSecret, params);
    return CompletableFuture.completedFuture(null);
}

调用处调整为:

SessionSuccessCallbackRequest params = new SessionSuccessCallbackRequest(userId, status);
sessionsCallbackService.sendUpdateCallback(sessionSecret, params)
    // 等待update执行完成后再触发success
    .thenRun(() -> {
        if (!StringUtils.isEmpty(sessionSecret)) {
            sessionsCallbackService.sendSuccessCallback(sessionSecret, params);
        }
    });

方案3:移除@Async注解同步执行

如果对接口性能要求不高,可以直接去掉两个方法上的@Async注解,两个方法会在当前主线程按调用顺序同步执行,天然保证顺序。缺点是回调请求的耗时会直接叠加到支付接口的响应耗时上,若回调超时会影响主流程。

内容的提问来源于stack exchange,提问作者Morozov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 21:45:08