[Java Sharing Inn] Освойте параллельную обработку CompletableFuture и экспоненциально сократите время запроса.

предисловие

Вы можете столкнуться с множеством таких сценариев в своей работе.Для интерфейса нужно вызывать метод запроса из нескольких других сервисов, а затем инкапсулировать данные и возвращать их после получения требуемых значений.

С подобной ситуацией можно столкнуться и в микросервисах: интерфейсу сервиса нужно несколько раз использовать feign, чтобы вызвать метод других сервисов для получения данных, и, наконец, получить нужное значение и инкапсулировать его обратно во фронтенд.

В таком сценарии, когда метод, вызываемый одним или несколькими RPC, требует много времени, ответ всего интерфейса будет очень медленным. После Java 8 есть инструмент, который очень подходит для обработки этого сценария, CompletableFuture.

Сцены

В этой главе в основном объясняется использование CompletableFuture для параллельной обработки, чтобы помочь вам быстро освоить и применить его на практике для этого очень распространенного сценария. Существует много внутренних применений CompletableFuture, но большинство сценариев, используемых отдельными лицами, обрабатываются параллельно.Те, кто интересуется другими сценариями, могут искать на Baidu отдельно.

Описание сценария:

Напишите интерфейс, вызовите два других HTTP-интерфейса, получите двадцать четыре солнечных термина и созвездия соответственно и, наконец, верните их вместе.

использование

1. Онлайн-API

Мы посещаем веб-сайт Jisu Data https://www.jisuapi.com, регистрируем учетную запись, вы можете бесплатно использовать некоторые онлайн-API, и в среднем есть 100 бесплатных возможностей в день, что совершенно идеально подходит для таких людей, как я. которые часто делают некоторые тесты достаточно локально.

Здесь я использую API для запроса двадцати четырех солнечных терминов и API для запроса созвездий Код случая будет предоставлен позже, или вы можете использовать мой непосредственно.

2. Напишите онлайн-запросы к API

Здесь мы моделируем ситуацию, требующую много времени при запросе.

1), запросите двадцать четыре солнечных термина

package com.example.async.service; 

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

/** 
 * <p> 
 * Запросите двадцать четыре солнечных Условия службы 
 * </p> 
 * 
 * @author Fulongyuan, общедоступная учетная запись: [Java Sharing Inn] 
 * @since 26 апреля 2022 г., 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); 
 
       //Время моделирования
       попробуйте {
          TimeUnit.SECONDS.sleep(5); 
       } catch (Exception e) { 
          log.error("[Двадцать четыре солнечных термина]>>>> Exception: {}", e.getMessage(), e); 
       } 

       вернуть результат ; 
   } 
   
}

2), запросить созвездие

пакет com.example.async.service; 

импорт cn.hutool.http.HttpUtil; 
импорт lombok.extern.slf4j.Slf4j; 
импорт org.springframework.stereotype.Service; 

импорт java.util.concurrent.TimeUnit; 

/** 
 * < p> 
 * Сервис для запроса созвездий 
 * </p> 
 * 
 * @author Fulongyuan Jushi, публичный аккаунт: [Java Sharing Inn] 
 * @since 26-04-2022 15:25 
 */ 
@Service 
@Slf4j 
public class ConstellationService { 
   public static final String APPKEY = "xxxxxx";// ваш ключ приложения 
   public static final String URL = "https://api.jisuapi.com/astro/all"; 

   public String getResult() { 

      String url = URL + "? appkey=" + APPKEY; 
      Строковый результат = HttpUtil.получить (адрес);

      // Моделирование занимает много времени 
      try { 
         TimeUnit.SECONDS.sleep(5); 
      } catch (Exception e) { 
         log.error("[Constellation]>>>> Exception: {}", e.getMessage(), e) ; 
      } 

      вернуть результат; 
   } 
   
}

3. Напишите службу запросов

пакет com.example.async.service; 

импортировать ломбок.extern.slf4j.Slf4j; 
импортировать org.springframework.stereotype.Service; 

импортировать java.util.HashMap; 
импортировать java.util.Map; 

/** 
 * <p> 
 * 查询服务
 * </p> 
 * 
 * @author 福隆苑居士,公众号:【Java分享客栈】
 * @since 26-04-2022 17:38 
 */ 
@Service 
@Slf4j 
public класс QueryService { 
   закрытый окончательный TwentyFourService двадцатьFourService; 
   частный окончательный ConstellationService constellationService; 

   общественный QueryService (TwentyFourService двадцатьFourService, ConstellationService constellationService) {
   } 
      this.twentyFourService = двадцатьFourService;
      this.constellationService = constellationService; 

   /** 
    * синхронно вернуть результат 
    * @return result 
    */ 
   public Map<String, Object> query() { 
      // 1. Запросить двадцать четыре солнечных терма 
      String двадцать четыре вывода = двадцать четыре службы. получить результат () ; 

      // 2. Запросить констелляцию 
      String constellationResult = constellationService.getResult(); 

      // 3. Возврат 
      Map<String, Object> map = new HashMap<>(); 
      map.put("twentyFourResult", двадцатьFourResult); 
      map.put ("constellationResult", constellationResult); 
      карта возврата; 
   } 
}

4. Напишите тестовый интерфейс

Здесь мы специально добавляем трудоемкие вычисления.

пакет com.example.async.controller; 

импортировать cn.hutool.core.date.TimeInterval; 
импортировать com.example.async.service.QueryService; 
импортировать ломбок.extern.slf4j.Slf4j; 
импортировать org.springframework.http.ResponseEntity; 
импортировать org.springframework.web.bind.annotation.GetMapping; 
импортировать org.springframework.web.bind.annotation.RequestMapping; 
импортировать org.springframework.web.bind.annotation.RestController; 

импортировать java.util.Map; 

/** 
 * <p> 
 * 测试
 * </p> 
 * 
 * @author 福隆苑居士,公众号:【Java分享客栈】
 * @since 26-04-2022 17:35 
 */ 
@RestController 
@RequestMapping(" /api") 
@Slf4j 
открытый класс TestController {

   закрытый окончательный QueryService queryService; 

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

   /** 
    * 同步查询
    * @return 结果
    */ 
   @GetMapping("/query") 
   public ResponseEntity<Map<String, Object>> query() { 
      // 计时
      final TimeInterval timer = new TimeInterval(); 
      таймер.старт(); 
      Map<String, Object> map = queryService.query(); 
      map.put("costTime", timer.intervalMs() + " мс"); 
      вернуть ResponseEntity.ok().тело(карта); 
   } 
}

5. Эффект

Видно, что на возврат двух интерфейсов ушло около 10 секунд.

6. Параллельный запрос CompletableFuture

Теперь воспользуемся CompletableFuture для преобразования интерфейса, параллельного запроса двух HTTP-интерфейсов и возврата.

пакет com.example.async.service; 

импортировать ломбок.extern.slf4j.Slf4j; 
импортировать org.springframework.stereotype.Service; 

импортировать java.util.HashMap; 
импортировать java.util.Map; 
импортировать java.util.concurrent.CompletableFuture; 

/** 
 * <p> 
 * 查询服务
 * </p> 
 * 
 * @author 福隆苑居士,公众号:【Java分享客栈】
 * @since 26-04-2022 17:38 
 */ 
@Service 
@Slf4j 
public класс QueryService { 
   закрытый окончательный TwentyFourService двадцатьFourService; 
   частный окончательный ConstellationService constellationService; 

   публичный QueryService (TwentyFourService, двадцатьFourService, ConstellationService, constellationService) {
      this.twentyFourService 
      = 
   _ 
         map.put("twentyFourResult", результат); 
      }).exceptionally((e) -> { 
         log.error("Исключение запроса 24 солнечных терминов: {}", e.getMessage (), д);

   /** 
    * Результат асинхронного возврата 
    * @return result 
    */ 
   public Map<String, Object> queryAsync() { 

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

      // 1. Запросить двадцать четыре солнечных терма 
      CompletableFuture< String>wentyFourQuery = CompletableFuture.supplyAsync(twentyFourService:: 
      getResult 
         ) 
         ; ("twentyFourResult" , ""); 
         return null; 
      }); 

      // 2. Запросить созвездие 
      CompletableFuture<String> constellationQuery = CompletableFuture.supplyAsync(constellationService::getResult); 
      constellationQuery.thenAccept((result) ->{ 
         log.info("Результат созвездия запроса: {}", результат); 
         map.put("constellationResult", результат); 
      }).exceptionally((e) -> { 
         log.error("Исключение созвездия запроса: {}" , e.getMessage(), e); 
         map.put("constellationResult", ""); 
         return null; 
      }); 

      // 3. все-оба запроса должны быть выполнены 
      CompletableFuture<Void> allQuery = CompletableFuture.allOf(twentyFourQuery, constellationQuery);
      CompletableFuture<Map<String, Object>> future = allQuery.thenApply((result) -> { 
         log.info("------------------ Все запросы выполнены-- - --------------- "); 
         карта возврата; 
      }).Exceptionally((e) -> { 
         log.error(e.getMessage(), e); 
         return null; 
      } ) ; 

      // Получаем возвращаемое значение асинхронного метода 
      // get() — исключение генерируется внутри и требует обработки вручную; join() — исключение обрабатывается внутри без ручной обработки, просто нажмите и посмотрите. 
      future.join(); 

      карта возврата; 
   } 
}

7. Напишите тестовый интерфейс

пакет com.example.async.controller; 

импортировать cn.hutool.core.date.TimeInterval; 
импортировать com.example.async.service.QueryService; 
импортировать ломбок.extern.slf4j.Slf4j; 
импортировать org.springframework.http.ResponseEntity; 
импортировать org.springframework.web.bind.annotation.GetMapping; 
импортировать org.springframework.web.bind.annotation.RequestMapping; 
импортировать org.springframework.web.bind.annotation.RestController; 

импортировать java.util.Map; 

/** 
 * <p> 
 * 测试
 * </p> 
 * 
 * @author 福隆苑居士,公众号:【Java分享客栈】
 * @since 26-04-2022 17:35 
 */ 
@RestController 
@RequestMapping(" /api") 
@Slf4j 
открытый класс TestController {

   закрытый окончательный QueryService queryService; 

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

   /** 
    * 异步查询
    * @return 结果
    */ 
   @GetMapping("/queryAsync") 
   public ResponseEntity<Map<String, Object>> queryAsync() { 
      // 计时
      final TimeInterval timer = new TimeInterval(); 
      таймер.старт(); 
      Map<String, Object> map = queryService.queryAsync(); 
      map.put("costTime", timer.intervalMs() + " мс"); 
      вернуть ResponseEntity.ok().тело(карта); 
   } 
}

8. Эффект CompletableFuture

Как видите, время удваивается.

9. Думай

Если в микросервисе есть очень сложный бизнес, которому нужно удаленно вызывать интерфейсы 5 сторонних производителей ладжи, предполагается, что каждый интерфейс занимает 5 секунд, сколько времени потребуется, чтобы использовать CompletableFuture для выполнения параллельной обработки?

Ответ — да, синхронный запрос занимает секунд 25, а параллельная обработка CompletableFuture — еще секунд 5. То есть в одном и том же интерфейсе чем больше времени вызывается интерфейс, тем больше оптимизация CompletableFuture.

рекомендация

отblog.csdn.net/Trouvailless/article/details/124450654