Одновременно запустить Void CompletionStage, но игнорировать результат - PullRequest
2 голосов
/ 05 мая 2020

У меня есть два вызова метода завершенияStages, каждый из которых вызывает удаленную службу, если условие не выполняется. Оба они - довольно длительные процессы, и нам нужно уменьшить задержку. Меня также не волнует ответ secondFuture. Он может вернуть CompletionStage<Void>, поскольку меня волнует только то, выполняется ли метод до выхода из основного метода. Дополнительная сложность заключается в том, что injectedClass2.serviceCall также генерирует действительно важное исключение (404 StatusRuntimeException), которое необходимо сообщить клиенту.

Как обеспечить асинхронное выполнение первого и второго будущих сценариев (независимо от каждого other), а второе будущее показывает свои коды ошибок и исключения для клиента.

Основной метод, представленный ниже, - моя лучшая попытка. Это работает, но я ищу лучшую реализацию, которая использует преимущества Completetables / streams и т. Д. c.

try {
      .
      .
      .
      CompletionStage<Response> firstFuture;
      CompletionStage<Response> secondFuture = CompletableFuture.completedFuture(Response.default());
      if (condition) {
        firstFuture = legacyImplThing.resolve(param1, param2);
      } else {
        firstFuture =
            injectedClass1.longRunningOp(param1, param2);
        secondFuture = injectedClass2.serviceCall(param1, param2, someOtherData);
      }

      final CompletionStage<MainMethodResponse> response =
          CompletableFutures.combine(firstFuture, secondFuture, (a, b) -> a)
              .thenApply(
                  v -> ServiceResponse.newBuilder().setParam(v.toString()).build());

      handleResponse(response, responseObserver);
    } catch (Exception e) {
      responseObserver.onError(e);
    }

Может быть, выходит за рамки, как можно протестировать / проверить, что два этапа завершения выполнялись одновременно?

EDIT: CompletableFutures.combine() - это метод сторонней библиотеки, а не часть java .util.concurrent пакет.

1 Ответ

2 голосов
/ 05 мая 2020

Объединение других этапов не изменяет предыдущие этапы. Другими словами, параллелизм полностью находится вне вашего контроля, поскольку он уже определен.

Более конкретно, когда вы вызываете injectedClass1.longRunningOp(param1, param2), реализация метода longRunningOp решает, как будет возвращено будущее будет завершена. Аналогично, когда вы вызываете injectedClass2.serviceCall(param1, param2, someOtherData), реализация serviceCall будет определять завершение возвращенного future-объекта. Оба метода могут использовать один и тот же исполнитель за кулисами или совершенно разные подходы. будущее. В этом случае вам придется заключить каждый вызов в другую асинхронную операцию, чтобы позволить им работать параллельно. Но было бы странно возвращать будущее при выполнении длительной операции в потоке вызывающего.

Ваш код

CompletableFutures.combine(firstFuture, secondFuture, (a, b) -> a)

не соответствует документированному API . Допустимый вызов:

firstFuture.thenCombine(secondFuture, (a, b) -> a)

В этом случае вы не влияете на завершение firstFuture или secondFuture. Вы только указываете, что должно произойти после оба фьючерса были завершены.

Между прочим, нет причин указывать такую ​​тривиальную функцию, как (a, b) -> a в thenCombine, просто чтобы связать еще один thenApply. Вы можете использовать в первую очередь

firstFuture.thenCombine(secondFuture,
    (v, b) -> ServiceResponse.newBuilder().setParam(v.toString()).build())

.

...