记一次,排查错误所遇到的问题,和学习到的内容。

上周五,刚上线的项目出现了503 ,查看日志发现如下内容:

System.Exception: Request api/blogpost/zzkDocs
<html>^M
<head><title> Internal Server Error</title></head>^M
<body bgcolor="white">^M
<center><h1> Internal Server Error</h1></center>^M
<hr><center>nginx/1.10. (Ubuntu)</center>^M
</body>^M
</html>^M
---> Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: <. Path '', line , position .
at Newtonsoft.Json.JsonTextReader.ParseValue()
at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at System.Net.Http.HttpClientExtensions.<ReadAsAsync>d__2`.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at CNBlogs.Zzk.ServiceAgent.ZzkDocumentManager`.<GetZzkDocAsync>d__8.MoveNext() in /home/xiaokang/CNBlogsZzk/CNBlogsZzk/src/CNBlogs.Zzk.ServiceAgent/ZzkDocumentManager.cs:line

日志报错的是NewtonJson 对文档进行Josn格式序列化的时候报错,报错内容是第0行,第0列的字符无法匹配,在google找了很多这样的错误,然而都没有办法解决。后来我才恍然大悟,我忽视了日志里面的:

<html>^M
<head><title>500 Internal Server Error</title></head>^M
<body bgcolor="white">^M
<center><h1>500 Internal Server Error</h1></center>^M
<hr><center>nginx/1.10.3 (Ubuntu)</center>^M
</body>^M
</html>^M

原来是本来请求的文档却反馈了503的html,这样当然没有办法转成json。站点是从另外一台服务器上获取的文档。于是打开提供数据的服务器,查看nginx日志终于发现了日志中存在786 worker_connections are not enough while connecting to upstream:

顾名思义就是连接数已经超过最大了768个限制了。问题查找到这里终于明白问题的出处了,因为我的提供数据的服务器使用了nginx代理服务器,nginx的配置文件限制了最大连接数为768个。那么出现这个问题要么是并发连接数量很大,要么是tcp连接请求完数据之后没有释放。我一开始就把注意力放在后者。tcp连接没有正常关闭,导致连接数越来越多。

这里又去重新回顾了tcp连接的三次握手和四次握手。

这张图是一个完整的tcp建立和断开连接的过程。Tcp建立的时候经历了三次握手,断开经历了四次握手。

三次握手:

第一次:A服务器向B服务器发送SYN包,表示我要和你建立连接,进入SYN-SEND状态。第二次:B服务器接受到SYN包,发送SYN+ACK报文到B服务器。第三次:B服务器发送ACK报文到A服务器,AB服务器进入ESTABLISED 状态。

四次握手:

第一次:A服务器向B服务器发送FIN报文进入FIN-WAIT1状态,表示我要断开连接。B服务器没有立刻发送FIN+ACK报文,因为B服务器可能还有数据需要传送所以第二次握手先发送ACK报文,A服务器进入FIN-WAIT2状态,B服务器进入CLOSE-WAIT状态。第三次:B服务器发送完数据之后,再发送FIN报文,表示可以断开连接。第四次,A服务器接受FIN报文,发送ACK报文给B,进入TIME-WAIT状态,B接受ACK报文,关闭连接。

A服务器能不能发送完ACK之后不进入TIME_WAIT就直接进入CLOSE状态呢?不行的,这个是为了TCP协议的可靠性,由于网络原因,ACK可能会发送失败,那么这个时候,被动一方会主动重新发送一次FIN,这个时候如果主动方在TIME_WAIT状态,则还会再发送一次ACK,从而保证可靠性。

这里再介绍一个概念,就是Keep-Alive 长连接。在HTTP 1.1版本的请求中,Keep-alive 是默认的。客户端发送keep-alive请求即表示,我主动断开服务器才可以断开,否在我们的连接一直建立。那为何要这样呢?因为每一次tcp的建立都要消耗资源,如果请求完立即关闭tcp连接,下次还要重新建立,这样服务器的负担就大大增加。当然这样也有弊端,建立的tcp不断开,占有的端口不释放,那么随着并发量很大,就会出现tcp连接无法建立的情况,就如同上面的错误一样。所以对于并发量大的短请求应当取消keep-alive。

于是我猜想报错的原因就是所有的请求都是keep-alive导致短时间内不能释放,然而我的站点并没有那么大的并发量,怎么想也不可能爆满啊。干脆设置一些keep-alive的时间限制,设置一个较短的时间,比如20s,20s之内tcp不再发送http请求,就关闭这条tcp连接。这样可以很好解决keep-alive的弊端。

然后这样的设置根本无济于事,因为问题的原因可能不再并发量这里,于是我又把注意力转移到tcp连接的TIME-WAIT上。因为很多时候,站点负荷大是因为出现了大量的TIME-WAIT的tcp连接。tcp连接主动关闭的一方会进入TIME-WAIT状态,这个状态会持续比较久的时间,这样如果有大量的tcp处于TIME-WAIT状态:

  • 网络情况不好时,如果主动方无TIME_WAIT等待,关闭前个连接后,主动方与被动方又建立起新的TCP连接,这时被动方重传或延时过来的FIN包过来后会直接影响新的TCP连接;
  • 同样网络情况不好并且无TIME_WAIT等待,关闭连接后无新连接,当接收到被动方重传或延迟的FIN包后,会给被动方回一个RST包,可能会影响被动方其它的服务连接。
  • 过多的话会占用内存,一个time_await占用4k大小

我查看netstat -net | grep xxxxxx 当我多次连续点击请求按钮,果然有出现很多tcp连接,很快处于TIME-WAIT状态,但是我太天真了,Linux系统对于TIME-WAIT 有一个处理机制TIME-WAIT的tcp连接很快被回收关闭,并没有占有很长时间。

于是再回头查看自己的代码,因为我的请求是使用HttpClient发送的,如果HttpClient不是static的,那么每个请求都会创建一个HttpClient,这样每次的请求都会新建一个连接,可是发现代码并没有错。

最后绝望的我去查看了站点的日志,发现果然出现503的时候请求量高的惊人,再仔细查看发现,这些请求都是广告请求!!原来出现worker_connections are not enough while connecting to upstream 不是什么tcp连接的问题,也不是nginx配置的问题,这些地方不会出问题的。出问题的地方要么是你的代码,要么就是真的有很多请求。把广告请求屏蔽就好了。

这里再留一个问题,我的项目是用asp.net core2.0写的,使用nginx作转发。打开netstat发现有很多localhost与localhost之间的tcp连接,一度让我以为这是问题的所在。现在还不知道为什么会有这些处于TIME-WAIT的本地tcp连接:

Nginx 日志 worker_connections are not enough while connecting to upstream的更多相关文章

  1. 2048 worker_connections are not enough while connecting to upstream

    2048 worker_connections are not enough while connecting to upstream http://mailman.nginx.org/piperma ...

  2. [nginx] connect() failed (111: Connection refused) while connecting to upstream, client: 101.18.123.107, server: localhost,

    nginx一直报错, 2016/12/02 10:23:19 [error] 1472#0: *31 connect() failed (111: Connection refused)while c ...

  3. 快速掌握Nginx(四) —— Nginx日志切片和常用配置总结

    1.Nginx日志管理 1.日志简单介绍 Nginx提供了日志记录的功能,日志文件在对我们管理网站十分有用,通过访问日志(access_log)我们可以获取请求来源.客户端信息.请求的资源等信息:通过 ...

  4. Centos 7 上使用nginx为Node.js配置反向代理时错误:(13: Permission denied) while connecting to upstream

    错误来源:Centos 7 上使用nginx为Node.js配置反向代理时产生(13: Permission denied) while connecting to upstream的错误 nginx ...

  5. nginx访问502 gateway,*1 connect() failed (111: Connection refused) while connecting to upstream

    安装好nginx,php环境后,配置虚拟主机,结果访问后就报502 gateway,查看日志文件后,显示错误如下: 2019/04/29 16:24:39 [error] 19433#19433: * ...

  6. Nginx filebeat+logstash+Elasticsearch+kibana实现nginx日志图形化展示

    filebeat+logstash+Elasticsearch+kibana实现nginx日志图形化展示   by:授客  QQ:1033553122   测试环境 Win7 64 CentOS-7- ...

  7. nginx 报错 connect() failed (111: Connection refused) while connecting to upstream

    公司网站搬迁到新服务器后,发现站点访问不了,network里面提示502,查看相关的server配置,感觉没有什么问题,经过测试发现txt.html.等非php文件能够直接访问,也就是php访问不了, ...

  8. nginx日志格式配置

    我一向对日志这个东西有些许恐惧,因为在分析日志是需要记住不同服务器日志的格式,就拿提取ip这一项来说,有的服务器日志是在第一列,有的是第二列或则第三列等等.知道今天我才发现,日志格式是可以自定义配置的 ...

  9. nginx日志

    相关知识可参考文章:nginx日志格式及自定义日志配置 1.查看nginx的log配置 1)vim /etc/nginx/nginx.conf 打开为 user nginx;worker_proces ...

随机推荐

  1. springboot+jps+druid项目搭建

    pom.xml文件 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www ...

  2. [mysql] Incorrect string value: '\xE4\xBC\x9A\xE5\x91\x98' for column 'name' at row 1

    数据库字符集错误, 修改为UTF8/utf8mb4字符集即可.

  3. Asp.net动态生成表单

    control.ascx <%@ Control Language="C#" AutoEventWireup="true" CodeBehind=&quo ...

  4. vue中使用mockjs

    第一步安装mockjs:npm i mockjs -S 在src目录下新建mock文件夹,文件夹添加test.js test.js内容如下: import Mock from 'mockjs'; co ...

  5. HTML开发之(块级标签,行内标签,行内块标签)

    显示模式的特性: 主要分为两大类: 块级元素:独占一行,对宽高的属性值生效:如果不给宽度,块级元素就默认为浏览器的宽度,即就是100%宽: 行内元素:可以多个标签存在一行,对宽高属性值不生效,完全靠内 ...

  6. centos 上不了网了

    昨天还用的好好的,今天就上不了网了,郁闷,不过,正好是一次学习linux网络配置的好机会,这会已经把它折腾好了,此文就是在linux下面的浏览器中写的! 先检查一下虚拟机中的网络设置是否正常,由于我的 ...

  7. .net垃圾收集机制【转】

    在.NET Framework中,内存中的资源(即所有二进制信息的集合)分为"托管资源"和"非托管资源".托管资源必须接受.NET Framework的CLR( ...

  8. JBuilder+struts一个常见异常

    [org.apache.commons.digester.Digester]-[ERROR] Parse Error at line 3 column 22: The content of eleme ...

  9. 【cs229-Lecture12】K-means算法

    上课内容: 无监督学习: K-means聚类算法 混合高斯模型 jensen不等式(用于推导出EM算法的一般形式) EM(Expectation Maximization)算法(最大期望算法) K-m ...

  10. 微信内置浏览器浏览H5页面弹出的键盘遮盖文本框的解决办法(转)

    最近在做微信公众号的内嵌页面,发现点击输入框时键盘盖住文本框,找到一段代码解决了这个问题. iOS和android手机都已亲测,需要的可以直接拷贝到代码中使用. js代码如下: $(function ...