nginx反向代理时如何保持长连接


Posted in Servers onMarch 31, 2021

·【场景描述】

HTTP1.1之后,HTTP协议支持持久连接,也就是长连接,优点在于在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。

如果我们使用了nginx去作为反向代理或者负载均衡,从客户端过来的长连接请求就会被转换成短连接发送给服务器端。

为了支持长连接,我们需要在nginx服务器上做一些配置。

·【要求】

使用nginx时,想要做到长连接,我们必须做到以下两点:

  • 从client到nginx是长连接
  • 从nginx到server是长连接

对于客户端而言,nginx其实扮演着server的角色,反之,之于server,nginx就是一个client。

·【保持和 Client 的长连接】

我们要想做到Client与Nginx之间保持长连接,需要:

  • Client发送过来的请求携带"keep-alive"header。
  • Nginx设置支持keep-alive

【HTTP配置】

默认情况下,nginx已经开启了对client连接的 keepalive 支持。对于特殊场景,可以调整相关参数。

http {

keepalive_timeout 120s;    #客户端链接超时时间。为0的时候禁用长连接。

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 之间生效)。

·【保持和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。

这个参数和之前http里面的 keepalive_timeout 不一样。

这个参数的含义是,连接池里面最大的空闲连接数量。

不理解?没关系,我们来举个例子:

场景:

有一个HTTP服务,作为upstream服务器接收请求,响应时间为100毫秒。

要求性能达到10000 QPS,我们需要在nginx与upstream服务器之间建立大概1000条HTTP请求。(1000/0.1s=10000) 

最优情况:

假设请求非常的均匀平稳,每一个请求都是100ms,请求结束会被马上放入连接池并置为idle(空闲)状态。

我们以0.1s为单位:

1. 我们现在keepalive的值设置为10,每0.1s钟有1000个连接

2. 第0.1s的时候,我们一共有1000个请求收到并释放

3. 第0.2s的时候,我们又来了1000个请求,在0.2s结束的时候释放   

请求和应答都比较均匀,0.1s释放的连接正好够用,不需要建立新连接,且连接池中没有idle状态的连接。   

第一种情况:

应答非常平稳,但是请求不平稳的时候

4. 第0.3s的时候,我们只有500个请求收到,有500个请求因为网络延迟等原因没有进来

这个时候,Nginx检测到连接池中有500个idle状态的连接,就直接关闭了(500-10)个连接

5. 第0.4s的时候,我们收到了1500个请求,但是现在池里面只有(500+10)个连接,所以Nginx不得不重新建立了(1500-510)个连接。

如果在第4步的时候,没有关闭那490个连接的话,只需要重新建立500个连接。   

第二种情况:

请求非常平稳,但是应答不平稳的时候

4. 第0.3s的时候,我们一共有1500个请求收到

但是池里面只有1000个连接,这个时候,Nginx又创建了500个连接,一共1500个连接

5. 第0.3s的时候,第0.3s的连接全部被释放,我们收到了500个请求

Nginx检测到池里面有1000个idle状态的连接,所以不得不释放了(1000-10)个连接 

造成连接数量反复震荡的一个推手,就是这个keepalive 这个最大空闲连接数。

上面的两种情况说的都是 keepalive 设置的不合理导致Nginx有多次释放与创建连接的过程,造成资源浪费。 

keepalive 这个参数设置一定要小心,尤其是对于 QPS 要求比较高或者网络环境不稳定的场景,一般根据 QPS 值和 平均响应时间能大致推算出需要的长连接数量。

然后将keepalive设置为长连接数量的10%到30%。 

【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.1。

HTTP1.0不支持keepalive特性,当没有使用HTTP1.1的时候,后端服务会返回101错误,然后断开连接。   

而 "Connection" header 可以选择被清理,这样即便是 Client 和 Nginx 之间是短连接,Nginx 和 upstream 之间也是可以开启长连接的。

【另外一种高级方式】

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中文官方文档: http://www.nginx.cn/doc/

测试参考文档: https://www.lijiaocn.com/问题/2019/05/08/nginx-ingress-keep-alive-not-work.html

keep-alive参考文档: https://wglee.org/2018/12/02/nginx-keepalive/

以上就是nginx反向代理时如何保持长连接的详细内容,更多关于nginx 保持长连接的资料请关注三水点靠木其它相关文章!

Servers 相关文章推荐
Apache站点配置SSL强制跳转443
Mar 09 Servers
Nginx反向代理及负载均衡如何实现(基于linux)
Mar 31 Servers
nginx location优先级的深入讲解
Mar 31 Servers
apache基于端口创建虚拟主机的示例
Apr 24 Servers
nginx安装以及配置的详细过程记录
Sep 15 Servers
Apache Pulsar结合Hudi构建Lakehouse方案分析
Mar 31 Servers
Docker 镜像介绍以及commit相关操作
Apr 13 Servers
Windows Server 2019 配置远程控制以及管理方法
Apr 28 Servers
Windows Server 2008 修改远程登录端口以及配置防火墙
Apr 28 Servers
Windows server 2012搭建FTP服务器
Apr 29 Servers
Tomcat安装使用及部署Web项目的3种方法汇总
Aug 14 Servers
Apache SkyWalking 监控 MySQL Server 实战解析
Sep 23 Servers
Nginx + consul + upsync 完成动态负载均衡的方法详解
Mar 31 #Servers
浅谈Nginx 中的两种限流方式
Mar 31 #Servers
查看nginx配置文件路径和资源文件路径的方法
Mar 31 #Servers
nginx里的rewrite跳转的实现
Mar 31 #Servers
nginx基于域名,端口,不同IP的虚拟主机设置的实现
Mar 31 #Servers
Nginx设置日志打印post请求参数的方法
Mar 31 #Servers
nginx 多个location转发任意请求或访问静态资源文件的实现
You might like
Thinkphp搜索时首页分页和搜索页保持条件分页的方法
2014/12/05 PHP
css动画效果之animation的常用样式
2021/03/09 HTML / CSS
JSON 学习之JSON in JavaScript详细使用说明
2010/02/23 Javascript
JavaScript 拾碎[三] 使用className属性
2010/10/16 Javascript
JS中showModalDialog 的使用解析
2013/04/17 Javascript
兼容主流浏览器的iframe自适应高度js脚本
2014/01/10 Javascript
jQuery定义插件的方法
2015/12/18 Javascript
vue2.0实现倒计时的插件(时间戳 刷新 跳转 都不影响)
2017/03/30 Javascript
利用原生js实现html5小游戏之打砖块(附源码)
2018/01/03 Javascript
Angular CLI 使用教程指南参考小结
2019/04/10 Javascript
使用 node.js 模仿 Apache 小部分功能
2019/07/07 Javascript
JS常见面试试题总结【去重、遍历、闭包、继承等】
2019/08/27 Javascript
layui table 多行删除(id获取)的方法
2019/09/12 Javascript
深入分析jQuery.one() 函数
2020/06/03 jQuery
在Linux上安装Python的Flask框架和创建第一个app实例的教程
2015/03/30 Python
Python编程中对文件和存储器的读写示例
2016/01/25 Python
python导入csv文件出现SyntaxError问题分析
2017/12/15 Python
Python列表推导式、字典推导式与集合推导式用法实例分析
2018/02/07 Python
Python装饰器原理与用法分析
2018/04/30 Python
Python自定义函数实现求两个数最大公约数、最小公倍数示例
2018/05/21 Python
浅谈pycharm出现卡顿的解决方法
2018/12/03 Python
Python 使用Numpy对矩阵进行转置的方法
2019/01/28 Python
pandas如何处理缺失值
2019/07/31 Python
解决python 上传图片限制格式问题
2019/10/30 Python
pytorch常见的Tensor类型详解
2020/01/15 Python
解决python运行启动报错问题
2020/06/01 Python
Bootstrap File Input文件上传组件
2020/12/01 HTML / CSS
Sephora丝芙兰泰国官方网站:国际知名化妆品购物
2017/11/15 全球购物
shell程序中如何注释
2012/01/28 面试题
教师实习自我鉴定
2013/12/18 职场文书
企业内部培训方案
2014/02/04 职场文书
2014年小学安全工作总结
2014/12/04 职场文书
学前班语言教学计划
2015/01/20 职场文书
解析Java中的static关键字
2021/06/14 Java/Android
Javascript使用integrity属性进行安全验证
2021/11/07 Javascript
JavaScript文档对象模型DOM
2021/11/20 Javascript