一、TCP_NODELAY
你怎么能够强制 socket 在它的缓冲区里发送数据?
一个解决方案是 TCP 堆栈的 TCP_NODELAY选项。这样就可使缓冲区中的数据当即发送出去。php
Nginx的 TCP_NODELAY 选项使得在打开一个新的 socket 时增长了TCP_NODELAY选项。但这时会形成一种状况:
终端应用程序每产生一次操做就会发送一个包,而典型状况下一个包会拥有一个字节的数据以及40个字节长的包头,因而产生4000%的过载,很轻易地就能令网络发生拥塞。为了不这种状况,TCP堆栈实现了等待数据 0.2秒钟,所以操做后它不会发送一个数据包,而是将这段时间内的数据打成一个大的包。这一机制是由Nagle算法保证。html
Nagle化后来成了一种标准而且当即在因特网上得以实现。它如今已经成为默认配置了,但有些场合下把这一选项关掉也是合乎须要的。如今假设某个应用程序发出了一个请求,但愿发送小块数据。咱们能够选择当即发送数据或者等待产生更多的数据而后再一次发送两种策略。
若是咱们立刻发送数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。若是请求当即发出那么响应时间也会快一些。以上操做能够经过设置套接字的 TCP_NODELAY = on 选项来完成,这样就禁用了Nagle 算法。(不须要等待0.2s)前端
二、TCP_NOPUSH
在 nginx 中,tcp_nopush 配置和 tcp_nodelay “互斥”。它能够配置一次发送数据的包大小。也就是说,它不是按时间累计 0.2 秒后发送包,而是当包累计到必定大小后就发送。java
注:在 nginx 中,tcp_nopush 必须和 sendfile 搭配使用。node
三、sendfile
如今流行的web 服务器里面都提供 sendfile选项用来提升服务器性能,那到底 sendfile是什么,怎么影响性能的呢?
sendfile其实是 Linux2.0+之后的推出的一个系统调用,web服务器能够经过调整自身的配置来决定是否利用 sendfile这个系统调用。先来看一下不用 sendfile的传统网络传输过程:
read(file,tmp_buf, len);
write(socket,tmp_buf, len);nginx
硬盘 >> kernel buffer >> user buffer>> kernel socket buffer >>协议栈web
1)通常来讲一个网络应用是经过读硬盘数据,而后写数据到socket 来完成网络传输的。上面2行用代码解释了这一点,不过上面2行简单的代码掩盖了底层的不少操做。来看看底层是怎么执行上面2行代码的:redis
上面4个步骤有4次上下文切换,有4次拷贝,咱们发现若是能减小切换次数和拷贝次数将会有效提高性能。在kernel2.0+ 版本中,系统调用 sendfile() 就是用来简化上面步骤提高性能的。sendfile() 不但能减小切换次数并且还能减小拷贝次数。算法
2)再来看一下用 sendfile()来进行网络传输的过程:
sendfile(socket,file, len);后端
硬盘 >> kernel buffer (快速拷贝到kernelsocket buffer) >>协议栈
步骤减小了,切换减小了,拷贝减小了,天然性能就提高了。这就是为何说在Nginx 配置文件里打开 sendfile on 选项能提升 web server性能的缘由。
综上,这三个参数都应该配置成on:sendfile on; tcp_nopush on; tcp_nodelay on;
当使用nginx做为反向代理时,为了支持长链接,须要作到两点:
一、保持和client的长链接:
默认状况下,nginx已经自动开启了对client链接的keep alive支持(同时client发送的HTTP请求要求keep alive)。通常场景能够直接使用,可是对于一些比较特殊的场景,仍是有必要调整个别参数(keepalive_timeout和keepalive_requests)。
1 2 3 4 |
http { keepalive_timeout 120s 120s; keepalive_requests 10000; } |
1)keepalive_timeout
语法:
keepalive_timeout timeout [header_timeout];
注:keepalive_timeout默认75s,通常状况下也够用,对于一些请求比较大的内部服务器通信的场景,适当加大为120s或者300s;
2)keepalive_requests:
keepalive_requests指令用于设置一个keep-alive链接上能够服务的请求的最大数量,当最大请求数量达到时,链接被关闭。默认是100。这个参数的真实含义,是指一个keep alive创建以后,nginx就会为这个链接设置一个计数器,记录这个keep alive的长链接上已经接收并处理的客户端请求的数量。若是达到这个参数设置的最大值时,则nginx会强行关闭这个长链接,逼迫客户端不得不从新创建新的长链接。
大多数状况下当QPS(每秒请求数)不是很高时,默认值100凑合够用。可是,对于一些QPS比较高(好比超过10000QPS,甚至达到30000,50000甚至更高) 的场景,默认的100就显得过低。
简单计算一下,QPS=10000时,客户端每秒发送10000个请求(一般创建有多个长链接),每一个链接只能最多跑100次请求,意味着平均每秒钟就会有100个长链接所以被nginx关闭。一样意味着为了保持QPS,客户端不得不每秒中从新新建100个链接。所以,就会发现有大量的TIME_WAIT的socket链接(即便此时keep alive已经在client和nginx之间生效)。所以对于QPS较高的场景,很是有必要加大这个参数,以免出现大量链接被生成再抛弃的状况,减小TIME_WAIT。
二、保持和server的长链接:
为了让nginx和后端server(nginx称为upstream)之间保持长链接,典型设置以下:(默认nginx访问后端都是用的短链接(HTTP1.0),一个请求来了,Nginx 新开一个端口和后端创建链接,后端执行完毕后主动关闭该连接)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
http { upstream BACKEND { server 192.168.0.1:8080 weight=1 max_fails=2 fail_timeout=30s; server 192.168.0.2:8080 weight=1 max_fails=2 fail_timeout=30s; keepalive 300; // 这个很重要! } server { listen 8080 default_server; server_name ""; location / { proxy_pass http://BACKEND; proxy_set_header Host $Host; proxy_set_header x-forwarded-for $remote_addr; proxy_set_header X-Real-IP $remote_addr; add_header Cache-Control no-store; add_header Pragma no-cache; proxy_http_version 1.1; // 这两个最好也设置 proxy_set_header Connection ""; } } } |
1)location中有两个参数须要设置:
1 2 3 4 5 6 7 8 |
http { server { location / { proxy_http_version 1.1; // 这两个最好也设置 proxy_set_header Connection ""; } } } |
HTTP协议中对长链接的支持是从1.1版本以后才有的,所以最好经过proxy_http_version指令设置为”1.1”;
而”Connection” header应该被清理。清理的意思,个人理解,是清理从client过来的http header,由于即便是client和nginx之间是短链接,nginx和upstream之间也是能够开启长链接的。这种状况下必须清理来自client请求中的”Connection” header。
2)upstream中的keepalive设置:
此处keepalive的含义不是开启、关闭长链接的开关;也不是用来设置超时的timeout;更不是设置长链接池最大链接数。官方解释:
咱们先假设一个场景: 有一个HTTP服务,做为upstream服务器接收请求,响应时间为100毫秒。若是要达到10000 QPS的性能,就须要在nginx和upstream服务器之间创建大约1000条HTTP链接。nginx为此创建链接池,而后请求过来时为每一个请求分配一个链接,请求结束时回收链接放入链接池中,链接的状态也就更改成idle。咱们再假设这个upstream服务器的keepalive参数设置比较小,好比常见的10.
A、假设请求和响应是均匀而平稳的,那么这1000条链接应该都是一放回链接池就当即被后续请求申请使用,线程池中的idle线程会很是的少,趋进于零,不会形成链接数量反复震荡。
B、显示中请求和响应不可能平稳,咱们以10毫秒为一个单位,来看链接的状况(注意场景是1000个线程+100毫秒响应时间,每秒有10000个请求完成),咱们假设应答始终都是平稳的,只是请求不平稳,第一个10毫秒只有50,第二个10毫秒有150:
C、一样,若是假设相应不均衡也会出现上面的链接数波动状况。
形成链接数量反复震荡的一个推手,就是这个keepalive 这个最大空闲链接数。毕竟链接池中的1000个链接在频繁利用时,出现短期内多余10个空闲链接的几率实在过高。所以为了不出现上面的链接震荡,必须考虑加大这个参数,好比上面的场景若是将keepalive设置为100或者200,就能够很是有效的缓冲请求和应答不均匀。
总结:
keepalive 这个参数必定要当心设置,尤为对于QPS比较高的场景,推荐先作一下估算,根据QPS和平均响应时间大致能计算出须要的长链接的数量。好比前面10000 QPS和100毫秒响应时间就能够推算出须要的长链接数量大概是1000. 而后将keepalive设置为这个长链接数量的10%到30%。比较懒的同窗,能够直接设置为keepalive=1000之类的,通常都OK的了。
三、综上,出现大量TIME_WAIT的状况
1)致使 nginx端出现大量TIME_WAIT的状况有两种:
2)致使后端server端出现大量TIME_WAIT的状况:
nginx没有打开和后端的长链接,即:没有设置proxy_http_version 1.1;和proxy_set_header Connection “”;从而致使后端server每次关闭链接,高并发下就会出现server端出现大量TIME_WAIT
一、配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
server { listen 80 default_server; listen 443 ssl; server_name toutiao.iqiyi.com toutiao.qiyi.domain m.toutiao.iqiyi.com; root /data/none; index index.php index.html index.htm; ###ssl settings start ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_certificate /usr/local/nginx/conf/server.pem; ssl_certificate_key /usr/local/nginx/conf/server.key; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; ssl_ciphers ALL:!kEDH!ADH:RC4+RSA:+HIGH:+EXP; ssl_prefer_server_ciphers on; ###ssl settings end … |
二、性能比较:
经过https访问Nginx通常会比http访问慢30%(https方式访问主要是耗Nginx服务器的cpu)经过下面实验验证:
统计qps时,每次清空nginx日志,而后加压,执行完毕后使用以下命令查看qps:
1 2 |
# cat log.2.3000https | grep '/api/news/v1/info?newsId=' | awk '{print$3}'| uniq | wc -l 37 |
注:不能持续加压,不然无限加大压力后每每是后端java服务出现瓶颈,致使返回给nginx的响应变慢,从而使得nginx压力变小。
三、优化: Nginx默认使用DHE算法来产生密匙,该加密算法效率很低。能够经过以下命令,删掉了kEDH算法。 ssl_ciphers ALL:!kEDH!ADH:RC4+RSA:+HIGH:+EXP;