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

如何在Mutiny超时后获取长运行语句的堆栈跟踪?

解决Mutiny超时时获取长运行任务堆栈的问题

你当前的核心问题是:用Mutiny包装异步长运行任务后,触发超时只能拿到超时异常的堆栈,无法定位到任务内部(比如Thread.sleep)的执行位置,试过捕获异常、onCancellation、onTermination等方法都无法获取有效堆栈。

以下是几种可行的解决方案:

方法1:实时dump长运行任务线程的堆栈

在任务执行时保存线程引用,超时后主动获取该线程的当前堆栈,能直接看到任务执行到哪一步。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import io.smallrye.mutiny.Uni;

public class TimeoutStacktraceExample {
    public static void main(String[] args) {
        AtomicReference<Thread> workerThread = new AtomicReference<>();

        Uni.createFrom()
            .completionStage(CompletableFuture.supplyAsync(() -> {
                workerThread.set(Thread.currentThread());
                try {
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    // 任务被中断时打印中断堆栈
                    e.printStackTrace();
                }
                return "item";
            }))
            .ifNoItem().after(java.time.Duration.ofMillis(100))
                .failWith(() -> {
                    // 超时时获取并打印任务线程的当前堆栈
                    Thread thread = workerThread.get();
                    if (thread != null) {
                        System.err.println("=== 长运行任务当前执行堆栈 ===");
                        for (StackTraceElement elem : thread.getStackTrace()) {
                            System.err.println("\tat " + elem);
                        }
                    }
                    return new TimeoutException("任务执行超时");
                })
            .onFailure().invoke(ex -> {
                System.err.println("=== 超时异常堆栈 ===");
                ex.printStackTrace();
            })
            .onFailure().recoverWithItem("error")
            .await().atMost(java.time.Duration.ofSeconds(30));
    }
}

方法2:记录任务启动时的调用堆栈

如果不需要实时执行位置,只需要知道任务是从哪里触发的,可以在任务启动时记录完整调用堆栈,超时后直接输出。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import io.smallrye.mutiny.Uni;

public class TaskStartStacktraceExample {
    public static void main(String[] args) {
        AtomicReference<StackTraceElement[]> taskStartupStack = new AtomicReference<>();

        Uni.createFrom()
            .completionStage(CompletableFuture.supplyAsync(() -> {
                // 任务启动时保存当前堆栈
                taskStartupStack.set(Thread.currentThread().getStackTrace());
                try {
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "item";
            }))
            .ifNoItem().after(java.time.Duration.ofMillis(100))
                .failWith(() -> {
                    System.err.println("=== 长运行任务启动时堆栈 ===");
                    for (StackTraceElement elem : taskStartupStack.get()) {
                        System.err.println("\tat " + elem);
                    }
                    return new TimeoutException("任务执行超时");
                })
            .onFailure().invoke(ex -> ex.printStackTrace())
            .onFailure().recoverWithItem("error")
            .await().atMost(java.time.Duration.ofSeconds(30));
    }
}

方法3:取消任务并捕获中断堆栈

超时后主动取消CompletableFuture,触发任务线程中断,Thread.sleep会响应中断抛出InterruptedException,此时就能在catch块中获取包含任务执行位置的堆栈。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeoutException;
import io.smallrye.mutiny.Uni;

public class CancelOnTimeoutExample {
    public static void main(String[] args) {
        CompletableFuture<String> longRunningTask = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                System.err.println("=== 任务被中断,执行堆栈 ===");
                e.printStackTrace();
            }
            return "item";
        });

        Uni.createFrom()
            .completionStage(longRunningTask)
            .ifNoItem().after(java.time.Duration.ofMillis(100))
                .failWith(() -> {
                    // 超时后取消任务,触发线程中断
                    longRunningTask.cancel(true);
                    return new TimeoutException("任务执行超时");
                })
            .onFailure().invoke(ex -> {
                System.err.println("=== 超时异常堆栈 ===");
                ex.printStackTrace();
            })
            .onFailure().recoverWithItem("error")
            .await().atMost(java.time.Duration.ofSeconds(30));
    }
}

方案选择建议

  • 需要定位任务当前执行位置:选方法1
  • 需要排查任务的触发链路:选方法2
  • 希望终止任务同时获取堆栈:选方法3

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 23:10:29