By default, Application Real-Time Monitoring Service (ARMS) allows you to monitor asynchronous tasks that are created by using the @Async annotation in Spring. In addition, ARMS allows you to monitor custom asynchronous tasks by adding scan packages for asynchronous pass-through or using ARMS SDKs for manual pass-through. If an error such as a method timeout occurs when your asynchronous task is running, you can use a trace to check the upstream and downstream of the asynchronous task. This helps you troubleshoot potential errors at the earliest opportunity.

Prerequisites

The ARMS agent V2.7.1.3 or later for public preview is used. If you want to update the ARMS agent, contact the official DingTalk account for ARMS (account ID: arms160804).

Method 1: Use the @Async annotation in Spring

By default, ARMS allows you to monitor asynchronous tasks that are created by using the @Async annotation in Spring. ARMS automatically enhances the following default executors and tasks in Spring:
  • Executors:
    • 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
  • Tasks:
    • org.springframework.aop.interceptor.AsyncExecutionInterceptor$1
    • org.springframework.aop.interceptor.AsyncExecutionInterceptor$$Lambda$

Method 2: Add a scan package for asynchronous pass-through

You can add a scan package for asynchronous pass-through to the configurations of your application to monitor asynchronous tasks. After an Runnable object, a Callable object, or a Supplier object is created, the corresponding method in the scan package for asynchronous pass-through automatically captures the trace context of the current thread. Then, when threads are used in asynchronous mode, the method passes the captured trace context to the threads.

  1. Log on to the ARMS console.
  2. In the left-side navigation pane, Select Application monitoring > Applications .
  3. In the top navigation bar of the MNS console, select the region where your cluster is deployed.
  4. Log on to the Applications Page, click the application name.
  5. In the left-side navigation pane, click Application Settings. On the page that appears, click the Custom Configuration tab.
  6. In the Advanced Settings section of the tab, enter the name of a scan package for asynchronous pass-through in the Asynchronous transparent transmission scan package name field.
    Scan package for asynchronous pass-through
  7. In the lower part of the tab, click Save.
Notice You must restart the application to make the configuration take effect. To add multiple scan packages for asynchronous pass-through, separate the package names with commas (,).
For example, you can use a scan package for asynchronous pass-through to monitor the asynchronous task that is created by using the following sample code. The full name of the sample scan package for asynchronous pass-through is com.alibaba.arms.brightroar.console.service.
Notice When you add the scan package for asynchronous pass-through to your application configurations, you can adjust the specified package name as needed. If you want to monitor a large number of asynchronous tasks, you can specify the prefix of the package name instead of the full package name. In this example, in the Asynchronous transparent transmission scan package name field, you can enter the full package name com.alibaba.arms.brightroar.console.service or the prefix com.alibaba.arms. If you enter the prefix com.alibaba.arms, all scan packages for asynchronous pass-through in this directory are used for monitoring. However, a short prefix may involve an excessively large number of scan packages for asynchronous pass-through, which affects the monitoring performance. Proceed with caution when you specify the package name.
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());
            }
        });
    }
}

Method 3: Use an ARMS SDK to implement manual pass-through

If the preceding methods cannot meet your requirements in a complex monitoring scenario, you can use an ARMS SDK to implement manual pass-through. You can enhance the Runnable and Callable methods and executors to concatenate the traces in asynchronous threads to monitor asynchronous tasks.

  1. Add the following dependency to the pom.xml file in your Maven project:
    <dependency>
     <groupId>com.alibaba.arms.apm</groupId>
     <version>1.7.5</version>
     <artifactId>arms-sdk</artifactId>
    </dependency>
  2. Enhance the Runnable and Callable methods and executors as required. You can use one of the following enhancement methods as needed:
    • Method 1: Enhance the Runnable and Callable methods

      Call the TraceRunnable.asyncEntry() method to enhance the Runnable method. Call the TraceCallable.asyncEntry() method to enhance the Callable method. The following code provides an example on how to enhance the Runnable and Callable methods:
      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();
          }
      }
    • Method 2: Enhance the executors

      Call the TraceExecutors.wrapExecutorService(executr, true) method to enhance the executors. The following code provides an example on how to enhance the executors:
      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();
          }
      }
    • Method 3: Enhance the Runnable and Callable methods and executors
      The following code provides an example on how to enhance the Runnable and Callable methods and executors:
      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();
          }
      }

Result

After the configurations are complete, you can view the trace details of your asynchronous tasks on the trace details page. For more information, see Trace query.