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.