业务nginx+tomcat,nginx没有正确处理响应?

后端接口请求经过:android->nginx->tomcat(gateway)->tomcat(业务service)。
问题发生的场景:android循环请求业务的一个添加接口,第一百次时(绝大多数情况是100,偶尔也有小于100的时候,稳定复现)service的tomcat返回OK,gateway把响应转个nginx,nginx没有正确响应。nginx配置了http1.1,后端两个tomcat版本是9.0.60.抓包看的直观原因是service前99个响应的响应头Connection: keep-alive,最后一个是Connection: close.
service端口的抓包
image.png
第100个请求gateway把响应转个nginx,nginx会报错:upstream sent invalid chunked response while reading upstream, client: XXXX, server: localhost, request: "POST /api/XXX HTTP/1.1", upstream: "http://XXX:XXX/api/XXX".百度看这种错误都是nginx需要配置http1.1,但是已经配置了。
同时在gateway端口的抓包
image.png

其他相关:
1.nginx的配置比较简单

  upstream app {
     server XXXX:8105;
     #keepalive 200;
  }
  server {
    listen       XXX;
    server_name  localhost;

    #keepalive_requests 10000;
    location / {
        proxy_pass  http://app;
        client_max_body_size 2048m;
        #proxy_buffer_size 128k;
        #proxy_buffers 32 32k;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

2.关于service为什么第100个请求响应头给了Connection: close,可能和tomcat的参数server.tomcat.max-keep-alive-requests有关,但是改了这个值现象也没有改变。所有想搞明白为啥在这种情况下nginx抓包中直接就挥手了没有正确处理。在wireshark中,追踪tcp流可以看到gateway的响应,但是追踪http流就只有请求。
image.png
image.png

==========================2023-06-24补充=======================
1.抓包不是同一时间抓的
2.gateway不是用的现有框架,是用的RestTemplate做的转发,代码如下

public ResponseEntity String redirectNonFile(HttpServletRequest request, String routeUrl, String prefix)
    throws Exception
{
    // build up the redirect URL
    String redirectUrl = createRedirectUrl(request, routeUrl, prefix);
    log.info("redirectUrl={}", redirectUrl);
    RequestEntity requestEntity = createRequestEntity(request, redirectUrl);
    return restTemplate.exchange(requestEntity, String.class);
}

3.第100个请求TCP层service给gateway的响应和gateway给nginx的响应是存在差异的(也不是同一时间的抓包,但是现象是相同的)
service给gateway的响应
image.png
gateway给nginx的响应
image.png
缺少了"21.."和结尾的"..",两个".."对应的是0d0a表示"\r\n",21不明白是什么意思,这个是百度ssseerr999童鞋回答中Transfer-Encoding: chunked学到的。"\r\n"是数据块的结尾,这么看nginx的处理没有问题。问题就变成了RestTemplate在接收到响应头Connection:close和Transfer-Encoding: chunked时转发的响应少了一部分数据。

==================2023-06-29问题解决===================
前段时间用server.tomcat.max-keep-alive-requests=10000减少了出现的次数。确认是网关测的问题后,做了如下修改解决。
image.png
本来是小问题,饶了一大圈...

阅读 2.3k
1 个回答

虽然不清楚这个问题的正确答案,但是我看截图上有个奇怪的点。

  1. tomcat这边的响应中没有content-lenth,都是以Transfer-Encoding: chunked形式返回的
  2. 倒数第三张图wireshark抓包的记录中/和nginx配置文件,看上去nginx是开启了http1.1,但是没有开启keep-alive连接复用,每次转发请求给tomcat的时候都是一个新的tcp连接。
  3. 第一张图,看上去连接又是复用的,客户端的请求头中带了keep-alive。

看了wireshark的截图,tomcat发送了PSH ACK后,nginx返回了ACK,这个ACK是收到了消息,再返回的FIN+ACK开始断开连接的,所以这里应该是没问题的。

至于为什么nginx在接收到了响应后,还是给客户端返回了空的响应,按照日志的记录,应该是上游的响应有问题。但是实际看上去好像也没啥问题。

最后虽然不知道问题的原因,我感觉可以试试让上游不要返回Transfer-Encoding: chunked,说不定可以解决这个问题。

推荐问题
宣传栏