Nginx中保持长链接的配置 - 运维记录

 

在Nginx中,对于http1.0与http1.1是支持长链接的。http请求是基于TCP协议之上的,那么当客户端在发起请求前,须要先与服务端创建TCP链接,而每一次的TCP链接是须要三次握手来肯定的,若是客户端与服务端之间网络差一点,这三次交互消费的时间会比较多,并且三次交互也会带来网络流量。固然,当链接断开后,也会有四次的交互,固然对用户体验来讲就不重要了。而http请求是请求应答式的,若是咱们能知道每一个请求头与响应体的长度,那么咱们是能够在一个链接上面执行多个请求的,这就是所谓的长链接,但前提条件是咱们先得肯定请求头与响应体的长度。对于请求来讲,若是当前请求须要有body,如POST请求,那么nginx就须要客户端在请求头中指定content-length来代表body的大小,不然返回400错误。也就是说,请求体的长度是肯定的,那么响应体的长度呢?先来看看http协议中关于响应body长度的肯定:nginx

1)对于http1.0协议来讲,若是响应头中有content-length头,则以content-length的长度就能够知道body的长度了,客户端在接收body时,就能够依照这个长度来接收数据,接收完后,就表示这个请求完成了。而若是没有content-length头,则客户端会一直接收数据,直到服务端主动断开链接,才表示body接收完了。
2)对于http1.1协议来讲,若是响应头中的Transfer-encoding为chunked传输,则表示body是流式输出,body会被分红多个块,每块的开始会标识出当前块的长度,此时,body不须要经过长度来指定。若是是非chunked传输,并且有content-length,则按照content-length来接收数据。不然,若是是非chunked,而且没有content-length,则客户端接收数据,直到服务端主动断开链接。ajax

从上面,咱们能够看到,除了http1.0不带content-length以及http1.1非chunked不带content-length外,body的长度是可知的。此时,当服务端在输出完body以后,会能够考虑使用长链接。可否使用长链接,也是有条件限制的。若是客户端的请求头中的connection为close,则表示客户端须要关掉长链接,若是为keep-alive,则客户端须要打开长链接,若是客户端的请求中没有connection这个头,那么根据协议,若是是http1.0,则默认为close,若是是http1.1,则默认为keep-alive。若是结果为keepalive,那么,nginx在输出完响应体后,会设置当前链接的keepalive属性,而后等待客户端下一次请求。固然,nginx不可能一直等待下去,若是客户端一直不发数据过来,岂不是一直占用这个链接?因此当nginx设置了keepalive等待下一次的请求时,同时也会设置一个最大等待时间,这个时间是经过选项keepalive_timeout来配置的,若是配置为0,则表示关掉keepalive,此时,http版本不管是1.1仍是1.0,客户端的connection无论是close仍是keepalive,都会强制为close。后端

若是服务端最后的决定是keepalive打开,那么在响应的http头里面,也会包含有connection头域,其值是”Keep-Alive”,不然就是”Close”若是connection值为close,那么在nginx响应完数据后,会主动关掉链接。因此,对于请求量比较大的nginx来讲,关掉keepalive最后会产生比较多的time-wait状态的socket。通常来讲,当客户端的一次访问,须要屡次访问同一个server时,打开keepalive的优点很是大,好比图片服务器,一般一个网页会包含不少个图片,打开keepalive会大量减小time-wait数量。缓存

先说说服务为何使用HTTPs长链接技术?好比下面几个缘由:
1)对响应时间要求较高;
2)服务走的是公网,客户端与服务端的TCP创建的三次握手和断开的四次挥手都须要40ms左右(真实数据包计算出来的),共须要80ms左右;
3)每一个接入方使用的IP就若干个,须要创建的请求链接有限。
4)使用长链接技术,能够大幅减小TCP频繁握手的次数,极大提升响应时间;同时,即便使用长链接技术,也不须要消耗不少的系统资源用来缓存sockets会话信息。bash

          Nginx反向代理时保持长链接          
服务器

1)场景描述
HTTP1.1以后,HTTP协议支持持久链接,也就是长链接,优势在于在一个TCP链接上能够传送多个HTTP请求和响应,减小了创建和关闭链接的消耗和延迟。若是咱们使用了nginx去做为反向代理或者负载均衡,从客户端过来的长链接请求就会被转换成短链接发送给服务器端。为了支持长链接,咱们须要在nginx服务器上作一些配置。网络

2)要求
当使用 Nginx做为反向代理时,为了支持长链接,须要作到两点::
从Client到Nginx的链接是长链接
从Nginx到Server的链接是长链接负载均衡

从 HTTP 协议的角度看,Nginx在这个过程当中,对于客户端它扮演着 HTTP 服务器端的角色。而对于真正的服务器端(在 Nginx的术语中称为 upstream)Nginx又扮演着 HTTP 客户端的角色。也就是说对于Client客户端而言,Nginx其实扮演着Server的角色,反之,之于Server,Nginx就是一个Client。socket

3)保持和 Client 的长链接
要想作到Client与Nginx之间保持长链接,须要:
-  Client发送过来的HTTP请求要求携带"keep-alive"header。
-  Nginx设置支持keepalive性能

[Nginx中HTTP配置]
默认状况下,Nginx已经自动开启了对 Client 链接的 keepalive 支持。通常场景能够直接使用,可是对于一些比较特殊的场景,仍是有必要调整个别参数。须要修改 Nginx的配置文件 (在 Nginx安装目录下的conf/nginx.conf):

http {
  keepalive_timeout 120s;        #客户端连接超时时间。为0的时候禁用长链接。即长链接的timeout
  keepalive_requests 10000;      #在一个长链接上能够服务的最大请求数目。当达到最大请求数目且全部已有请求结束后,链接被关闭。默认值为100。即每一个链接的最大请求数
}

大多数状况下,keepalive_requests = 100也够用,可是对于 QPS 较高的场景,很是有必要加大这个参数,以免出现大量链接被生成再抛弃的状况,减小TIME_WAIT。QPS=10000 时,客户端每秒发送 10000 个请求 (一般创建有多个长链接),每一个链接只能最多跑 100 次请求,意味着平均每秒钟就会有 100 个长链接所以被 nginx 关闭。一样意味着为了保持 QPS,客户端不得不每秒中从新新建 100 个链接。所以,若是用netstat命令看客户端机器,就会发现有大量的TIME_WAIT的socket链接 (即便此时keep alive已经在 Client 和 NGINX 之间生效)。

keepalive_timeout 指令,语法以下:

Syntax: keepalive_timeout timeout [header_timeout];
Default:    keepalive_timeout 75s;
Context:    http, server, location

第一个参数设置 keepalive 客户端链接在服务器端保持开启的超时值。值为 0 会禁用 keepalive客户端链接。
可选的第二个参数在响应的 header 域中设置一个值 "Keep-Alive: timeout=time"。这两个参数能够不同。
须要注意:默认 75s 通常状况下也够用,对于一些请求比较大的内部服务器通信的场景,适当加大为 120s 或者 300s。第二个参数一般能够不用设置。

keepalive_requests 指令
keepalive_requests 指令用于设置一个keepalive链接上能够服务的请求的最大数量。当最大请求数量达到时,链接被关闭。默认是100。

这个参数的真实含义,是指一个keepalive创建以后,Nginx就会为这个链接设置一个计数器,记录这个keepalive的长链接上已经接收并处理的客户端请求的数量。若是达到这个参数设置的最大值时,则 Nginx会强行关闭这个长链接,逼迫客户端不得不从新创建新的长链接。

这个参数每每被大多数人忽略,由于大多数状况下当 QPS(每秒请求数) 不是很高时,默认值 100 凑合够用。可是,对于一些 QPS 比较高(好比超过 10000QPS,甚至达到 30000,50000 甚至更高) 的场景,默认的 100 就显得过低。

简单计算一下,QPS=10000 时,客户端每秒发送 10000 个请求 (一般创建有多个长链接),每一个链接只能最多跑 100 次请求,意味着平均每秒钟就会有 100 个长链接所以被 Nginx关闭。一样意味着为了保持 QPS,客户端不得不每秒中从新新建 100 个链接。所以,若是用netstat命令看客户端机器,就会发现有大量的TIME_WAIT的socket链接 (即便此时keep alive已经在 Client 和 Nginx之间生效)。所以对于 QPS 较高的场景,很是有必要加大keepalive_requests这个参数,以免出现大量链接被生成再抛弃的状况,减小TIME_WAIT。

4)保持和Server的长链接
想让Nginx和Server之间维持长链接,最朴素最典型的设置以下:

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_http_version 1.1;                   #设置http版本为1.1
  proxy_set_header Connection "";           #设置Connection为长链接(默认为no)
  }
}
}

[upstream配置]
在upstream配置中,有一个参数特别重要特别当心,就是keepalive。大多数未仔细研读过 Nginx的同窗一般都会误解这个参数,有些人理解为这里的 keepalive 是设置是否打开长链接,觉得应该设置为 ON/OFF。有些人会被前面的 keepalive_timeout 误导,觉得这里也是设置 keepalive 的 timeout。可是实际上这个keepalive参数的含义很是的奇特:

Syntax:    keepalive connections;
Default:   —
Context:   upstream

Activates the cache for connections to upstream servers.
激活到 upstream 服务器的链接缓存。

The connections parameter sets the maximum number of idle keepalive connections to upstream servers that are preserved in the cache of each worker process. When this number is exceeded, the least recently used connections are closed.
connections 参数设置每一个 worker 进程在缓冲中保持的到 upstream 服务器的空闲 keepalive 链接的最大数量. 当这个数量被突破时,最近使用最少的链接将被关闭。

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. The connections parameter should be set to a number small enough to let upstream servers process new incoming connections as well.
keepalive 指令不会限制一个 nginx worker 进程到 upstream 服务器链接的总数量。connections 参数应该设置为一个足够小的数字来让 upstream 服务器来处理新进来的链接。

在这里能够看到,前面的几种猜想能够确认是错误的了:
-  keepalive 不是 ON/OFF 之类的开关
-  keepalive 不是 TIMEOUT,不是用来设置超时值

不少人读到这里后,会产生另一个误解:认为这个参数是设置到upstream服务器的长链接的数量,分歧在因而最大链接数仍是最小链接数,不得不说这也是一个挺逗的分歧……

这里请特别注意Nginx文档里的这句话,相当重要:

The connections parameter sets the maximum number of idle keepalive connections to upstream servers
connections 参数设置到 upstream 服务器的空闲 keepalive 链接的最大数量

请仔细体会这个 idle 的概念,何为idle。大多数人之因此误解为是到upstream服务器的最大长链接数,通常都是由于看到了文档中的这句话,而漏看了这个idle一词。

而后继续看Nginx文档后面另一句话:

When this number is exceeded, the least recently used connections are closed.
当这个数量被突破时,最近使用最少的链接将被关闭。

这句话更是大大强化了前面关于keepalive设置的是最大长链接数的误解:若是链接数超过keepalive的限制,就关闭链接。这不是赤裸裸的最大链接数么?可是 Nginx的文档立马给出了指示,否认了最大链接数的可能:

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 服务器链接的总数量。

keepalive 参数的理解
要真正理解 keepalive 参数的含义,请回到Nginx文档中的这句:

The connections parameter sets the maximum number of idle keepalive connections to upstream servers
connections 参数设置到 upstream 服务器的空闲 keepalive 链接的最大数量

请注意空闲 keepalive 链接的最大数量中空闲这个关键字keepalive这个参数和以前http里面的 keepalive_timeout 不同。这个参数的含义是,链接池里面最大的空闲链接数量。不理解?不要紧,下面来举个例子:

先假设一个场景: 有一个 HTTP 服务,做为upstream服务器接收请求,响应时间为 100 毫秒。若是要达到 10000 QPS 的性能,就须要在 nginx 和upstream服务器之间创建大约 1000 条 HTTP 链接。nginx 为此创建链接池,而后请求过来时为每一个请求分配一个链接,请求结束时回收链接放入链接池中,链接的状态也就更改成 idle。

再假设这个upstream服务器的keepalive参数设置比较小,好比常见的 10.

假设请求和响应是均匀而平稳的,那么这 1000 条链接应该都是一放回链接池就当即被后续请求申请使用,线程池中的 idle 线程会很是的少,趋进于零。咱们以 10 毫秒为一个单位,来看链接的状况 (注意场景是 1000 个线程 + 100 毫秒响应时间,每秒有 10000 个请求完成):
1)每 10 毫秒有 100 个新请求,须要 100 个链接
2)每 10 毫秒有 100 个请求结束,能够释放 100 个链接
3)若是请求和应答都均匀,则 10 毫秒内释放的链接恰好够用,不须要新建链接,链接池空闲链接为零

而后再回到现实世界,请求一般不是足够的均匀和平稳,为了简化问题,咱们假设应答始终都是平稳的,只是请求不平稳,第一个 10 毫秒只有 50, 第二个 10 毫秒有 150:
1)下一个 10 毫秒,有 100 个链接结束请求回收链接到链接池,可是假设此时请求不均匀 10 毫秒内没有预计的 100 个请求进来,而是只有 50 个请求。注意此时链接池回收了 100 个链接又分配出去 50 个链接,所以链接池内有 50 个空闲链接。
2)而后注意看keepalive=10的设置,这意味着链接池中最多允许保留有 10 个空闲链接。所以 nginx 不得不将这 50 个空闲链接中的 40 个关闭,只留下 10 个。
3)再下一个 10 个毫秒,有 150 个请求进来,有 100 个请求结束任务释放链接。150 - 100 = 50, 空缺了 50 个链接,减掉前面链接池保留的 10 个空闲链接,nginx 不得不新建 40 个新链接来知足要求。

能够看到,在短短的 20 毫秒内,仅仅由于请求不够均匀,就致使 nginx 在前 10 毫秒判断空闲链接过多关闭了 40 个链接,然后 10 毫秒又不得不新建 40 个链接来弥补链接的不足。

再来一次相似的场景,假设请求是均匀的,而应答再也不均匀,前 10 毫秒只有 50 个请求结束,后 10 毫秒有 150 个:
1)前 10 毫秒,进来 100 个请求,结束 50 个请求,致使链接不够用,nginx 为此新建 50 个链接
2)后 10 毫秒,进来 100 个请求,结束 150 个请求,致使空闲链接过多,ngixn 为此关闭了150-100-10=40个空闲链接

特别提醒:第二个应答不均匀的场景其实是对应第一个请求不均匀的场景:正是由于请求不均匀,因此致使 100 毫秒以后这些请求的应答必然不均匀。

现实世界中的请求每每和理想状态有巨大差别,请求不均匀,服务器处理请求的时间也不平稳,这理论上的大概 1000 个链接在反复的回收和再分配的过程当中,必然出现两种很是矛盾场景在短期内反复:
1)链接不够用,形成新建链接。
2)链接空闲,形成关闭链接。从而使得总链接数出现反复震荡,不断的建立新链接和关闭链接,使得长链接的效果被大大削弱。

形成链接数量反复震荡的一个推手,就是这个keepalive 这个最大空闲链接数。毕竟链接池中的 1000 个链接在频繁利用时,出现短期内多余 10 个空闲链接的几率实在过高。上面状况说的都是keepalive设置不合理致使Nginx有屡次释放与建立链接的过程,形成资源浪费。所以为了不出现上面的链接震荡,必须考虑加大这个参数,好比上面的场景若是将keepalive设置为100或者200, 就能够很是有效的缓冲请求和应答不均匀。

总结:keepalive 这个参数必定要当心设置,尤为对于 QPS 比较高的场景,推荐先作一下估算,根据 QPS 和平均响应时间大致能计算出须要的长链接的数量。好比前面 10000 QPS 和 100 毫秒响应时间就能够推算出须要的长链接数量大概是 1000,而后将keepalive设置为这个长链接数量的 10% 到 30%。比较懒的同窗,能够直接设置为 keepalive=1000 之类的,通常都 OK 的了。

[location配置]

http {
  server {
  location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;              #设置http版本为1.1
    proxy_set_header Connection "";      #设置Connection为长链接(默认为no)
  }
}
}

HTTP 协议中对长链接的支持是从 1.1 版本以后才有的,所以最好经过 proxy_http_version 指令设置为 1.1HTTP1.0不支持keepalive特性,当没有使用HTTP1.1的时候,后端服务会返回101错误,而后断开链接。而 "Connection" header 能够选择被清理,这样即使是 Client 和 Nginx 之间是短链接,Nginx 和 upstream 之间也是能够开启长链接的。这种状况下必须清理来自 Client 请求中的 “Connection” header。

[另一种高级方式]

http {
  map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}   

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_connect_timeout 15;         #与upstream server的链接超时时间(没有单位,最大不能够超过75s)
  proxy_read_timeout 60s;           #nginx会等待多长时间来得到请求的响应
  proxy_send_timeout 12s;           #发送请求给upstream服务器的超时时间   
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection $connection_upgrade;
  }
}
}

http里面的map的做用是:
-  让转发到代理服务器的 "Connection" 头字段的值,取决于客户端请求头的 "Upgrade" 字段值。
-  若是 $http_upgrade没有匹配,那 "Connection" 头字段的值会是upgrade
-  若是 $http_upgrade为空字符串的话,那 "Connection" 头字段的值会是 close

[补充]
Nginx支持WebSocket。对于Nginx将升级请求从客户端发送到后台服务器,必须明确设置Upgrade和Connection标题。这也算是上面状况所很是用的场景。HTTP的Upgrade协议头机制用于将链接从HTTP链接升级到WebSocket链接,Upgrade机制使用了Upgrade协议头和Connection协议头。为了让Nginx能够将来自客户端的Upgrade请求发送到后端服务器Upgrade和Connection的头信息必须被显式的设置

[注意]
在Nginx的配置文件中,若是当前模块中没有proxy_set_header的设置,则会从上级别继承配置。继承顺序为:http, server, location。若是在下一层使用proxy_set_header修改了header的值,则全部的header值均可能会发生变化以前继承的全部配置将会被丢弃。因此,尽可能在同一个地方进行proxy_set_header,不然可能会有别的问题。

           Nginx配置长链接 (ajax60秒请求超时)           
在使用ajax作轮训的时候前台发出的ajax请求老是会在60秒以后返回405超时响应,通过排除ajax超时响应设置后猜想nginx对请求进行了超时响应处理,猜想是nginx配置有问题;Nginx从 1.1.4 开始,实现了对后端机器的长链接支持,这意味着 Nginx 与后端机器的通讯效率更高,后端机器的负担更低。

server {
   listen 80;
   server_name www.kevin.com;
location / {
    proxy_http_version 1.1; 
    proxy_read_timeout 600s;         #新增配置1
    proxy_send_timeout 120s;         #新增配置2
    proxy_pass http://127.0.0.1:8086;
    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_read_timeout: 链接成功后_等候后端服务器响应时间_其实已经进入后端的排队之中等候处理(也能够说是后端服务器处理请求的时间)proxy_send_timeout: 后端服务器数据回传时间, 就是在规定时间以内后端服务器必须传完全部的数据。

相关文章
相关标签/搜索