一次现网问题定位-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

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

相关推荐
^—app5668663 小时前
游戏运存小启动不起来临时解决方法
运维·服务器
Ujimatsu4 小时前
虚拟机安装Debian 13.x及其常用软件(2026.4)
linux·运维·ubuntu
志栋智能4 小时前
超自动化安全:构建智能安全运营的核心引擎
大数据·运维·服务器·数据库·安全·自动化·产品运营
Edward111111116 小时前
4月28日防火墙问题
linux·运维·服务器
想学后端的前端工程师6 小时前
【补充内外网突然不通的情况】
运维·服务器
面汤放盐6 小时前
何时使用以及何时不应使用微服务:没有银弹
java·运维·云计算
子琦啊7 小时前
【算法复习】字符串 | 两个底层直觉,吃透高频题
linux·运维·算法
AOwhisky8 小时前
Kubernetes 学习笔记:集群管理、命名空间与 Pod 基础
linux·运维·笔记·学习·云原生·kubernetes
小龙在慢慢变强..8 小时前
目录结构(FHS 标准)
linux·运维·服务器