使用ARMS监控异步任务

若您的异步任务出现接口超时等异常,可以通过调用链路查看异步任务上下游以便及时处理潜在问题。ARMS默认自动监控使用Spring @Async标签实现的异步任务。如果您需要监控自定义异步任务,可以通过添加异步透传扫描包和使用ARMS SDK进行手动透传实现。

前提条件

该功能要求ARMS探针版本为v2.7.1.3或以上。升级探针的操作,请参见升级ARMS探针

监控Spring @Async标签实现的异步任务

ARMS默认自动监控使用Spring @Async标签实现的异步任务。对于下列Spring框架中默认的ExecutorTask,ARMS会自动完成增强:

  • Executor:

    • org.springframework.scheduling.concurrent.ConcurrentTaskExecutor

    • org.springframework.core.task.SimpleAsyncTaskExecutor

    • org.springframework.scheduling.quartz.SimpleThreadPoolTaskExecutor

    • org.springframework.core.task.support.TaskExecutorAdapter

    • org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor

    • org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler

    • org.springframework.jca.work.WorkManagerTaskExecutor

    • org.springframework.scheduling.commonj.WorkManagerTaskExecutor

  • Task:

    • org.springframework.aop.interceptor.AsyncExecutionInterceptor$1

    • org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda$

监控自定义异步任务

添加异步透传扫描包

您可以选择在应用设置中添加异步透传扫描包实现异步任务监控。异步透传扫描包中的Runnable、CallableSupplier接口在创建新对象时会自动捕获当前线程调用链的上下文,并在异步线程中执行时使用该调用链上下文,完成串联。

  1. 登录ARMS控制台,在左侧导航栏选择应用监控 > 应用列表

  2. 应用列表页面顶部选择目标地域,然后单击目标应用名称。

    说明

    语言列显示Java图标图标的应用为接入应用监控的应用,显示-图标的应用为接入可观测链路 OpenTelemetry 版的应用。

  3. 在左侧导航栏,单击应用设置,然后单击自定义配置页签。

  4. 高级设置区域的异步透传扫描包名对话框中,添加异步透传扫描包。

    异步透传扫描包

  5. 在页面底部,单击保存

重要

配置后需重启应用才能使功能生效。如需添加多个异步透传扫描包,可以使用英文半角逗号(,)分隔。

例如,对于通过以下示例代码创建的异步任务,您可以使用添加异步透传扫描包的方式对此异步任务进行监控。此示例代码中,异步透传包名为com.alibaba.arms.brightroar.console.service。

重要
  • 在配置时,您可以按需调整异步透传包名的范围。若需监控的异步任务过多,您可以缩小异步透传包名的范围。在本示例中,除了输入完整的异步透传包名com.alibaba.arms.brightroar.console.service以外,您还可以输入更短的前缀包名com.alibaba.arms来自动扫描此目录下的所有子透传包。但需注意的是,若前缀包名范围过大,会对性能产生影响,请谨慎操作。

  • 线程池在通过submit等方法提交Runnable对象时,如果您提交的Runnable对象是lambda表达式形式,可能会出现重复的TraceID,推荐您使用下面新建匿名对象的方法来使用本功能。4.0及以上版本的探针提交lambda表达式形式的Runnable对象不会出现此问题。

package com.alibaba.arms.brightroar.console.service;

@Service
public class NameService {

    private ExecutorService es = Executors.newFixedThreadPool(5);

    public void name() {
        es.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(System.currentTimeMillis()+ ": my name is john, " + Thread.currentThread().getId());
            }
        });
    }
}

使用ARMS SDK手动透传

当您的使用方式比较复杂,上述场景无法满足需求时,您还可以选择使用ARMS SDK进行手动透传。通过手动增强Runnable接口、Callable接口和Executor,在异步线程中完成调用链串联,实现异步任务监控。

  1. Maven项目的pom.xml中添加以下依赖。

    <dependency>
     <groupId>com.alibaba.arms.apm</groupId>
     <version>1.7.5</version>
     <artifactId>arms-sdk</artifactId>
    </dependency>
  2. 增强Runnable接口、Callable接口和Executor。您可以按需选择以下任意一种增强方式。

    方式一:增强Runnable接口和Callable接口

    使用TraceRunnable.asyncEntry()增强Runnable接口,使用TraceCallable.asyncEntry()增强Callable接口。示例代码如下:

    public class AsyncEntryExample {
    
        private final ExecutorService executor = Executors.newSingleThreadExecutor();
    
        @GetMapping(value = "/sdk-async-plugin/asyncEntry-propagation")
        public String asyncEntryAndExecute() throws Exception {
            CompletableFuture<String> future = new CompletableFuture<>();
            Runnable command = TraceRunnable.asyncEntry(() -> future.complete("asyncEntry-execute"));
            executor.execute(command);
            Thread.sleep(1000);
            return future.get();
        }
    }

    方式二:增强Executor

    使用TraceExecutors.wrapExecutorService(executor, true)增强Executor。示例代码如下:

    public class AutoExample {
    
        private final ExecutorService contextPropagationExecutor
                = TraceExecutors.wrapExecutorService(Executors.newSingleThreadExecutor(), true);
    
        @GetMapping(value = "/sdk-async-plugin/auto-context-propagation")
        public String autoWrapAndExecute() throws Exception {
            CompletableFuture<String> future = new CompletableFuture<>();
            contextPropagationExecutor.execute(() -> future.complete("auto-execute"));
            Thread.sleep(1000);
            return future.get();
        }
    }

    方式三:同时增强Runnable接口、Callable接口和Executor

    示例代码如下:

    public class ManualExample {
        private final ExecutorService traceExecutor
                = TraceExecutors.wrapExecutorService(Executors.newSingleThreadExecutor());
    
        @GetMapping(value = "/sdk-async-plugin/manual-context-propagation")
        public String manualWrapAndExecute() throws Exception {
            CompletableFuture<String> future = new CompletableFuture<>();
            traceExecutor.execute(TraceRunnable.wrap(() -> future.complete("manual-execute")));
            traceExecutor.execute(() -> "Not captured");
            Thread.sleep(1000);
            return future.get();
        }
    }

执行结果

配置完成后,您可以在调用链路详情页查看异步任务的调用链详情。具体详情,请参见调用链路查询

异步调用详情