2015-09-07 2 views
2

Это очень простая рекурсия для CompletableFuture, я хочу создать надежную систему, чтобы каждый раз возникал исключение для повторного запуска процесса, я полагаю, что у него слишком много проблем и мы хотим получить ваши отзывы об этомCompletedFuture рекурсия по надежности

private CompletableFuture<?> recursion() { 
return CompletableFuture.runAsync(() -> { 
    //code here 
    }).handleAsync((v, th) -> { 
     if (th != null) 
      return this.recursion(); 
     else 
      return v; 
    }); 
} 

Edit1:

int tries =5; 
private CompletableFuture<?> recursion() { 
    return CompletableFuture.runAsync(() -> { 
    //code here 
    }).handleAsync((v, th) -> { 
     if (th != null && tries-- > 0){ 
      Thread.sleep(1000); 
      return this.recursion(); 
     }else 
      return v; 
    }); 
} 

Edit2: чистка кода как возвращение CompletableFuture<?> не нуждается так повешен его возвращаться void рассматривает @Holger комментарий и используя ATO micInteger для попыток

AtomicInteger tries =5; 
private void recursion() { 
    CompletableFuture.runAsync(() -> { 
    //code here 
    }).whenCompleteAsync((v, th) -> { 
     if (th != null && (tries.getAndDecrement() > 0)){ 
      Thread.sleep(1000); 
      this.recursion(); 
     }); 
} 

пришлите мне свои отзывы, я спорю, но очень ценю это.

+0

Вы должны также принимать в том, что с помощью рекурсии может взорвать с 'StackOverflowError' или бесконечным циклом. – Tunaki

+0

Да, я считаю, что на самом деле считается, что количество попыток и спать между каждой попыткой. –

+0

Такие вещи уже существуют, но я предлагаю вам взглянуть на 'rxjava'. Существует также «asyn-cretry», который хорош: https://github.com/nurkiewicz/async-retry –

ответ

0

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

Тем не менее, если вы хотите реализовать повторные попытки, ваш код не сделает это правильно.

Ваш код должен быть принят компилятором, потому что вы используете действие, которое не дает значения и возвращает CompletableFuture<?>. Это скрывает проблемы с вашим кодом:

Предполагается, что бифурка, переданная в handleAsync, должна предоставить значение результата, но вы вызываете this.recursion(), который производит CompletableFuture<?>. Компилятор не возражает против того, что в неисключительном случае возвращается v, так как Void и CompletableFuture<?> имеют общий тип супер, Object, поэтому весь метод эффективно возвращает CompletableFuture<Object>, который совместим с типом возврата CompletableFuture<?>.

Если объявлен типа возвращаемого значения, как CompletableFuture<Void>, логическая ошибка стала узнаваема сразу: в исключительном случае, если вы запускаете другую асинхронную операцию, но так как вы не проверить это результат, а просто возвращаете CompletableFuture<?>, который затем обрабатывают, как Object , вызывающий абонент никогда не заметит, произошла ли повторная попытка (или последующие попытки). Вызывающий всегда будет получать CompletableFuture<?>, который сообщает об успешном завершении, либо (Void)null, либо CompletableFuture<?> в качестве результата.

Как правило, вы не должны использовать рекурсию для повторения. Для этого нет оснований. Давайте продемонстрировать логику действия, которая возвращает значение:

CompletableFuture<String> performAsyncAction() { 
    Supplier<String> action=() -> { 
     if(Math.random()>0.2) 
      throw new IllegalStateException("simulated failure"); 
     return "value implying success"; 
    }; 
    int retries=5; 
    return CompletableFuture.supplyAsync(() -> { 
     try { return action.get(); } 
     catch(Throwable t) { 
      for(int i=0; i<retries; i++) try { 
       Thread.sleep(1000); 
       return action.get(); 
      } catch(Throwable next) { t.addSuppressed(next); } 
      throw t; 
     } 
    }); 
} 

Это легко адаптируются использовать Runnable, runAsync и CompletableFuture<Void>.


Обновление: если вы просто хотите запланировать повторную попытку, не давая обратную связь инициатора, вы можете реализовать его, не блокируя поток, ожидая задержки истечь:

static ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor(); 

static void performAsyncAction(Runnable r, int tries, long delay, TimeUnit u) { 
    if(tries>0) 
     e.execute(()-> { try { r.run(); } catch(Throwable t) { 
      e.schedule(()->performAsyncAction(r, tries-1, delay, u), delay, u); 
     }}); 
} 

При этом используется рекурсия, когда она оседает на лямбда-выражениях.То же самое будет работать без рекурсии, если вы используете внутренний класс:

static ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor(); 

static void performAsyncAction(Runnable r, int tries, long delay, TimeUnit u) { 
    if(tries>0) 
     e.execute(new Runnable() { 
      int left = tries; 
      public void run() { 
       try { r.run(); } catch(Throwable t) { 
        if(--left > 0) e.schedule(this, delay, u); 
       } 
      } 
     }); 
} 
+0

вы код, означающий, что поток будет заблокирован внутри цикла для всех попыток, и я не хочу этого делать, я понимаю ваши точки, но, поскольку я говорю, что это базовый, он думал, чтобы получить от пользователя исключение, чтобы повторить попытку , –

+0

Цикл выполняется внутри асинхронной операции, то есть отдельной нити. Нет смысла отделять итерации отдельными потоками, которые будут ждать друг друга, так как каждый 'handleAsync' создает * зависимую * операцию. Фактически, базовая реализация может быть достаточно умной, чтобы признать, что этапы зависят друг от друга и повторно используют поток предыдущего этапа для следующего. Но если нет, вы создаете «количество попыток» дополнительных потоков для получения выгоды. Вы не можете изменить последовательный характер «повторите попытку, если предыдущая попытка не удалась». – Holger

+0

wait with handleAsync ?? не он не ждет, когда он выпускает первый поток, и в моем случае при запуске его с пулом потоков фактически поток освобождается и повторно используется одной и той же операцией, но использование цикла for не освобождает поток вообще, другими словами, если ваш CompletableFuture не означает, что ресурсы не освобождаются. –

Смежные вопросы