一次现网问题定位-nginx报错upstream sent invalid chunked response while reading upstream

背景

版本发布后,附件下载功能报错。经观察:文件大小1k以下功能正常,大于等于1k下载报错。

nginx日志显示以下报错

c 复制代码
2026/03/16 11:38:16 [error] 11#0: *465 upstream sent invalid chunked response while reading upstream, client: x.x.x.x, server: localhost, request: "GET /x/x/mediaFile/download?mediaType=img&mediaId=x HTTP/1.1", upstream: "http://x.x.x.x:x/mediaFile/download?mediaType=img&mediaId=x", host: "x", referrer: "x"

定位过程

以前定位过类似问题(https://blog.csdn.net/shuxiaohua/article/details/149671002),大概知道是响应的header传的有问题。

组网如下:

测试环境复现后,在A中打印了B返回的响应头。有如下发现

当下载的文件超过1K(大于等于)后,B的响应头中包含{"Transfer-Encoding":"chunked"}

如果文件小于1k,则不包含该响应头。

翻阅代码后,大概知道了原因。

A的代码中增加了"Content-Length"响应头,tomcat会正常传输字节流(详细见之前的文章)。当文件超过1k后,B的响应头包含"Transfer-Encoding":"chunked",会被A透传给Nginx,但此时A的响应并不是按照这个编码传输的数据,因此Nginx无法解析响应而报错。

A服务问题代码

c 复制代码
    public void download(... HttpServletResponse servletResponse) {
        # 调用B服务
        Response response = PostManUtil.getStream(commonOkHttpClient, url, null, requestHeaders);
        
        Headers responseHeaders = response.headers();
        HttpHeaders headers = new HttpHeaders();
        for (int i = 0; i < responseHeaders.size(); i++) {
            headers.add(responseHeaders.name(i), responseHeaders.value(i));
        }
        
        # 该代码产生的问题
        if (!headers.containsKey(HttpHeaders.CONTENT_LENGTH)) {
            headers.setContentLength(resource.contentLength());
        }

        ResponseBody responseBody = response.body();
        byte[] responseBytes = responseBody.bytes();        
        OutputStream outputStream = servletResponse.getOutputStream();
        outputStream.write(responseBytes );
        outputStream.flush();
    }

问题解决过程

确认"Transfer-Encoding":"chunked"什么场景下产生的,看能否在不修改代码的情况下,规避问题

疑问,B服务的响应为什么超过1k后,编码就自动变成"Transfer-Encoding":"chunked"

从B服务的代码可以看到是有设置"Content-Length"头的,按照之前的源码分析,如果传了该头,tomcat是不可能采用chunked编码传输的。为了确认这一点,在dev环境断点调试tomcat相关代码,发现B服务确实不会传"Transfer-Encoding":"chunked"头。

那只有可能是ALB加的。

c 复制代码
    # 部分代码有省略
    private void writeContent2Client(HttpServletRequest httpServletRequest, HttpServletResponse response,
            String fileName, byte[] content) throws IOException {
        try (OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
            response.reset();
            response.setStatus(HttpServletResponse.SC_OK);
            response.addHeader("Content-Disposition",
                    "attachment;filename=\"" + UrlEscapers.urlFragmentEscaper().escape(fileName) + "\"");
            response.addHeader(CONTENT_LENGTH, String.valueOf(content.length));
            outputStream.write(content);
            outputStream.flush();
        }
    }

ALB为什么会增加"Transfer-Encoding":"chunked"

使用千问输入"nginx 1k以上会使用chunked编码"(ALB是基于nginx),其给出了几种可能的场景。其中一种是开启了压缩。此时我想起来以前定位过相似的问题,之前找过ALB确认过他们确实有开启压缩。故咨询他们压缩配置是如何设置的。

c 复制代码
gzip on;  
gzip_buffers 4 16k;  
gzip_comp_level 2; 
gzip_min_length 1k; 
gzip_http_version 1.0;  
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;  
gzip_vary on; 

通过其配置也能印证当前的现象,gzip只有在响应大小超过1k后开始进行压缩,压缩后就会自动添加"Transfer-Encoding":"chunked"头。

问题规避方案

尝试联系ALB的人确认是否能够修改压缩相关的参数,答案否。

当前问题是A服务传了"Transfer-Encoding":"chunked"头,但是并没有按照这个编码进行传输。那我们也可以开启A服务tomcat的压缩,这样就能按照chunked编码传输(开启压缩后,服务器是没办法知道压缩后的大小,也不可能压缩完了后,在整体传输,这样对于报文比较大的,会占用大量内存,所以一般开启压缩后都是使用chunked编码传输,压缩一部分然后传输一部分)。从而使nginx解码正确。A服务增加配置如下:

c 复制代码
server.compression.enabled=true
server.compression.min-response-size=1024
server.compression.mime-types=text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml

经验证后,问题得以解决。

相关推荐
成都极云科技2 小时前
「算力服务器托管企业」——IDC行业的新方向
运维·服务器
**蓝桉**3 小时前
Prometheus时间出现误差
linux·运维·prometheus
浊酒入清梦3 小时前
Gradle多模块项目构建docker镜像脚本
运维·docker·容器
江畔何人初3 小时前
HPA是如何在k8s集群实现自动扩缩容机制的
linux·运维·服务器·云原生·kubernetes
杨云龙UP3 小时前
Oracle 19c RAC多节点运行状态最简排查指南_20260316
linux·运维·服务器·数据库·sql·oracle
Scabbards_3 小时前
基于docker的LLM服务部署
运维·docker·容器
于眠牧北3 小时前
ubuntu22.04在docker中安装redis6.2.x并配置远程连接
运维·redis·docker·容器
暴力求解4 小时前
Linux---ELF与库加载
linux·运维·服务器
Shaidou_Data4 小时前
数据要素自动化实践:沙淘金数据清洗与治理技术方案详解
运维·自动化