Nginx: Connection reset by peer 错误定位

      最近Nginx反向代理遇到了“104: Connection reset by peer”错误,google了一下,这里记录一下。

本文根据众多互联网博客内容整理后形成,引用内容的版权归原始作者所有,仅限于学习研究使用。

1. nginx快速定位异常

错误信息 错误说明
"upstream prematurely(过早的) closed connection" 请求uri的时候出现的异常,是由于upstream还未返回应答给用户时用户断掉连接造成的,对系统没有影响,可以忽略
"recv() failed (104: Connection reset by peer)" (1)服务器的并发连接数超过了其承载量,服务器会将其中一些连接Down掉; (2)客户关掉了浏览器,而服务器还在给客户端发送数据; (3)浏览器端按了Stop
"(111: Connection refused) while connecting to upstream" 用户在连接时,若遇到后端upstream挂掉或者不通,会收到该错误
"(111: Connection refused) while reading response header from upstream" 用户在连接成功后读取数据时,若遇到后端upstrream挂掉或者不通,会收到该错误
"(111: Connection refused) while sending request to upstream" Nginx和upstream连接成功后发送数据时,若遇到后端upstream挂掉或者不通,会收到该错误
"(110: Connection timed out) while connecting to upstream" nginx连接后面的upstream时超时
"(110: Connection timed out) while reading upstream" nginx读取来自upstream的响应时超时
"(110: Connection timed out) while reading response header from upstream" nginx读取来自upstream的响应头时超时
"(110: Connection timed out) while reading upstream" nginx读取来自upstream的响应时超时
"(104: Connection reset by peer) while connecting to upstream" upstream发送了RST,将连接重置
"upstream sent invalid header while reading response header from upstream" upstream发送的响应头无效
"upstream sent no valid HTTP/1.0 header while reading response header from upstream" upstream发送的响应头无效
"client intended to send too large body" 用于设置允许接受的客户端请求内容的最大值,默认值是1M,client发送的body超过了设置值

2. 错误信息

Connection reset by peer

nginx的错误日志中会出现

Connection reset by peer) while reading response header from upstream, client: 1.1.1.1, server: 102.local, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000

2.1 日志查看方法

这个错误是在nginx的错误日志中发现的,为了更全面的掌握nginx运行的异常,强烈建议在nginx的全局配置中增加:

error_log logs/error.log notice;

这样,就可以记录nginx的详细异常信息。

3. 错误原因

3.1 连接已经被上游close。

    readv() failed (104: Connection reset by peer) while reading upstream

      服务端确实已经关闭了连接: upstream发送了RST,将连接重置。

      errno = 104 错误表明你在对一个对端socket已经关闭的的连接调用write或send方法,在这种情况下,调用write或send方法后,对端socket便会向本端socket发送一个RESET信号,在此之后如果继续执行write或send操作,就会得到errno为104,错误描述为connection reset by peer。

      如果对方socket已经执行了close的操作,本端socket还继续在这个连接上收发数据,就会触发对端socket发送RST报文。按照TCP的四次握手原理,这时候本端socket应该也要开始执行close的操作流程了,而不是接着收发数据。 

抓包确认下

tcpdump -i any -s0 -A port

    RESET信号可以抓包看到,如下所示:

  • 比如,当后端为php程序时,如果php运行较慢,并超出php-fpm.conf的request_terminate_timeout设置的秒数。request_terminate_timeout用于设置当某个php脚本运行最长时间,若超出php-fpm进程管理器强行中止当前程序,并关闭fastcgi和nginx的网络连接,然后nginx中就会出现Connection reset by peer的错误了。

      也就是说,产生这个错误的原因是:php 程序的运行时间超出request_terminate_timeout设置的值。

      在php-fpm环境下,在php的安装目录的etc/php-fpm.conf中有此值的设置项,可将其设置为0或更大的值。这样将php的request_terminate_timeout设置为较大的值或0,可减少因php脚本执行时行过长导致nginx产生Connection reset by peer错误。

  • 比如,当后端为java 程序时

java 的也类似,不能Java端主动关闭连接。 如果上游的tomcat 或者 netty 已经关闭连接, 那么nginx 肯定就是 Connection reset by peer

3.2:数据长度不一致

     发送端和接收端事先约定好的数据长度不一致导致的,接收端被通知要收的数据长度小于发送端实际要发送的数据长度。

3.3 nginx的buffer太小,timeout太小。

参考:

https://juejin.cn/post/6844904084021968909

https://cloud.tencent.com/developer/article/1521256

3.3.1: FastCGI 缓存小,timeout太小。

      nginx的buffer太小,timeout太小。

      主要指php的环境,nginx如果要解析php脚本语言,就必须通过配置fastcgi模块来提供对php支持。

问题概述:图片bit 64生成数据流太大,导致小程序分享弹窗的二维码图片生成失败

nginx http模块添加以下参数配置:

 

fastcgi_buffer_size 128k;

fastcgi_buffers 4 128k;

fastcgi_busy_buffers_size 128k;

fastcgi_temp_file_write_size 128k;

后台报错:

img

排查:

Client------>nginx------->h5------>nginx---------->client

客户端通过h5的nginx页面点击,nginx反向代理到h5 [无异常]

h5通过客户端请求调取相应接口 [无异常]

接口返回数据通过nginx展示给客户端 [异常]

Ps: 图片通过bit 64解析生成返回给客户端,由于数据长度太长导致

解决方法:

调整nginx配置文件参数,修改后参数:

fastcgi_buffer_size 256k;

fastcgi_buffers 4 256k;

fastcgi_busy_buffers_size 256k;

fastcgi_temp_file_write_size 256k;

先简单的说一下 Nginx 的 buffer 机制,对于来自 FastCGI Server 的 Response,Nginx 将其缓冲到内存中,然后依次发送到客户端浏览器。缓冲区的大小由 fastcgi_buffers 和 fastcgi_buffer_size 两个值控制。

比如如下配置:

 

fastcgi_buffers 8 4K;

fastcgi_buffer_size 4K;

fastcgi_buffers 控制 nginx 最多创建 8 个大小为 4K 的缓冲区,而 fastcgi_buffer_size 则是处理 Response 时第一个缓冲区的大小,不包含在前者中。所以总计能创建的最大内存缓冲区大小是 84K+4K = 36k。而这些缓冲区是根据实际的 Response 大小动态生成的,并不是一次性创建的。比如一个 8K 的页面,Nginx 会创建 24K 共 2 个 buffers。

当 Response 小于等于 36k 时,所有数据当然全部在内存中处理。如果 Response 大于 36k 呢?fastcgi_temp 的作用就在于此。多出来的数据会被临时写入到文件中,放在这个目录下面。同时你会在 error.log 中看到一条类似 warning。

显然,缓冲区设置的太小的话,Nginx 会频繁读写硬盘,对性能有很大的影响,但也不是越大越好,没意义,呵呵!

FastCGI缓冲设置主要参数

fastcgi_buffers 4 64k

这个参数指定了从FastCGI进程到来的应答,本地将用多少和多大的缓冲区读取,假设一个PHP或JAVA脚本所产生页面大小为256kb,那么会为其分配4个64kb的缓冲来缓存;若页面大于256kb,那么大于256kb的部分会缓存到fastcgi_temp指定路径中,这并非是个好办法,内存数据处理快于硬盘,一般该值应该为站点中PHP或JAVA脚本所产生页面大小中间值,如果站点大部分脚本所产生的页面大小为256kb,那么可把值设置为16 16k,4 64k等。

fastcgi_buffer_size=64k

读取fastcgi应答第一部分需要多大缓冲区,该值表示使用1个64kb的缓冲区读取应答第一部分(应答头),可以设置为fastcgi_buffers选项缓冲区大小。

fastcgi_connect_timeout=300

连接到后端fastcgi超时时间,单位秒,下同。

fastcgi_send_timeout=300

向fastcgi请求超时时间(这个指定值已经完成两次握手后向fastcgi传送请求的超时时间)

fastcgi_reAd_timeout=300

接收fastcgi应答超时时间,同理也是2次握手后。

3.3.2: proxy_buffer缓存小

原因就是请求的头文件过大导致502错误

解决方法就是提高头部的缓存:

 

http{

client_header_buffer_size 5m;

location / {

proxy_buffer_size 128k;

proxy_busy_buffers_size 192k;

proxy_buffers 4 192k;

}

}

另外可能:

nginx的buffer太小,timeout太小。

nginx http模块添加以下参数配置:

client_header_buffer_size 64k;
large_client_header_buffers 4 64k;
client_body_buffer_size 20m;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 128k;
fastcgi_busy_buffers_size 256k;
gzip_buffers 16 8k;
proxy_buffer_size 64k;
proxy_buffers 4 128k;
proxy_busy_buffers_size 256k;

keepalive_timeout 240;
fastcgi_connect_timeout 600;
fastcgi_send_timeout 600;
fastcgi_read_timeout 600;

proxy_connect_timeout 600s;
proxy_send_timeout 1200;
proxy_read_timeout 1200;

3.3.3 proxy_max_temp_file_size太小

       比如服务器架构是nginx + tomcat 提供下载上传以及其他服务,nginx上配置proxy_max_temp_file_size=200m,在前后端速度不对等的时候提供数据缓存。但是实际运行中发现,当客户端下载速度比较慢时,大文件下到200多M时就会失败

       针对这个问题,主要是由于客户端下载速度比较慢,而nginx和tomcat之间高速传输,两端速度不对等,导致nginx和tomcat之间的连接一直处于idle状态,一定时间(tomcat的keepalive时间)后tomcat主动断开连接,客户端下载失败。

3.4 没有设置keepalive

      ngx_http_upstream_check_module这个模块,在使用tcp检测后端状态时,只进行了TCP的三次握手,没有主动断开这个连接,而是等待服务端来断开。当后端是nginx或者tomcat时(linux上),超时后后端会发fin包关闭这个连接。这个错误日志recv() failed (104: Connection reset by peer)是在后端为IIS的情况下抛出的,抓包发现IIS并不会发fin包来断开链接,而是在超时后发RST包重置连接,所以导致了这个问题。

      从这个问题也反应出ngx_http_upstream_check_module这个模块还是需要完善下检测机制的,如果是在检测后端状态后主动关闭这个连接,应该就不会出现connect reset这个问题

     通过修改源代码已经解决了该问题

 

static ngx_check_conf_t ngx_check_types[] = {
{ NGX_HTTP_CHECK_TCP,
ngx_string("tcp"),
ngx_null_string,
0,
ngx_http_upstream_check_peek_handler,
ngx_http_upstream_check_peek_handler,
NULL,
NULL,
NULL,
0,
1 },

将最后一行的1改为0即可,根据数据结构分析可得知,这个1代表启用keepalived,所以客户端才不会主动断开连接,因为这是tcp的端口连通性检查,不需要keepalived,将其改为0禁止keepalived即可。

修改之后的代码如下:
static ngx_check_conf_t ngx_check_types[] = {
{ NGX_HTTP_CHECK_TCP,
ngx_string("tcp"),
ngx_null_string,
0,
ngx_http_upstream_check_peek_handler,
ngx_http_upstream_check_peek_handler,
NULL,
NULL,
NULL,
0,
0 },

如果使用了 proxy_pass upstream,可以配置keepalive


 upstream gateway{
            server 192.168.88.31:8081;
            server 192.168.88.44:8081;
            server 192.168.88.115:8081;
            server 192.168.88.80:8081;
            #以下是新增配置
            keepalive 100;
}

location / {
           proxy_pass http://gateway;
           proxy_set_header   Host             $host;
           proxy_set_header   X-Real-IP        $remote_addr;
           proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
           #以下是新增配置
           proxy_connect_timeout      120;   
           proxy_send_timeout         300;    
           proxy_read_timeout         300; 
           proxy_http_version 1.1;    
           proxy_set_header Connection ""; 
}

3.5:设置lingering_close

​ 即使你禁用了 http keepalive,nginx 仍然会尝试处理 HTTP 1.1 pipeline 的请求。你可以配置
lingering_close off 禁用此行为,但这不是推荐的做法,因为会违反 HTTP 协议。见

​ http://nginx.org/en/docs/http/ngx_http_core_module.html#lingering_close

4. upstream sent invalid chunked response while reading upstream解决

4.1  http1.0 对keepalive是不支持

默认http_version是1.0,http1.0对keepalive是不支持的,所以导致了此问题。

proxy_http_version 1.1;

proxy_set_header Connection "";

两条规则缺一不可,都是为了支持后端请求 HTTP1.1 协议

Nginx: Connection reset by peer 错误定位_第1张图片

反向代理配置这里不展开,参考2中重点提到一句:

      Be extra sure to include proxy_http_version 1.1 or the web socket connection will fail.

对比自己的NGINX配置,确实少了此配置项。

4.2 问题背景:

    一开始是一个下载文件的需求,但是不能直接下载,需要通过nginx做代理转发后,才能将文件流输出给合作方.然后我们将url的请求通过nginx代理到真实去下载文件流的服务器发现并不能下载到文件.(是通过请求浏览器去下载的,浏览器会显示此网页无法正常运作)

Nginx: Connection reset by peer 错误定位_第2张图片

 


4.3 问题分析:

    1.一开始以为是代码问题,检查了代码,发现直接调用接口是可以下载成功的,那么问题就出在转发上面了.

    2.然后查看nginx的error日志,发现报的错误是upstream sent invalid chunked response while reading upstream.之后就是google搜索问题,发现在nginx的location模块里面加上proxy_http_version 1.1就可以了.

 

    3.也可以查看nginx官网对于proxy_http_version的描述,

Nginx: Connection reset by peer 错误定位_第3张图片

       如果不填写http的版本的话,默认是http1.0.从nginx的error日志上看出原始请求是使用的http1.1的版本,而且下载文件是使用的分块传递,http1.0是不支持这个特性的.可以简单的了解一下分块传递.

这里写图片描述

       http1.0是建立连接,发送请求信息,接收请求信息,断掉连接.不支持分块传递,所以nginx报错了.

4.4 问题总结:

    这个问题与其说是nginx报错,不如说是不了解http不同版本之间特性的差异.而且要记住一点的是nginx代理后的默认http版本是1.0.如果原始请求是长连接或者分块传递,记得加上http1.1的参数.

proxy_http_version 1.1;

proxy_set_header Connection "";

两条规则缺一不可,都是为了支持后端请求 HTTP1.1 协议

5. 参考:

https://github.com/alibaba/tengine/issues/901

https://my.oschina.net/u/1024107/blog/1838968

https://blog.csdn.net/zjk2752/article/details/21236725

http://nginx.org/en/docs/http/ngx_http_core_module.html#lingering_close

https://blog.csdn.net/crj121624/article/details/79956283

https://www.jianshu.com/p/319791e8cd37

https://blog.csdn.net/sc9018181134/article/details/82055225

请我喝咖啡

如果觉得文章写得不错,能对你有帮助,可以扫描我的微信二维码请我喝咖啡哦~~哈哈~~

你可能感兴趣的:(Nginx,nginx,运维)