[Java Sharing Inn] Master the parallel processing of CompletableFuture and shorten the query time exponentially.

foreword

You may encounter many such scenarios in your work. For an interface, you need to call the query method from several other services, and then encapsulate the data and return it after obtaining the required values.

It is also possible to encounter a similar situation in microservices. The interface of a service needs to use feign several times to call the method of other services to obtain data, and finally get the desired value and encapsulate it back to the front end.

In such a scenario, when the method called by one or more RPCs is time-consuming, the response of the entire interface will be very slow. After Java 8, there is a tool that is very suitable for handling this scenario, CompletableFuture.

Scenes

This chapter mainly explains the parallel processing usage of CompletableFuture to help you quickly master and apply it to practical work for this very common scenario. There are many internal usages of CompletableFuture, but most of the scenarios used by individuals are processed in parallel. Those interested in other scenarios can search on Baidu separately.

Scenario Description:

Write an interface, call the other two HTTP interfaces, get the twenty-four solar terms and constellations respectively, and finally return them together.

usage

1. Online API

We visit the Jisu Data website https://www.jisuapi.com, register an account, and you can use some of the online APIs for free. There are an average of 100 free opportunities per day, which is completely perfect for someone like me who often does some tests locally. enough.

Here, I use the API for querying twenty-four solar terms and the API for querying constellations. The case code will be provided later, or you can use mine directly.

2. Write online API queries

Here, we simulate the time-consuming situation when querying.

1), query the twenty-four solar terms

package com.example.async.service;

import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * <p>
 * Inquire about the service of 24 solar terms
 * </p>
 *
 * @author Fulongyuan Jushi, public account: [Java Sharing Inn]
 * @since 2022-04-26 15:25
 */
@Service
@ Slf4j
public class TwentyFourService {

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

   public String getResult() {
       String url = URL + "?appkey=" + APPKEY;
       String result = HttpUtil.get(url);

       // simulation time
       try {
          TimeUnit.SECONDS.sleep(5);
       } catch (Exception e) {
          log.error("[Twenty-four solar terms]>>>> Exception: {}", e.getMessage(), e);
       }

       return result;
   }
   
}

2), query the constellation

package com.example.async.service;

import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * <p>
 * Service for querying constellations
 * </p>
 *
 * @author Fulongyuan Jushi, public account: [Java Sharing Inn]
 * @since 2022-04-26 15:25
 */
@Service
@ Slf4j
public class ConstellationService {
   public static final String APPKEY = "xxxxxx";// 你的appkey
   public static final String URL = "https://api.jisuapi.com/astro/all";

   public String getResult() {

      String url = URL + "?appkey=" + APPKEY;
      String result = HttpUtil.get(url);

      // simulation time
      try {
         TimeUnit.SECONDS.sleep(5);
      } catch (Exception e) {
         log.error("[Constellation]>>>> Exception: {}", e.getMessage(), e);
      }

      return result;
   }
   
}

3. Write a query service

package com.example.async.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * Inquiry service
 * </p>
 *
 * @author Fulongyuan Jushi, public account: [Java Sharing Inn]
 * @since 2022-04-26 17:38
 */
@Service
@ Slf4j
public class QueryService {
   private final TwentyFourService twentyFourService;
   private final ConstellationService constellationService;

   public QueryService(TwentyFourService twentyFourService, ConstellationService constellationService) {
      this.twentyFourService = twentyFourService;
      this.constellationService = constellationService;
   }

   /**
    * Synchronous return result
    * @return result
    */
   public Map<String, Object> query() {
      // 1. Query the twenty-four solar terms
      String twentyFourResult = twentyFourService.getResult();

      // 2. Query the constellation
      String constellationResult = constellationService.getResult();

      // 3, return
      Map<String, Object> map = new HashMap<>();
      map.put("twentyFourResult", twentyFourResult);
      map.put("constellationResult", constellationResult);
      return map;
   }
}

4. Write the test interface

Here, we specifically add time-consuming calculations.

package com.example.async.controller;

import cn.hutool.core.date.TimeInterval;
import com.example.async.service.QueryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * <p>
 * test
 * </p>
 *
 * @author Fulongyuan Jushi, public account: [Java Sharing Inn]
 * @since 2022-04-26 17:35
 */
@RestController
@RequestMapping("/api")
@ Slf4j
public class TestController {

   private final QueryService queryService;

   public TestController(QueryService queryService) {
      this.queryService = queryService;
   }

   /**
    * Synchronous query
    * @return result
    */
   @GetMapping("/query")
   public ResponseEntity<Map<String, Object>> query() {
      // timing
      final TimeInterval timer = new TimeInterval();
      timer.start();
      Map<String, Object> map = queryService.query();
      map.put("costTime", timer.intervalMs() + " ms");
      return ResponseEntity.ok().body(map);
   }
}

5. Effect

It can be seen that it took about 10 seconds for the two interfaces to return.

6. CompletableFuture parallel query

Now let's use CompletableFuture to transform the interface, query two HTTP interfaces in parallel and return.

package com.example.async.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

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

/**
 * <p>
 * Inquiry service
 * </p>
 *
 * @author Fulongyuan Jushi, public account: [Java Sharing Inn]
 * @since 2022-04-26 17:38
 */
@Service
@ Slf4j
public class QueryService {
   private final TwentyFourService twentyFourService;
   private final ConstellationService constellationService;

   public QueryService(TwentyFourService twentyFourService, ConstellationService constellationService) {
      this.twentyFourService = twentyFourService;
      this.constellationService = constellationService;
   }

   /**
    * Asynchronous return result
    * @return result
    */
   public Map<String, Object> queryAsync() {

      Map<String, Object> map = new HashMap<>();

      // 1. Query the twenty-four solar terms
      CompletableFuture<String> twentyFourQuery = CompletableFuture.supplyAsync(twentyFourService::getResult);
      twentyFourQuery.thenAccept((result) -> {
         log.info("Query the results of the twenty-four solar terms: {}", result);
         map.put("twentyFourResult", result);
      }).exceptionally((e) -> {
         log.error("Query 24 solar terms exception: {}", e.getMessage(), e);
         map.put("twentyFourResult", "");
         return null;
      });

      // 2. Query the constellation
      CompletableFuture<String> constellationQuery = CompletableFuture.supplyAsync(constellationService::getResult);
      constellationQuery.thenAccept((result) -> {
         log.info("Query constellation result: {}", result);
         map.put("constellationResult", result);
      }).exceptionally((e) -> {
         log.error("Query constellation exception: {}", e.getMessage(), e);
         map.put("constellationResult", "");
         return null;
      });

      // 3, allOf - both queries must be completed
      CompletableFuture<Void> allQuery = CompletableFuture.allOf(twentyFourQuery, constellationQuery);
      CompletableFuture<Map<String, Object>> future = allQuery.thenApply((result) -> {
         log.info("------------------- All queries completed------------------- ");
         return map;
      }).exceptionally((e) -> {
         log.error(e.getMessage(), e);
         return null;
      });

      // Get the return value of the async method
      // get() - an exception is thrown internally and needs to be handled manually; join() - an exception is handled internally without manual handling, just click it and you will know.
      future.join();

      return map;
   }
}

7. Write the test interface

package com.example.async.controller;

import cn.hutool.core.date.TimeInterval;
import com.example.async.service.QueryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

/**
 * <p>
 * test
 * </p>
 *
 * @author Fulongyuan Jushi, public account: [Java Sharing Inn]
 * @since 2022-04-26 17:35
 */
@RestController
@RequestMapping("/api")
@ Slf4j
public class TestController {

   private final QueryService queryService;

   public TestController(QueryService queryService) {
      this.queryService = queryService;
   }

   /**
    * Asynchronous query
    * @return result
    */
   @GetMapping("/queryAsync")
   public ResponseEntity<Map<String, Object>> queryAsync() {
      // timing
      final TimeInterval timer = new TimeInterval();
      timer.start();
      Map<String, Object> map = queryService.queryAsync();
      map.put("costTime", timer.intervalMs() + " ms");
      return ResponseEntity.ok().body(map);
   }
}

8. CompletableFuture effect

As you can see, the time is doubled.

9. Think

If in a microservice, there is a very complex business that needs to remotely call the interfaces of 5 third-party laji manufacturers, each interface is assumed to take 5 seconds, how long will it take to use CompletableFuture to perform parallel processing?

The answer is yes, synchronous query takes about 25 seconds, and CompletableFuture parallel processing is still about 5 seconds. That is to say, in the same interface, the more time-consuming interfaces are called, the greater the optimization of CompletableFuture.

Guess you like

Origin blog.csdn.net/Trouvailless/article/details/124450654