一、nginx之tcp_nopush、tcp_nodelay、sendfile
1、TCP_NODELAY
你怎么可以逼迫 socket 在它的缓冲区里发送数据?
一个办理方案是 TCP 堆栈的 TCP_NODELAY选项。这样就可以使缓冲区中的数据立即发送出去。
Nginx的 TCP_NODELAY 选项使得在打开一个新的 socket 时增加了TCP_NODELAY选项。但这时会造成一种情形:
终端运用程序每产生一次操作就会发送一个包,而范例情形下一个包会拥有一个字节的数据以及40个字节长的包头,于是产生4000%的过载,很轻易地就能令网络发生拥塞。为了避免这种情形,TCP堆栈实现了等待数据 0.2秒钟,因此操作后它不会发送一个数据包,而是将这段韶光内的数据打成一个大的包。这一机制是由Nagle算法担保。
Nagle化后来成了一种标准并且立即在因特网上得以实现。它现在已经成为默认配置了,但有些场合下把这一选项关掉也是合乎须要的。现在假设某个运用程序发出了一个要求,希望发送小块数据。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。如果我们立时发送数据,那么交互性的以及客户/做事器型的运用程序将极大地受益。如果要求立即发出那么相应韶光也会快一些。以上操作可以通过设置套接字的 TCP_NODELAY = on 选项来完成,这样就禁用了Nagle 算法。(不须要等待0.2s)
2、TCP_NOPUSH在 nginx 中,tcp_nopush 配置和 tcp_nodelay “互斥”。它可以配置一次发送数据的包大小。也便是说,它不是按韶光累计 0.2 秒后发送包,而是当包累计到一定大小后就发送。
注:在 nginx 中,tcp_nopush 必须和 sendfile 搭配利用。
3、sendfile现在盛行的web 做事器里面都供应 sendfile选项用来提高做事器性能,那到底 sendfile是什么,怎么影响性能的呢?sendfile实际上是 Linux2.0+往后的推出的一个别系调用,web做事器可以通过调度自身的配置来决定是否利用 sendfile这个别系调用。先来看一下不用 sendfile的传统网络传输过程:read(file,tmp_buf, len);write(socket,tmp_buf, len);
硬盘 >> kernel buffer >> user buffer>> kernel socket buffer >>协议栈
1)一样平常来说一个网络运用是通过读硬盘数据,然后写数据到socket 来完成网络传输的。上面2行用代码阐明了这一点,不过上面2行大略的代码粉饰了底层的很多操作。来看看底层是怎么实行上面2行代码的:
系统调用 read()产生一个高下文切换:从 user mode 切换到 kernel mode,然后 DMA 实行拷贝,把文件数据从硬盘读到一个 kernel buffer 里。数据从 kernel buffer拷贝到 user buffer,然后系统调用 read() 返回,这时又产生一个高下文切换:从kernel mode 切换到 user mode。系统调用write()产生一个高下文切换:从 user mode切换到 kernel mode,然后把步骤2读到 user buffer的数据拷贝到 kernel buffer(数据第2次拷贝到 kernel buffer),不过这次是个不同的 kernel buffer,这个 buffer和 socket干系联。系统调用 write()返回,产生一个高下文切换:从 kernel mode 切换到 user mode(第4次切换了),然后 DMA 从 kernel buffer拷贝数据到协议栈(第4次拷贝了)。上面4个步骤有4次高下文切换,有4次拷贝,我们创造如果能减少切换次数和拷贝次数将会有效提升性能。在kernel2.0+ 版本中,系统调用 sendfile() 便是用来简化上面步骤提升性能的。sendfile() 不但能减少切换次数而且还能减少拷贝次数。
2)再来看一下用 sendfile()来进行网络传输的过程:sendfile(socket,file, len);
硬盘 >> kernel buffer (快速拷贝到kernelsocket buffer) >>协议栈
系统调用sendfile()通过 DMA把硬盘数据拷贝到 kernel buffer,然后数据被 kernel直接拷贝到其余一个与 socket干系的 kernel buffer。这里没有 user mode和 kernel mode之间的切换,在 kernel中直接完成了从一个 buffer到另一个 buffer的拷贝。DMA 把数据从 kernelbuffer 直接拷贝给协议栈,没有切换,也不须要数据从 user mode 拷贝到 kernel mode,由于数据就在 kernel 里。步骤减少了,切换减少了,拷贝减少了,自然性能就提升了。这便是为什么说在Nginx 配置文件里打开 sendfile on 选项能提高 web server性能的缘故原由。
综上,这三个参数都该当配置成on:sendfile on; tcp_nopush on; tcp_nodelay on;
nginx的实现,以及源码剖析
更多Linux做事器开拓高阶知识Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等等学习资料请后台私信【学习资料】获取二、nginx长连接——keepalive当利用nginx作为反向代理时,为了支持长连接,须要做到两点:
从client到nginx的连接是长连接从nginx到server的连接是长连接1、保持和client的长连接:
默认情形下,nginx已经自动开启了对client连接的keep alive支持(同时client发送的HTTP要求哀求keep alive)。一样平常场景可以直策应用,但是对付一些比较分外的场景,还是有必要调度个别参数(keepalive_timeout和keepalive_requests)。
http { keepalive_timeout 120s 120s; keepalive_requests 10000;}
1)keepalive_timeout
语法:
keepalive_timeout timeout [header_timeout];
第一个参数:设置keep-alive客户端连接在做事器端保持开启的超市价(默认75s);值为0会禁用keep-alive客户端连接;第二个参数:可选、在相应的header域中设置一个值“Keep-Alive: timeout=time”;常日可以不用设置;注: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。
2、保持和server的长连接:为了让nginx和后端server(nginx称为upstream)之间保持长连接,范例设置如下:(默认nginx访问后端都是用的短连接(HTTP1.0),一个要求来了,Nginx 新开一个端口和后端建立连接,后端实行完毕后主动关闭该链接)
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中有两个参数须要设置:
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;更不是设置长连接池最大连接数。官方阐明:
The connections parameter sets the maximum number of idle keepalive connections to upstream servers connections(设置到upstream做事器的空闲keepalive连接的最大数量)When this number is exceeded, the least recently used connections are closed. (当这个数量被打破时,最近利用最少的连接将被关闭)It should be particularly noted that the keepalive directive does not limit the total number of connections to upstream servers that an nginx worker process can open.(特殊提醒:keepalive指令不会限定一个nginx worker进程到upstream做事器连接的总数量)我们先假设一个场景: 有一个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:
下一个10毫秒,有100个连接结束要求回收连接到连接池,但是假设此时要求不屈均10毫秒内没有估量的100个要求进来,而是只有50个要求。把稳此时连接池回收了100个连接又分配出去50个连接,因此连接池内有50个空闲连接。然后把稳看keepalive=10的设置,这意味着连接池中最多容许保留有10个空闲连接。因此nginx不得不将这50个空闲连接中的40个关闭,只留下10个。再下一个10个毫秒,有150个要求进来,有100个要求结束任务开释连接。150 - 100 = 50,空缺了50个连接,减掉前面连接池保留的10个空闲连接,nginx不得不新建40个新连接来知足哀求。C、同样,如果假设相应不屈衡也会涌现上面的连接数颠簸情形。
造成连接数量反复震荡的一个推手,便是这个keepalive 这个最大空闲连接数。毕竟连接池中的1000个连接在频繁利用时,涌现短韶光内多余10个空闲连接的概率实在太高。因此为了避免涌现上面的连接震荡,必须考虑加大这个参数,比如上面的场景如果将keepalive设置为100或者200,就可以非常有效的缓要冲乞降应答不屈均。
总结:keepalive 这个参数一定要小心设置,尤其对付QPS比较高的场景,推举先做一下估算,根据QPS和均匀相应韶光大体能打算出须要的长连接的数量。比如前面10000 QPS和100毫秒相应韶光就可以推算出须要的长连接数量大概是1000. 然后将keepalive设置为这个长连接数量的10%到30%。比较
3、综上,涌现大量TIME_WAIT的情形1)导致 nginx端涌现大量TIME_WAIT的情形有两种:
keepalive_requests设置比较小,高并发下超过此值后nginx会逼迫关闭和客户端保持的keepalive长连接;(主动关闭连接后导致nginx涌现TIME_WAIT)keepalive设置的比较小(空闲数太小),导致高并发下nginx会频繁涌现连接数震荡(超过该值会关闭连接),一直的关闭、开启和后端server保持的keepalive长连接;2)导致后端server端涌现大量TIME_WAIT的情形:nginx没有打开和后真个长连接,即:没有设置proxy_http_version 1.1;和proxy_set_header Connection “”;从而导致后端server每次关闭连接,高并发下就会涌现server端涌现大量TIME_WAIT
三、nginx配置https1、配置
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…
2、性能比较:通过https访问Nginx一样平常会比http访问慢30%(https办法访问紧张是耗Nginx做事器的cpu)通过下口试验验证:
nginx后端挂了5台java做事器,java做事器中有个大略的java程序,从redis缓存随机读取一个value值输出到前端;(挂的java做事器越多,对nginx压力越大)压测nginx,3000并发,一共要求30000次,返回结果都是200的情形下进行比拟;实验结果:A、做事器负载比拟:https访问,做事器cpu最高可以达到20%,而http的访问,做事器cpu基本在1%旁边;无论哪种访问,nginx做事器负载、内存都不高;B、nginx吞吐量比拟(qps):• https访问,30000次要求花了28s;(是http的3倍)• http访问,30000次要求花了9s;统计qps时,每次清空nginx日志,然后加压,实行完毕后利用如下命令查看qps:
# cat log.2.3000https | grep '/api/news/v1/info?newsId=' | awk '{print$3}'| uniq | wc -l37
注:不能持续加压,否则无限加大压力后每每是后端java做事涌现瓶颈,导致返回给nginx的相应变慢,从而使得nginx压力变小。
3、优化:Nginx默认利用DHE算法来产生密匙,该加密算法效率很低。可以通过如下命令,删掉了kEDH算法。ssl_ciphers ALL:!kEDH!ADH:RC4+RSA:+HIGH:+EXP;