Spring Reactive WebClient调用AKS服务返回404错误求助
问题分析与解决方案
问题现象
使用Spring Reactive WebClient调用Azure Kubernetes Service(AKS)上的服务时,返回nginx的404 Not Found错误,但调用虚拟机规模集(VMSS)上的同逻辑服务正常;更换为RestClient调用AKS服务则完全正常。
可能原因与排查解决步骤
1. URL拼接一致性问题
WebClient与RestClient的URL拼接逻辑存在细微差异,若baseUrl和uri的斜杠格式不一致,可能导致最终请求URL不符合AKS ingress的路由规则。
- 排查方式:给WebClient添加请求拦截器,打印最终请求URL,与RestClient的请求URL对比:
WebClient webClient = WebClient.builder() .filter(ExchangeFilterFunction.ofRequestProcessor(clientRequest -> { log.info("WebClient 请求URL: {}", clientRequest.url()); return Mono.just(clientRequest); })) // 其他原有配置 .build(); - 解决方案:统一
baseUrl和uri的斜杠格式,比如:- 若
baseUrl结尾带斜杠(如http://aksdomainname.net/aks-gateway/svc/catalog/),则uri开头去掉斜杠(改为v1/ast) - 或
baseUrl结尾不带斜杠(如http://aksdomainname.net/aks-gateway/svc/catalog),uri开头带斜杠(保持/v1/ast)
- 若
2. 请求头重复/覆盖问题
WebClient同时使用defaultHeader和headers(h -> h.putAll(httpHeaders))可能导致请求头重复(比如Content-Type或Accept出现多个值),而AKS的nginx网关对重复头的处理与VMSS不同,导致路由失败。
- 排查方式:添加拦截器打印WebClient的请求头,与RestClient的请求头对比:
.filter(ExchangeFilterFunction.ofRequestProcessor(clientRequest -> { log.info("WebClient 请求头: {}", clientRequest.headers()); return Mono.just(clientRequest); })) - 解决方案:合并头配置,避免重复设置:
WebClient webClient = WebClient.builder() .exchangeStrategies(ExchangeStrategies.builder() .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(100 * 1024 * 1024)).build()) .baseUrl(aksUrl) .clientConnector(connector) .build(); // 请求时统一设置所有头 final String resp = webClient.post().uri(uri) .bodyValue(request) .headers(h -> { h.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); h.set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE); h.putAll(httpHeaders); // 仅添加自定义头,避免重复 }) // 其他原有处理逻辑 .block();
3. HttpClient配置差异
WebClient使用Reactor Netty HttpClient,而RestClient默认使用JDK/Apache HttpClient,两者的默认配置(如HTTP版本、重定向策略、连接池)可能存在差异,导致AKS网关不兼容。
- 排查方式:先去掉WebClient的自定义HttpClient配置,使用默认连接器测试:
WebClient webClient = WebClient.builder() .exchangeStrategies(ExchangeStrategies.builder() .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(100 * 1024 * 1024)).build()) .baseUrl(aksUrl) .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).build(); - 解决方案:若默认配置有效,再逐步添加自定义配置,同时强制使用HTTP/1.1(AKS ingress常依赖HTTP/1.1):
HttpClient httpClient = HttpClient.create() .protocol(HttpProtocol.H11) // 强制HTTP/1.1 .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 300_000) .responseTimeout(Duration.ofMillis(300_000)); final ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
4. 请求体序列化差异
WebClient与RestClient的JSON序列化规则可能不同(比如字段顺序、空值处理),导致AKS服务无法正确识别请求,进而返回404。
- 排查方式:拦截WebClient的请求体,打印后与RestClient的请求体对比:
.filter(ExchangeFilterFunction.ofRequestProcessor(clientRequest -> { clientRequest.body().insert(clientRequest.headers(), Context.of()) .flatMap(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); log.info("WebClient 请求体: {}", new String(bytes)); return Mono.empty(); }).subscribe(); return Mono.just(clientRequest); })) - 解决方案:统一WebClient与RestClient的ObjectMapper配置:
// 使用与RestClient相同的ObjectMapper实例 ObjectMapper objectMapper = new ObjectMapper(); // 配置序列化规则(如忽略空值、字段顺序等) objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); ExchangeStrategies strategies = ExchangeStrategies.builder() .codecs(configurer -> { configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper)); configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper)); configurer.defaultCodecs().maxInMemorySize(100 * 1024 * 1024); }).build(); WebClient webClient = WebClient.builder() .exchangeStrategies(strategies) // 其他配置 .build();
5. AKS Ingress路由规则检查
若以上步骤都无效,需检查AKS的nginx ingress配置,确认是否对请求的特定属性(如请求头、URL大小写、HTTP方法)有严格限制,确保WebClient的请求完全符合路由规则。
内容的提问来源于stack exchange,提问作者Ismail
相关产品推荐
相关产品推荐

