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

上周五,刚上线的项目出现了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. mysql中查看视图的元数据?

    需求描述: 查看视图的元数据的方法. 操作过程: 1.通过查看information_schema数据库下的views表来查看视图的定义语句 mysql> select definer,view ...

  2. iOS UITextField控件总结

    先声明下面总结不是自己写的. 参考网址:http://blog.csdn.net/tskyfree/article/details/8121915 //初始化textfield并设置位置及大小   U ...

  3. Visual Studio 添加SVN插件

    这两天为了开发一个移动混合式的框架,被迫去学习去使用VisualStudio,这玩意当年离开校园就再也没用过了,再次看到感觉还是很眼熟...,这篇文件就简单说明下VS下怎么安装SVN插件吧: 1 首先 ...

  4. web -- 前端访问后台跨区问题解决

    package com.xindatai.ibs.web.filter; import java.io.IOException; import javax.servlet.Filter; import ...

  5. Wcf使用Net.Tcp做回调操作

    契约: [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode = Se ...

  6. 使用pull方式解析xml文件示例:

    网上的示例太多,基本类似,个人在此做个简单的总结: 1.首先在工程的asserts目录下建一个book.xml文件: <?xml version="1.0" encoding ...

  7. php-fpm 配置进程池

    什么是 php-fpm :php 是作为一个独立服务存在的,这个服务叫做 php-fpm什么是 php-fpm pool :也就是 php-fpm 的进程池,这个进程池中运行了多个子进程,用来并发处理 ...

  8. HTML5和CSS3扁平化风格博客(进阶篇)

    趁热打铁,将剩下的部分完结~ 接上篇,增加了一些js特效:侧边栏,返回顶部. 至于效果,也不知道gif的图片怎么显示不上去了 无奈只能直接上代码了,完整版请点击: https://files.cnbl ...

  9. Masonry — 使用纯代码进行iOS应用的autolayout自适应布局

    本文转载至   http://www.ios122.com/2015/09/masonry/ 简化iOS应用使用纯代码机型自适应布局的工作,使用一种简洁高效的语法替代NSLayoutConstrain ...

  10. 普通for循环和增强for循环的区别

    1.普通for循环:自行维护循环次数,循环体自行维护获取元素的方法: int[] array = new int[]{1,2,3,4,5}; //int[] array ={1,2,3,4,5} ; ...