如何在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
相关产品推荐
相关产品推荐

