[Java Sharing Inn] In this article, I will learn about JD Retail's open source AsyncTool, which can completely solve the problem of asynchronous programming.

I. Introduction

This chapter mainly follows the previous article on CompletableFuture. If you want to know more, you can go to the case first:

https://juejin.cn/post/7091132240574283813

CompletableFuture has provided common asynchronous orchestration solutions such as serial and parallel, but in There are still many deficiencies in the details, such as callbacks and complex sequences, which are stretched.


I have paid attention to AsyncTool, an open source tool with a good number of stars on Gitee before:

https://gitee.com/jd-platform-opensource/asyncTool

is written by a senior engineer of Jingdong Retail, providing a very rich asynchronous arrangement Function, and has passed the internal test of JD.com, it is the encapsulation and complement of CompletableFuture, and it is very good after a trial.


Two, usage

1. Introduction

1) Not recommended: the introduction of maven, this is a pit, objective reasons often lead to dependency download failure, not recommended; 2), Recommended

: download the source code directly, because the amount of code is very small, only a few core classes and test classes .

As shown in the figure below, just copy the downloaded source code, put the core code in the java directory, and put the test code in the test directory.

111.jpg


2. Write workers

1) Worker is an idea in AsyncTool, which is specially used to process tasks, such as query, rpc call and other time-consuming operations. A task is a worker; 2) Building a worker is very simple, only need to implement the IWorker and ICallback

interfaces ;

3), here, we undertake the case of the previous article, and create workers for querying the twenty-four solar terms and constellations respectively;

4), the begin method will be executed when the construction starts, and the result method will be executed after the result is obtained , the action method is where specific tasks are processed, and general business is written here, and the defaultValue method provides the default value returned when a timeout exception occurs.

1), twenty-four solar terms worker

package com.example.async.worker;

import cn.hutool.http.HttpUtil;
import com.jd.platform.async.callback.ICallback;
import com.jd.platform.async.callback.IWorker;
import com.jd.platform.async.executor.timer.SystemClock;
import com.jd.platform.async.worker.WorkResult;
import com.jd.platform.async.wrapper.WorkerWrapper;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 二十四节气worker
 * </p>
 *
 * @author 福隆苑居士,公众号:【Java分享客栈】
 * @since 2022-04-27 18:01
 */
@Slf4j
public class TwentyFourWorker implements IWorker<String, String>, ICallback<String, String> {
    
    

   public static final String APPKEY = "xxxxxx";// 你的appkey
   public static final String URL = "https://api.jisuapi.com/jieqi/query";

   @Override
   public void begin() {
    
    
      // System.out.println(Thread.currentThread().getName() + "- start --" + System.currentTimeMillis());
   }

   @Override
   public void result(boolean success, String param, WorkResult<String> workResult) {
    
    
      if (success) {
    
    
         System.out.println("callback twentyFourWorker success--" + SystemClock.now() + "----" + workResult.getResult()
               + "-threadName:" +Thread.currentThread().getName());
      } else {
    
    
         System.err.println("callback twentyFourWorker failure--" + SystemClock.now() + "----"  + workResult.getResult()
               + "-threadName:" +Thread.currentThread().getName());
      }
   }

   /**
    * 查询二十四节气
    */
   @Override
   public String action(String object, Map<String, WorkerWrapper> allWrappers) {
    
    
      String url = URL + "?appkey=" + APPKEY;
      String result = HttpUtil.get(url);

      // 模拟时长
      try {
    
    
         TimeUnit.SECONDS.sleep(5);
      } catch (Exception e) {
    
    
         log.error("[二十四节气]>>>> 异常: {}", e.getMessage(), e);
      }

      return result;
   }

   @Override
   public String defaultValue() {
    
    
      return "twentyFourWorker";
   }
}

2), Constellation worker

package com.example.async.worker;

import cn.hutool.http.HttpUtil;
import com.jd.platform.async.callback.ICallback;
import com.jd.platform.async.callback.IWorker;
import com.jd.platform.async.executor.timer.SystemClock;
import com.jd.platform.async.worker.WorkResult;
import com.jd.platform.async.wrapper.WorkerWrapper;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 星座worker
 * </p>
 *
 * @author 福隆苑居士,公众号:【Java分享客栈】
 * @since 2022-04-27 18:01
 */
@Slf4j
public class ConstellationWorker implements IWorker<String, String>, ICallback<String, String> {
    
    

   public static final String APPKEY = "xxxxxx";// 你的appkey
   public static final String URL = "https://api.jisuapi.com/astro/all";

   @Override
   public void begin() {
    
    
      // System.out.println(Thread.currentThread().getName() + "- start --" + System.currentTimeMillis());
   }

   @Override
   public void result(boolean success, String param, WorkResult<String> workResult) {
    
    
      if (success) {
    
    
         System.out.println("callback constellationWorker success--" + SystemClock.now() + "----" + workResult.getResult()
               + "-threadName:" +Thread.currentThread().getName());
      } else {
    
    
         System.err.println("callback constellationWorker failure--" + SystemClock.now() + "----"  + workResult.getResult()
               + "-threadName:" +Thread.currentThread().getName());
      }
   }

   /**
    * 查询星座
    */
   @Override
   public String action(String object, Map<String, WorkerWrapper> allWrappers) {
    
    
      String url = URL + "?appkey=" + APPKEY;
      String result = HttpUtil.get(url);

      // 模拟异常
      //    int i = 1/0;

      // 模拟时长
      try {
    
    
         TimeUnit.SECONDS.sleep(5);
      } catch (Exception e) {
    
    
         log.error("[星座]>>>> 异常: {}", e.getMessage(), e);
      }

      return result;
   }

   @Override
   public String defaultValue() {
    
    
      return "constellationWorker";
   }
}

3. Asynchronous orchestration

1), create a new AsyncToolService, and declare, build, and arrange workers in it;

2), Async.beginWork is to execute asynchronous tasks, and the parameters are timeout time and worker, and the timeout time can be set shorter to see the effect; 3

) , Finally, the encapsulation result can be returned. Here, to save time for the demonstration case, return it directly with map.

package com.example.async.service;

import com.example.async.worker.ConstellationWorker;
import com.example.async.worker.TwentyFourWorker;
import com.jd.platform.async.executor.Async;
import com.jd.platform.async.wrapper.WorkerWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;

/**
 * <p>
 * AsyncTools服务
 * </p>
 *
 * @author 福隆苑居士,公众号:【Java分享客栈】
 * @since 2022-04-27 17:56
 */
@Service
@Slf4j
public class AsyncToolService {
    
    

   /**
    * 异步返回结果
    *     ---- 方式:AsyncTool并行处理
    *
    * @return 结果
    */
   public Map<String, Object> queryAsync() throws ExecutionException, InterruptedException {
    
    
      // 声明worker
      TwentyFourWorker twentyFourWorker = new TwentyFourWorker();
      ConstellationWorker constellationWorker = new ConstellationWorker();

      // 构建二十四节气worker
      WorkerWrapper<String, String> twentyFourWrapper =  new WorkerWrapper.Builder<String, String>()
            .worker(twentyFourWorker)
            .callback(twentyFourWorker)
            .param("0")
            .build();

      // 构建星座worker
      WorkerWrapper<String, String> constellationWrapper =  new WorkerWrapper.Builder<String, String>()
            .worker(constellationWorker)
            .callback(constellationWorker)
            .param("1")
            .build();

      // 开始工作,这里设定超时时间10s,测试时可以设短一点看效果。
      Async.beginWork(10000, twentyFourWrapper, constellationWrapper);

      // 打印当前线程数
      log.debug("----------------- 当前线程数 ----------------");
      log.debug(Async.getThreadCount());

      // 打印结果
      log.debug("----------------- 二十四节气 ----------------");
      log.debug("结果: {}", twentyFourWrapper.getWorkResult());
      log.debug("----------------- 星座 ----------------");
      log.debug("结果: {}", constellationWrapper.getWorkResult());

      // 返回
      Map<String, Object> map = new HashMap<>();
      map.put("twentyFour", twentyFourWrapper.getWorkResult());
      map.put("constellation", constellationWrapper.getWorkResult());

      // 关闭(spring web类应用不用关闭,否则第二次执行会报线程池异常。)
      // Async.shutDown();

      return map;
   }
}

4. Test effect

In the previous case, the result of synchronous execution was demonstrated in about 10 seconds, while the result of CompletableFuture was more than 5 seconds.
After testing here, it is found that the result of AsyncTool is also about 5 seconds, which is similar to CompletableFuture, but AsyncTool provides richer arrangements.

222.jpg

Let's increase the time-consuming task of one of the constellation workers to simulate the effect of timeout. It can be found that AsyncTool directly returns the default value set in the defaultValue method above.

333.jpg


3. Common arrangement

AsyncTool actually provides a lot of asynchronous orchestration methods, including more complex ones, but taking the small and medium-sized enterprises I have worked as an example, there is almost no complex orchestration, and the most commonly used ones are parallel and serial+parallel.

AsyncTool's QuickStart.md has made a concise description: https://gitee.com/jd-platform-opensource/asyncTool/blob/master/QuickStart.md

1), task parallelism

That is to say, the layout used in our case in this article is the one I usually use the most.

444.jpg


2), serial + parallel

This is actually a serial and parallel connection through next(), which is also used in some scenarios.

555.jpg


3) Depend on the results of other tasks

This is also a very common scenario. Task B relies on the results of task A to achieve business and finally returns.

AsyncTool also provides a very convenient way:

1) Set an id name when building worker-A in the service;

2) You can find that the second input parameter of the worker's action method is a map, which contains all wrappers;

3 ), in the action method of worker-B, get this id to get the wrapper and get the result of A.

666.jpg

777.jpg


4. Pit avoidance experience

1. Do not close the thread pool

AsyncTool provides many test classes, which contain all the layout methods, which can be viewed and verified one by one, but one point should be paid attention to during use. If it is a spring-web project, such as springboot, manual Async.shutdown() processing is not required, otherwise After it will be executed once, the thread pool will be closed. This is where many people directly copy the test code and ignore it.

888.jpg


2. Custom thread pool

This problem can be seen in the issue of AsyncTool. The author decides which thread pool to use based on the business of JD Retail. The default thread pool they use is newCachedThreadPool, which is a thread pool of unlimited length and has the feature of reuse. , according to the author, because most of Jingdong's scenes are low-time-consuming (10ms), high-concurrency, and instantaneous impact scenes, so this kind of thread pool is most suitable.

999.jpg

1010.jpg

According to my experience, different companies have different businesses and projects. Small and medium-sized enterprises often rely on enterprises and institutions to survive, and there are many third-party manufacturers. The rpc interface is often time-consuming and uncontrollable, which does not meet the requirements of Jingdong Retail. Due to the characteristics of concurrency, it is not appropriate to directly use the unlimited number of core threads of Integer.MAX_VALUE.

I suggest that small and medium-sized enterprises use a custom thread pool, adjust the final number of core threads and task queue length according to their own hardware level and pressure test results, and determine an appropriate rejection strategy, such as direct rejection or the main thread, which will be safer.


5. Example code

The complete sample code is provided to everyone, which contains the key of my extremely fast data API, 100 free calls per day, no need to register an account, first come first test, if you are slow, you can only wait for tomorrow.

Link: https://pan.baidu.com/doc/share/kJyph2LX076okHVWv38tlw-159275174957933

Extraction code: yqms



The original article is purely hand-written, if you find it helpful, please give it a thumbs up . Share work experience and interesting stories from time to time, please pay attention


if you like it .


Guess you like

Origin blog.csdn.net/xiangyangsanren/article/details/124467100