项目反应器:如何用不同的参数重试单声道直到满足某些条件

Project reactor: How to retry mono with different argument until some condition is met

我需要使用项目反应器异步生成唯一代码。

方法签名如下所示:

public Mono<String> generateCode() 

所以流程应该是这样的:

  1. 生成随机码
  2. 检查数据库中是否存在此代码
  3. 如果存在,重新生成代码(第1步)并再次检查(第2步)
  4. 如果代码是唯一的,return它

我目前的解决方案是像这样递归调用 generateCode:

 Mono<String> generateCode() {
    String code = generateCodeValue();
    return emailConfirmationRepository
        .findByCode(code)
        .flatMap(codeOpt -> codeOpt.map(c -> generateCode()).orElseGet(() -> Mono.just(code)));
  }

但我不喜欢这样,因为每次调用都会创建自己的堆栈,这可能会导致 WhosebugError。

我知道,应该有非常大量的调用,它很可能不会发生,但是,我仍然需要一个没有递归的解决方案,就像一个普通的 while 循环,但是有异步代码。

如何使用 reactor 实现此目的?

当给定代码存在并使用 retry 运算符时,您可能 return 错误 Mono

Mono<String> generateCode() {
    return Mono.fromCallable(() -> generateCodeValue())
            .flatMap(code -> emailConfirmationRepository
                    .findByCode(code)
                    .flatMap(codeOpt -> codeOpt
                            .map(c -> Mono.<String>error(new CodeAlreadyExistsException()))
                            .orElseGet(() -> Mono.just(code))))
            .retry(5);
}

class CodeAlreadyExistsException extends RuntimeException {}

不过请注意,重试将重试您的所有步骤。所以,如果你有更复杂的代码,比如:

Mono<String> generateCode() {
return Mono.fromCallable(() -> generateCodeValue())
        .flatMap(code -> doSomeExpensiveOperation1())
        .flatMap(code -> doSomeDangerousOperation2())
        .flatMap(code -> emailConfirmationRepository
                .findByCode(code)
                .flatMap(codeOpt -> codeOpt
                        .map(c -> Mono.<String>error(new CodeAlreadyExistsException()))
                        .orElseGet(() -> Mono.just(code))))
        .retry(5);
}
class CodeAlreadyExistsException extends RuntimeException {}

然后您在 "findByCode" 之前的所有步骤将再次重复,包括 doSomeExpensiveOperation1 和 doSomeDangerousOperation2。

要无限期地重试直到满足某些条件,您应该:

Mono<String> generateCode() {
return Mono.fromCallable(() -> generateCodeValue())
        .flatMap(code -> emailConfirmationRepository
                .findByCode(code)
                .flatMap(codeOpt -> codeOpt
                        .map(c -> Mono.<String>error(new CodeAlreadyExistsException()))
                        .orElseGet(() -> Mono.just(code))))
        .retry(CodeAlreadyExistsException.class::isInstance)
}

class CodeAlreadyExistsException extends RuntimeException {}

谢谢@alexander-pankin。