WebClient使用

文章目录

    • [1)WebClient 里最关键的异常对应表](#1)WebClient 里最关键的异常对应表)
    • 2)使用实例

1)WebClient 里最关键的异常对应表

归类 异常类 含义 场景
HTTP响应异常 org.springframework.web.reactive.function.client WebClientResponseException 4xx/5xx 收到响应但状态码错误, 若.onStatus() 已经处理了所有 4xx 和 5xx,WebClientResponseException 将永远不会被触发
网络连接异常 java.net.UnknownHostException 域名解析失败(请求没发出去) DNS 异常
网络连接异常 java.net.NoRouteToHostException 路由不可达(请求没发出去) 操作系统找不到路径(防火墙、网络配置)
网络连接异常 java.net.ConnectException 连接被拒绝(请求没发出去) 服务器不存在、端口未开放、被拒绝连接(服务宕机、地址错误)
网络连接异常 io.netty.channel. ConnectTimeoutException TCP 连接超时(请求没发出去) 1. 配置了.option(CONNECT_TIMEOUT_MILLIS),才会出现该异常 2.服务器存在,但在指定时间内没有响应连接请求(网络慢、防火墙拦截、服务器负载高) 3.是java.net.ConnectException的子类,直接处理ConnectException就成
响应超时 java.util.concurrent.TimeoutException 整体响应超时(从发起到接收完整响应) 1. 配置了responseTimeout(),才会出现该异常 2.不配置的话,默认无连接超时
响应超时 io.netty.handler.timeout. ReadTimeoutException 读取数据超时(Netty) 1.Reactor Netty 默认 10 秒,读超时 2.可通过.responseTimeout(...)配置超时时间
响应超时 io.netty.handler.timeout. WriteTimeoutException 写入数据超时(Netty) 配置了WriteTimeoutHandler,才会出现该异常
其他未知异常 未知错误 未名异常 兜底

2)使用实例

java 复制代码
@PostMapping("/reverse")
    public Mono<String> reverse(@RequestHeader HttpHeaders headers,
                                @RequestBody RQTextMessage message) {
        message.setLocalDateTime(LocalDateTime.now());
        log.info("reverse message:{}", message);

        return textMessageWebClient.post()
                .uri("/reverse")
                .headers(clientHeaders -> {
                    clientHeaders.addAll(headers);
                })
                .bodyValue(message)
                .retrieve()
                .bodyToMono(String.class)

                // ==================== 1. HTTP 响应异常 (收到响应但状态码错误) ====================
                .onErrorResume(WebClientResponseException.class, ex -> {
                    return handleHttpError(ex);
                })

                // ==================== 2. 网络连接异常 (连接阶段失败) ====================
                // 2.1 DNS 解析失败
                .onErrorResume(UnknownHostException.class, ex -> {
                    return handleNetworkError("域名解析失败", ex);
                })
                // 2.2 路由不可达 (防火墙/网络配置问题)
                .onErrorResume(NoRouteToHostException.class, ex -> {
                    return handleNetworkError("网络不可达", ex);
                })
                // 2.3 连接超时 (服务器在规定时间内未响应)
                .onErrorResume(ConnectTimeoutException.class, ex -> {
                    return handleNetworkError("连接超时", ex);
                })
                // 2.4 连接被拒绝 (服务宕机/端口未开放)
                .onErrorResume(ConnectException.class, ex -> {
                    return handleNetworkError("连接被拒绝", ex);
                })

                // ==================== 3. 响应超时异常 (连接成功但响应超时) ====================
                // 3.1 整体响应超时 (由 responseTimeout 配置触发)
                .onErrorResume(TimeoutException.class, ex -> {
                    return handleTimeoutError("响应超时", ex);
                })
                // 3.2 读取超时 (由 ReadTimeoutHandler 触发,如未配置则不会触发)
                .onErrorResume(ReadTimeoutException.class, ex -> {
                    return handleTimeoutError("读取超时", ex);
                })

                // ==================== 4. 其他未知异常 (兜底) ====================
                .onErrorResume(Exception.class, ex -> {
                    return handleUnknownError(ex);
                });
    }

    /**
     * 处理 HTTP 响应错误
     */
    private Mono<String> handleHttpError(WebClientResponseException ex) {
        log.error("HTTP响应错误: {} - {}", ex.getStatusCode(), ex.getResponseBodyAsString());
        return Mono.error(new BusinessException(
                ex.getRawStatusCode(),
                "远程服务返回错误: " + ex.getStatusCode()));
    }

    /**
     * 处理网络连接错误
     */
    private Mono<String> handleNetworkError(String reason, Exception ex) {
        log.warn("{}: {}", reason, ex.getMessage());
        return Mono.error(new BusinessException(
                HttpStatus.SERVICE_UNAVAILABLE.value(),
                "网络连接失败: " + reason));
    }

    /**
     * 处理超时错误
     */
    private Mono<String> handleTimeoutError(String reason, Exception ex) {
        log.warn("{}: {}", reason, ex.getMessage());
        return Mono.error(new BusinessException(
                HttpStatus.GATEWAY_TIMEOUT.value(),
                "请求超时: " + reason));
    }

    /**
     * 处理未知错误
     */
    private Mono<String> handleUnknownError(Exception ex) {
        log.error("未知异常: {}", ex.getClass().getName(), ex);
        return Mono.error(new BusinessException(
                HttpStatus.INTERNAL_SERVER_ERROR.value(),
                "系统内部错误"));
    }