当前位置: 首页 > 知识库问答 >
问题:

在Spring反应式webclient中处理连接错误

凌博实
2023-03-14

我有一个spring webclient对外部服务进行http调用,并得到反应式断路器工厂(resilience4J impl)的支持。当客户端建立连接并响应失败(任何内部服务器或4XX错误)时,WebClient和断路器的行为符合预期。但是,如果客户端无法建立连接,无论是连接被拒绝还是未知主机,它都会开始崩溃。

  1. 我似乎无法在webClient中捕获错误消息并触发断路器。
  2. 断路器永远不会打开并抛出TimeoutException。
    java.util.concurrent.TimeoutException:在“电路断路器”中1000毫秒内没有观察到任何项目或终端信号(并且没有配置回退)

来自Web客户端的错误。
io.netty.channel.AbstractChannel$AnnotatedConnectException:连接拒绝:localhost/127.0.0.1: 9000

这是我的代码。我也粘贴了错误来源。我试图将ConnectException映射到我的自定义异常以供断路器拾取,但它不起作用。有人能帮助我在没有远程服务器响应的情况下处理错误吗?

 public Mono<String> toSink(
  Envelope envelope, ConsumerConfiguration webClientConfiguration) {

return getWebClient()
    .post()
    .uri(
        uriBuilder -> {
          if (webClientConfiguration.getPort() != null) {
            uriBuilder.port(webClientConfiguration.getPort());
          }
          return uriBuilder.path(webClientConfiguration.getServiceURL()).build();
        })
    .headers(
        httpHeaders ->
            webClientConfiguration.getHttpHeaders().forEach((k, v) -> httpHeaders.add(k, v)))
    .bodyValue(envelope.toString())
    .retrieve()
    .bodyToMono(Map.class)
    // Convert 5XX internal server error and throw CB exception
    .onErrorResume(
        throwable -> {
          log.error("Inside the error resume callback of webclient {}", throwable.toString());
          if (throwable instanceof WebClientResponseException) {
            WebClientResponseException r = (WebClientResponseException) throwable;
            if (r.getStatusCode().is5xxServerError()) {
              return Mono.error(new CircuitBreakerOpenException());
            }
          }
          return Mono.error(new CircuitBreakerOpenException());
        })
    .map(
        map -> {
          log.info("Response map:{}", Any.wrap(map).toString());
          return Status.SUCCESS.name();
        })
    .transform(
        it -> {
          ReactiveCircuitBreaker rcb =
              reactiveCircuitBreakerFactory.create(
                  webClientConfiguration.getCircuitBreakerId());
          return rcb.run(
              it,
              throwable -> {
                /// "Did not observe any item or terminal signal within 1000ms.. " <--- Error here
                log.info("throwable in CB {}", throwable.toString());
                if (throwable instanceof CygnusBusinessException) {
                  return Mono.error(throwable);
                }
                return Mono.error(
                    new CircuitBreakerOpenException(
                        throwable, new CygnusContext(), null, null, null));
              });
        })
    ///io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/127.0.0.1:9000  <-- Error prints here    
    .onErrorContinue((throwable, o) -> log.error(throwable.toString()))
    .doOnError(throwable -> log.error("error from webclient:{}", throwable.toString()));

}

共有2个答案

欧阳正谊
2023-03-14

我将对您的解决方案提出以下建议:

1-onErrorContinue有另一个变体,它接受谓词,因此您可以定义此运算符将应用于哪些异常-Docs

2-返回一个Mono.error,而不是从Mono/Flux运算符抛出RuntimeExceptions。另一个stackoverflow答案很好地涵盖了这一点-Stackoverflow

3-使用副作用运算符执行日志记录(doOn*

.doOnError(throwable -> log.info("throwable => {}", throwable.toString()))
.onErrorResume(throwable -> throwable instanceof ReadTimeoutException || throwable instanceof ConnectException,
                       t -> Mono.error(new CircuitBreakerOpenException()))

希望这有帮助。

尉迟边浩
2023-03-14

我通过添加onError继续块并重新抛出异常作为在我的断路器代码中处理的自定义来修复它。

.onErrorContinue(
        (throwable, o) -> {
          log.info("throwable => {}", throwable.toString());
          if (throwable instanceof ReadTimeoutException || throwable instanceof ConnectException) {
            throw new CircuitBreakerOpenException();
          }
        })
 类似资料:
  • 我想在以下条件下抛出自定义异常: > 如果我得到了json格式的正确错误响应,我想反序列化它,并在onStatus()中抛出名为CommonException的异常 如果作为响应的一部分接收HTML内容或反序列化未成功执行,则我希望抛出GenericeException,这是我在onErrorMap()中创建的 在抛出GenericException时,我希望将从下游响应获得的相同HttpStat

  • 我在尝试处理调用Spring WebFlux的web客户机的不同错误时遇到了麻烦。 我如何修改我的代码,使它返回我生成的自定义消息?

  • 我们使用和作为Spring 5.1.9的一部分,使用方法发出请求。此方法的文档强调了以下内容: ...使用exchange()时,应用程序有责任使用任何响应内容,而不管场景如何(成功、错误、意外数据等)。不这样做会导致内存泄漏。 如果我理解实现,那么如果请求被成功调度,将始终给我们一个响应,而不管响应代码是什么(例如4xx、5xx)。在这个场景中,我们只需要调用来使用响应。我关心的是错误场景(例如

  • 我希望从spring reactive WebClient进行SOAP调用。我找不到任何文件。想知道会有什么方法。现在我在想 null 缺点和其他方法是什么?

  • 我正在Kotlin(1.4.30)中使用Spring WebFlux WebClient(引导2.4.3)执行一个get http调用。当请求超时时,它以异常方式失败,但我希望返回一个默认值。我看到对、等的引用在之后使用,但在我的示例中似乎没有这些引用(只有、、) 电话: 连接和读取超时的WebClient配置: 响应模型: 回应: 它的模拟配置:

  • 因此,应用程序流程如下: Spring应用程序接收请求- 这是我正在使用的网络客户端(副本): 现在我不想通过控制器将此Mono对象直接返回到客户端(如Angular应用程序),因为这是一个中间步骤。我想对从 WebClient 收到的响应运行一些验证。 我已经试过<代码>。block()方法来检索函数,但是按照反应式编程,这似乎是一种不好的做法。(阻塞操作)此外,我无法理解如何使用<代码>。su