一、关于nginx upstream

在nginx的模块中,分为3种类型,分别是handler,filter和upstream,其中upstream可以看做一种特殊的handler,它主要用来实现和后端另外的服务器进行通信,由于在nginx中全部都是使用非阻塞,并且是一个流式的处理,所以upstream的实现很复杂。

upstream顾名思义,真正产生内容的地方在”上游”而不是nginx,也就是说nginx是位于client和后端的upstream之间的桥梁,在这种情况下,一个upstream需要做的事情主要有2个:

第一个是当client发送http请求过来之后,需要创建一个到后端upstream的请求。

第二个是当后端发送数据过来之后,需要将后端upstream的数据再次发送给client。 

当使用nginx作为反向代理时,为了支持长连接,需要做到两点:

从client到nginx的连接是长连接

从nginx到server的连接是长连接

从HTTP协议的角度看,nginx在这个过程中,对于客户端它扮演着HTTP服务器端的角色。

而对于真正的服务器端(在nginx的术语中称为upstream)nginx又扮演着HTTP客户端的角色。

nginx 通常情况下都是用来当作一个反向代理,通常一个请求都需要经过client -> nginx -> backend_server这么几成关系。

通常情况下client -> nginx使用的 HTTP 1.1 或者 2.0 的协议,keep-alive 复用了 TCP 的连接,减少了 TCP 频发创建和销毁带来的性能损失。

但是默认情况下,nginx -> backend_server是 HTTP 1.0 的协议,并没有复用 TCP 的连接。

二、nginx upstream连接后端的两种方式:短连接和长连接

Keep-Alive即俗称的长连接,使客户端到服务端建立的连接持续有效,当对服务器发起后续请求时,Keep-Alive功能可以复用先前使用过的连接,减少了重新建立连接所需的开销。通常HTTP协议采用的是请求应答模式,客户端到服务端的一次请求完成了,它们之间的连接也随之关闭。在某些应用场景下,比如后端RESTful服务,就需要保持住连接,这时就可以启用HTTP Keep-Alive。

nginx 1.1.14版本以前与后端upstream服务器建立的都是短链接,即通过HTTP/1.0向后端发起连接,并把请求的”Connection” header设为”close”。这样nginx往upstream后端发请求时,也会消耗很多的时间与带宽,如果让nginx与upstream后端建立起长链接,从nginx发起的请求就可以挑选一个合适的长链接发往upstream后端服务器,这样即可以节省带宽,也可以提高响应速度。

其中keepalive指定最大保持的长连接数为1024.,同时要求HTTP协议版本指定为HTTP 1.1,以及清除掉HTTP头部Connection。

nginx upstream keepalive长连接的实现原理  

先每个进程需要一个connection pool,里面都是长连接,多进程之间是不需要共享这个连接池的。 

一旦与后端服务器建立连接,则在当前请求连接结束之后不会立即关闭连接,而是把用完的连接保存在一个keepalive connection pool里面,以后每次需要建立向后连接的时候,只需要从这个连接池里面找,如果找到合适的连接的话,就可以直接来用这个连接,不需要重新创建socket或者发起connect(),这样既省下建立连接时在握手的时间消耗,又可以避免TCP连接的slow start。如果在keepalive连接池找不到合适的连接,那就按照原来的步骤重新建立连接。

如果你的连接池的数控制在128,总共线程池内的线程数是128 * nginx worker ,但因为你要应对更多的并发请求,所以临时又加了很多的连接,但这临时的连接是短连接和长连接要看你的nginx版本。如果是长连接,那它如何被收回,两地保证,一点是它会主动去释放,另一点是keepalive timeout的时间。

说明:

1. 默认情况下 Nginx 访问后端都是用的短连接(HTTP1.0),一个请求来了,nginx 新开一个端口和后端建立连接,请求结束连接回收。如果像上面的配置一样设置了长连接,nginx 会接受客户端的请求,处理完成之后 Nginx 会「继续保持和后端的长连接」,如果并发请求超过了 keepalive 指定的最大连接数,nginx 会启动新的连接 来转发请求,新连接在请求完毕后关闭,而且新建立的连接是长连接,这可能会造成额外的问题,最后再说。

2. keepalive 指定的 数值 是nginx 每个 worker  连接后端的最大长连接数,而不是整个 nginx 的。 而且这里的后端指的是「所有的后端」,而不是每一个后端(已验证)。

3. HttpUpstreamModule 模块里面还有一共指令:least_conn,解释如下:

Specifies that a group should use a load balancing method where a request is passed to the server with the least number of active connections, taking into account weights of servers. If there are several such servers, they are tried in turn using a weighted round-robin balancing method.

翻译:指定服务器组的负载均衡方法,根据其权重值,将请求发送到「活跃连接数最少」的那台服务器。 如果这样的服务器有多台,那就采取有权重的轮转法进行尝试。

4. 一个现象,当 keepalive 设置小的时候,比如1,那么并发请求上去之后 nginx 会出现大量的 TIME_WAIT,而如果把 keepalive 关掉(proxy_http_version 1.1 和 proxy_set_header Connection “” 指令也去掉),那么 TIME_WAIT 就会出现在后端服务器了,后端用的是 tornado,相信 jetty 和 tomcat 也是一样。

nginx 和后端的长连接不够用时 nginx 会新建连接来处理新的请求,而我们的配置已经配置死了 HTTP1.1,建立连接后,后端认为是「长连接」而不会主动关闭连接(一般有个空闲超时),关闭连接由 nginx 来做了,所以 nginx 会出现大量的 TIME_WAIT。

而默认情况下,nginx 用 HTTP1.0 请求后端,后端处理完成后就主动关闭连接,所以 TIME_WAIT 在后端。

那么现在有新的问题:如果开启了长连接,而长连接又大量不够用,此时 Nginx 存在的 TIME_WAIT 可能会大量占用端口,导致端口用尽,如果用尽,后果很严重。

在高并发环境下keepalive参数配置不当容易产生大量 TIME_WAIT,导致端口耗尽,服务异常。

keepalive值应该大于等于 upstream 中 server 的数量,建议是 server 数的 2 倍。

keepalive用于设置保留在每个 nginx worker 缓存中『已经与上游服务器建立的空闲连接』的最大连接数,如果超过这个值,最近最少使用的连接将被关闭。

nginx 官方建议这个值应该尽可能小,以便让上游服务器也能处理新进入的连接。但是『尽可能小』有些太模糊,如果配置不当会降低性能以及产生其他问题。

在大量短链接(或者大量分散用户)的请求场景下,同时 upstream 为轮询模式时,因为keepalive值小于 upstream server 数,upstream 新的空闲连接会从缓存中挤出老的空闲链接,导致keepalive在某种意义上失效。

三、参考

关于 client的长连接,server的长连接

https://skyao.gitbooks.io/learning-nginx/content/documentation/keep_alive.html

ngx_http_upstream_module

http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream

https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive

http://mdounin.ru/hg/ngx_http_upstream_keepalive/rev/489c5d4318ff

关于nginx upstream长链接的介绍及具体的实现方式

 http://bollaxu.iteye.com/blog/900424

使用nginx keepalive,网站TPS性能提升对比

http://sohulinux.blog.sohu.com/180744817.html

记一次压测引起的nginx负载均衡性能调优

http://xiaorui.cc/archives/3495

Tuning NGINX for Performance

https://www.nginx.com/blog/tuning-nginx

Why does nginx proxy_pass close my connection?

https://stackoverflow.com/questions/46771389/why-does-nginx-proxy-pass-close-my-connection

nginx keepalive connections config example to upstream servers

https://bl.ocks.org/magnetikonline/4a2e68b2ce94bd0c945e

Enable Keepalive connections in Nginx Upstream proxy configurations

https://ma.ttias.be/enable-keepalive-connections-in-nginx-upstream-proxy-configurations

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐