为什么Webflux请求日志逻辑中getBody().doOnNext()调用次数不固定?
问题原因
- HTTP请求体在传输时可能被拆分为多个TCP数据包,WebFlux会将每个数据包封装为独立的
DataBuffer对象,通过Flux<DataBuffer>依次发射,并非所有场景下都只会返回1个DataBuffer元素。你当前的代码将日志打印逻辑放在doOnNext中,每收到一个DataBuffer就会触发一次日志输出,自然会出现多条拆分的请求体日志。 - 现有代码存在隐藏bug:第一个
DataBuffer处理完成后就关闭了ByteArrayOutputStream,后续DataBuffer写入时会触发IO异常,仅因为你捕获了异常才没有向上传播,但日志记录的内容也会不完整。
修复方案
你需要先把所有DataBuffer拼接为完整的请求体,等所有内容接收完毕后再打印日志,再将完整请求体传递给下游处理,同时要注意DataBuffer的内存释放,避免内存泄漏。修复后的代码参考如下:
@Override public Flux<DataBuffer> getBody() { DataBufferFactory bufferFactory = getDelegate().bufferFactory(); return DataBufferUtils.join(super.getBody()) .flatMapMany(fullBuffer -> { // 读取完整请求体打日志 try { byte[] bodyBytes = new byte[fullBuffer.readableByteCount()]; fullBuffer.get(fullBuffer.readerIndex(), bodyBytes); String body = new String(bodyBytes, StandardCharsets.UTF_8); LOGGER.info("Request: method={}, uri={}, payload={}, audit={}", getDelegate().getMethod(), getDelegate().getPath(), body, value("audit", true)); // 返回完整请求体给下游 return Flux.just(bufferFactory.wrap(bodyBytes)); } finally { // 释放原有的DataBuffer,避免内存泄漏 DataBufferUtils.release(fullBuffer); } }) // 处理空请求体场景 .switchIfEmpty(Flux.empty()); }
如果你的业务场景存在下游多次订阅请求体Flux的情况,可以在返回的Flux最后添加.cache()操作符,避免重复读取请求体导致的重复日志问题。
内容的提问来源于stack exchange,提问作者genki98
相关产品推荐
相关产品推荐

