You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebFlux POST请求体间歇性截断问题咨询

Spring WebFlux POST请求体间歇性截断问题咨询

大家好,我最近在基于Spring Boot WebFlux开发的应用里遇到了一个非常棘手的间歇性问题,想请各位帮忙分析下:

通过POST方法发送的请求体偶尔会被截断,导致后端无法正确处理请求。但奇怪的是,应用日志里完全没有内存异常抛出或记录,而且当我重试完全相同的请求时,又能成功处理。

相关代码片段

控制器代码

@RequestMapping(value = "/updateRequest", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public Mono<BookRecord> update(@RequestBody Payload e) {
    return bookService.sendRequest(e);
}

请求处理器(自定义ServerHttpRequestDecorator)

为了调试请求体,我写了一个自定义的请求处理器来打印接收的payload:

public class RequestProcessor extends ServerHttpRequestDecorator {

    private final ServerHttpRequest requestDecorator;

    public RequestProcessor(ServerHttpRequest requestDecorator) {
        super(requestDecorator);
        this.requestDecorator = requestDecorator;
    }

    @Override
    public Flux<DataBuffer> getBody() {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        return super.getBody().doOnNext((dataBuffer) -> {
            try {
                Channels.newChannel(baos).write(dataBuffer.asByteBuffer());
                System.out.println(" Received payload " + IOUtils.toString(baos.toByteArray(), "UTF-8"));
            } catch (IOException var11) {
                var11.printStackTrace();
            } finally {
                try {
                    baos.flush();
                    baos.close();
                } catch (IOException var10) {
                    var10.printStackTrace();
                }
            }
        });
    }
}

日志示例

请求体被截断时的日志

从日志里能看到,当payload被截断时,getBody()方法会多次触发,每次输出的payload都是不完整的片段:

2023-01-20 18:33:53.059 | Severity=DEBUG | [16b38379-1, L:/0:0:0:0:0:0:0:1:8084 - R:/0:0:0:0:0:0:0:1:62902] FluxReceive{pending=0, cancelled=false, inboundDone=false, inboundError=null}: subscribing inbound receiver
Received payload {
"data": "EP2+t7DXkiKcdUeScGwck6kgWC2/Fzf5Q8oU2Jp2mQzAZm6SpB/Bjp0iDyB6HOuAafFi9X1vBkMqry+4TXEfTpjncHEbuGwiTD5IkujMA6/G4aVtbZ915eHMTcCdaCfqlg2v8X4GKZbKrnV7fmMLxEUruFItgfVZ+k6/U6MkqdluK6bXkXKaeopMTHQ2u8SgsDuYnqcx7Kh1313gOeHC/QuYDm4tdkXRnccs3CAcicQRMJJHQ3c2eR7Cbx1sY5KsgLazNz795iXjxStoJKoaY8AzwKwf6EpG2D9ndnCwTEKU18s73QJbgAXlB/XBVvSKJ7VYPx1z2sBPjCIbNByfOW6FoKnMdj4ux/dh/XrfQCuGsBg9yVTyGt2i4x5A5IqzzuD9qON0+5v8FXK78CiEFcIr0zlJqH3vdbRQKWwNuZdTB//9Ya6gZxxpsQ7aq9blx+0sgSPzK3p9tD+HvQ+vSr0v/N+nCXO/wVuaLBXpRkt2dzSrb3i1exRHOEFGL6Oefjl4vGdk0WUaNc6MSqxl1P+q7VBsJRQy7tsHj+SLucrUwmN+IykJlNkbXNn8jPxW7bLV65YFBXsQ5Ye5moYfs+SQqGDUrlofQ2R8yoAFmAZdOzrzjcdh8kdLjXCFtxrQRgqzo42HEm/wXCBIst/C1ThFTZkTaw5H4mqfVVqUQBl6UxqLvEH5YJYVR8mVJQ6QWNHoh2iKzweuV6JbHA4ususgMD03bDP/Qchd/0mz/CgrL1tuvpgX1UjWpwULBWzVFLt+bMWk314RSj9puCAAnTP2mEZdJ2a7fxz28y3Qmus/hPZprcABbJHAyCDQBsqmEfhw0fDdDdO9v0CmXXg2B93EILqIaks+x3HSqzUI1yvBo2RmCvHfX3iMe7wrbdWCXjUn1CRVKJsUjThXotfzqgYYdNQYo2CRApbHu3+VdaUc8KeOhVk86i62gQJXpHEgwSQgDmVE2CwpxGaT8tnbo2jIHxQ9W1iwCwwR8wuyyUb1dhZUYPC2/BzdloJaiYlkcMPktGO04j9VcfUIDXg90DKJ3wJpOY7heHzGM9R8Or7VMg9q3in1CKVlXplPAblpEHsMbjjh8CYx98fPuStsvFU0KBmVetOFcw45a9zEugEssi+8xuQKFIuzaJ1nQsAcScbNwSJ7GQM/1LgBLoX6gfMrux7cQg8nmc/0

Received payload {
"data": "EP2+t7DXkiKcdUeScGwck6kgWC2/Fzf5Q8oU2Jp2mQzAZm6SpB/Bjp0iDyB6HOuAafFi9X1vBkMqry+4TXEfTpjncHEbuGwiTD5IkujMA6/G4aVtbZ915eHMTcCdaCfqlg2v8X4GKZbKrnV7fmMLxEUruFItgfVZ+k6/U6MkqdluK6bXkXKaeopMTHQ2u8SgsDuYnqcx7Kh1313gOeHC/QuYDm4tdkXRnccs3CAcicQRMJJHQ3c2eR7Cbx1sY5KsgLazNz795iXjxStoJKoaY8AzwKwf6EpG2D9ndnCwTEKU18s73QJbgAXlB/XBVvSKJ7VYPx1z2sBPjCIbNByfOW6FoKnMdj4ux/dh/XrfQCuGsBg9yVTyGt2i4x5A5IqzzuD9qON0+5v8FXK78CiEFcIr0zlJqH3vdbRQKWwNuZdTB//9Ya6gZxxpsQ7aq9blx+0sgSPzK3p9tD+HvQ+vSr0v/N+nCXO/wVuaLBXpRkt2dzSrb3i1exRHOEFGL6Oefjl4vGdk0WUaNc6MSqxl1P+q7VBsJRQy7tsHj+SLucrUwmN+IykJlNkbXNn8jPxW7bLV65YFBXsQ5Ye5moYfs+SQqGDUrlofQ2R8yoAFmAZdOzrzjcdh8kdLjXCFtxrQRgqzo42HEm/wXCBIst/C1ThFTZkTaw5H4mqfVVqUQBl6UxqLvEH5YJYVR8mVJQ6QWNHoh2iKzweuV6JbHA4ususgMD03bDP/Qchd/0mz/CgrL1tuvpgX1UjWpwULBWzVFLt+bMWk314RSj9puCAAnTP2mEZdJ2a7fxz28y3Qmus/hPZprcABbJHAyCDQBsqmEfhw0fDdDdO9v0CmXXg2B93EILqIaks+x3HSqzUI1yvBo2RmCvHfX3iMe7wrbdWCXjUn1CRVKJsUjThXotfzqgYYdNQYo2CRApbHu3+VdaUc8KeOhVk86i62gQJXpHEgwSQgDmVE2CwpxGaT8tnbo2jIHxQ9W1iwCwwR8wuyyUb1dhZUYPC2/BzdloJaiYlkcMPktGO04j9VcfUIDXg90DKJ3wJpOY7heHzGM9R8Or7VMg9q3in1CKVlXplPAblpEHsMbjjh8CYx98fPuStsvFU0KBmVetOFcw45a9zEugEssi+8xuQKFIuzaJ1nQsAcScbNwSJ7GQM/1LgBLoX6gfMrux7cQg8nmc/0rLgZ4sKWT2d8LU9dHYp0+a2cEPaPsd0bv6swxBnh2mdoj89ZpAfE/SoCCV3OUQsqjqMWXTsCbefUiAPi88R5J9T4/FzKvLXz5VyKhA93hx5H+H6m4tGqihUCzl0VDsyqdxJZsrgnysUd0bFphnD5VUIwSwBNNSk5km0oU/1k+YKNcRTO/oZbcgDCqOohvp/4D6ZI9LCluVcKLUVxs1nZcr8m9ztDo4whdADP6uztZO13i4rfq3+N"
}

请求成功时的日志

而当请求成功处理时,payload是完整的,getBody()也只触发一次:

2023-01-20 18:43:08.415 | Severity=DEBUG | [16b38379-3, L:/0:0:0:0:0:0:0:1:8084 - R:/0:0:0:0:0:0:0:1:62902] FluxReceive{pending=0, cancelled=false, inboundDone=false, inboundError=null}: subscribing inbound receiver
Received payload {
"data": "EP2+t7DXkiKcdUeScGwck6kgWC2/Fzf5Q8oU2Jp2mQzAZm6SpB/Bjp0iDyB6HOuAafFi9X1vBkMqry+4TXEfTpjncHEbuGwiTD5IkujMA6/G4aVtbZ915eHMTcCdaCfqlg2v8X4GKZbKrnV7fmMLxEUruFItgfVZ+k6/U6MkqdluK6bXkXKaeopMTHQ2u8SgsDuYnqcx7Kh1313gOeHC/QuYDm4tdkXRnccs3CAcicQRMJJHQ3c2eR7Cbx1sY5KsgLazNz795iXjxStoJKoaY8AzwKwf6EpG2D9ndnCwTEKU18s73QJbgAXlB/XBVvSKJ7VYPx1z2sBPjCIbNByfOW6FoKnMdj4ux/dh/XrfQCuGsBg9yVTyGt2i4x5A5IqzzuD9qON0+5v8FXK78CiEFcIr0zlJqH3vdbRQKWwNuZdTB//9Ya6gZxxpsQ7aq9blx+0sgSPzK3p9tD+HvQ+vSr0v/N+nCXO/wVuaLBXpRkt2dzSrb3i1exRHOEFGL6Oefjl4vGdk0WUaNc6MSqxl1P+q7VBsJRQy7tsHj+SLucrUwmN+IykJlNkbXNn8jPxW7bLV65YFBXsQ5Ye5moYfs+SQqGDUrlofQ2R8yoAFmAZdOzrzjcdh8kdLjXCFtxrQRgqzo42HEm/wXCBIst/C1ThFTZkTaw5H4mqfVVqUQBl6UxqLvEH5YJYVR8mVJQ6QWNHoh2iKzweuV6JbHA4ususgMD03bDP/Qchd/0mz/CgrL1tuvpgX1UjWpwULBWzVFLt+bMWk314RSj9puCAAnTP2mEZdJ2a7fxz28y3Qmus/hPZprcABbJHAyCDQBsqmEfhw0fDdDdO9v0CmXXg2B93EILqIaks+x3HSqzUI1yvBo2RmCvHfX3iMe7wrbdWCXjUn1CRVKJsUjThXotfzqgYYdNQYo2CRApbHu3+VdaUc8KeOhVk86i62gQJXpHEgwSQgDmVE2CwpxGaT8tnbo2jIHxQ9W1iwCwwR8wuyyUb1dhZUYPC2/BzdloJaiYlkcMPktGO04j9VcfUIDXg90DKJ3wJpOY7heHzGM9R8Or7VMg9q3in1CKVlXplPAblpEHsMbjjh8CYx98fPuStsvFU0KBmVetOFcw45a9zEugEssi+8xuQKFIuzaJ1nQsAcScbNwSJ7GQM/1LgBLoX6gfMrux7cQg8nmc/0rLgZ4sKWT2d8LU9dHYp0+a2cEPaPsd0bv6swxBnh2mdoj89ZpAfE/SoCCV3OUQsqjqMWXTsCbefUiAPi88R5J9T4/FzKvLXz5VyKhA93hx5H+H6m4tGqihUCzl0VDsyqdxJZsrgnysUd0bFphnD5VUIwSwBNNSk5km0oU/1k+YKNcRTO/oZbcgDCqOohvp/4D6ZI9LCluVcKLUVxs1nZcr8m9ztDo4whdADP6uztZO13i4rfq3+N"
}

我现在有点摸不着头脑,怀疑是不是自定义的RequestProcessor里处理请求体的方式有问题?比如在doOnNext里关闭ByteArrayOutputStream的时机不对?或者WebFlux本身的请求体接收逻辑有没有什么需要注意的配置点?有没有遇到过类似问题的朋友能给点排查思路或者解决方案?

备注:内容来源于stack exchange,提问作者Collins Ochieng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 14:47:47