Nginx长连接学习之二


背景

距离最开始学习Nginx的长连接已经一年半;
距离最开始学习Linux的TCP内核参数也已经过去了一年. 最近产品再次出现了TCP链接相关的问题.
因为一开始不知道部署模式已经变更, 我先排除了内存参数的问题. 结果很打脸, 还是内核参数问题导致的, 但是可能比较隐晦.
因为Nginx服务器使用的CentOS9_Stream 并且进行了内核升级. 这里想还是继续总结一下, 因为我已经忘记了一年半以前学习的内容.
参数都已经还给了互联网.

问题场景描述

本次压测场景出现了 CPU忽高忽低的问题.
最开始不清楚部署已经增加了nginx, 一直以为是直连springboot应用
所以一开始一直怀疑是 类似于有进程执行了强制safepoint导致的停顿.
所以这个教训告诉大家, 一定要详细了解系统的部署拓扑图, 没有拓扑图前不要进行任何推测. 增加了nginx的error log等信息后发现了大量的
cannot assign port 等的提示, 基本上实锤了与2023年的问题完全相同. TCP四元组端口号不足导致的问题.

基础知识说明

第一: tcp内核参数
tcp内核参数其实是一个很重要的点.
ulimit里面的nofile nproc等参数 其实属于安全部分的设置, 他的数值受限制于内核参数的上限.
除此之外还有类似于tcp内核参数, 文件数, 内存数相关的内核参数. 第二: nginx
nginx 可以作为web服务器, 也可以作为反向代理服务器.
作为web服务器时比较单纯, 只跟客户端沟通就可以了.
但是作为proxy代理服务器时就比较复杂:
一方面要作为 链接 客户端的 web服务器进行交互,
另一方面要作为 客户端与backend的应用服务器进行交互.
所以代理服务器时 他不仅仅是服务器, 同时还是客户端. 第三: linux内核
Linux内核是Linux操作系统的基础. 很多参数其实会作用于Linux内核上面
但是也有很多是无法通过参数进行修改的. 比如今天想讨论的 time_wait持续时间的问题.
如果无法通过参数进行修改, 那么可能需要重新编译内核. 但是如果没有足够的技术储备,不建议如此使用.
会导致运维起来难度增加, 并且存在后续的升级和维护困难.
这一点参照了 解bug之路 公众号里面的Linux源码学习相关

问题现象

通过如下命令在linux服务器上面查询

netstat -ano |grep ^tcp |awk '{print $6}'|sort |uniq -c|sort -k1hr

发现有较多的 time_wait 相关的信息
114229 TIME _WAIT 因为后面有四到五台应用服务器 平均米格应用服务器已经快3万个 time wait的链接对应了.
所以nginx 会出现 cannot assign port的问题.

解决问题的方式-修改内核参数

1. 修改linux内核参数
这里很早之前总结过:
最新核的参数其实是:
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.ip_local_port_range = 2048 65500
net.ipv4.ip_local_reserved_ports = 5200,6379,7005,8001-8100 需要注意 tcp_max_tw_buckets 是针对全局 整个系统的.
一般设置到 5000 时 新增的 time_wait的 端口就会将旧的端口冲掉.
有一定很小的概率出现 四元组冲突. 需要注意. 另外注意 ip_local_port_range 是针对每个四元组来的, 不同的IP会选择 整个range进行port分配
所以建议增加一个 reserved的参数, 保证重启服务器时不会出现因为端口号占用 导致启动失败. 需要注意 这三个参数的修改并非解决问题, 而是避开了问题, 将能够支撑的QPS/TPS进行了增加.
彻底解决应该不能依靠这类参数修改类解决

解决问题的方式-重新编译内核

网上很多资料都说 net.ipv4.tcp_fin_timeout 等内核参数可以修改 time_wait的时间
但是更多大佬都说了 是内核编译时就已经决定的一个参数
https://zhuanlan.zhihu.com/p/286537295
这个文章里面的就说的很清楚(需要去读原文,才能看懂下面这一段)
/* 这边TCP_TIMEWAIT_LEN 60 * HZ */
inet_twsk_schedule(tw, &tcp_death_row, TCP_TIMEWAIT_LEN,
TCP_TIMEWAIT_LEN); 他只受到 TCP_TIMEWAIT_LEN 参数的影响, 但是修改的话 需要重新编译内核.
并且作者后面也说了, 在time_wait 的每个slot大于100个时有可能会加大每个slot内的time wait的处理时间.
导致一个time wait的链接最大处理时间超过 112秒((7.5s+7.5s)*7+7.5s)). 并且他也发现某些内核, 会将等待事件从 60/8 的 7.5秒修改为 1秒,
这样最大的等待事件会变成 68秒. (8.5s+7+7.5s) 需要说明 这种处理方式跟内核参数修改是类似的, 也是为了缓解问题, 并非最终解决问题. 文章里面也发现, 不同内核下面网络栈的表现是不一样的, 可能某些非stable版本的系统, 会尝试进行的调整time_wait等变量
然后使用该内核的用户相当于免费的小白鼠进行测试. 如果出现了异常, 可能就是内核版本的修改不太适应本地应用系统额特性 世界就是一个草台班子, 任何修改都可能触发意想不到的问题. 所以先改代码的都是勇士, 都是需要被尊重的.

插播-长连接的优势

1)对响应时间要求较高;
2)服务走的是公网,客户端与服务端的TCP建立的三次握手和断开的四次挥手都需要40ms左右(真实数据包计算出来的),共需要80ms左右;
3)每个接入方使用的IP就若干个,需要建立的请求连接有限。
4)使用长连接技术,可以大幅减少TCP频繁握手的次数,极大提高响应时间;
同时,即使使用长连接技术,也不需要消耗很多的系统资源用来缓存sockets会话信息。 来源: https://www.cnblogs.com/kevingrace/p/9364404.html

解决问题的方式-长连接

最后一种可能是较好的解决问题的方式是 长连接.
知识背景里面也提到了.
Nginx 主要是有 服务器端 和 客户端的双重角色.
所以解决问题也需要两者都是进行考虑. 但是跟之前解决问题的思路一样. 所有的问题都是环环相扣的
配置nginx只是问题解决的一部分, 并不能够将所有的问题全部解决.
可能需要客户端浏览器以及后端应用服务器同步进行修改才可以. 主要的参数有:
keepalive
keepalive_timeout
keepalive_requests
proxy_timeout
等参数. 这里的配置其实比较繁琐, 需要仔细理解.

解决问题的方式-长连接-nginx作为服务端

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

解决问题的方式-长连接-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; #这个很重要!
keepalive_requests 10000;
} 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)
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 600s; #新增配置1
proxy_send_timeout 120s; #新增配置2
}
}
}

注意事项

需要注意, 如果采用websocket的话. 默认应该是长连接.
但是需要使用单点的站点进行升级处理. Nginx支持WebSocket。
对于Nginx将升级请求从客户端发送到后台服务器,必须明确设置Upgrade和Connection标题。
这也算是上面情况所非常用的场景。
HTTP的Upgrade协议头机制用于将连接从HTTP连接升级到WebSocket连接,Upgrade机制使用了Upgrade协议头和Connection协议头。
为了让Nginx可以将来自客户端的Upgrade请求发送到后端服务器,Upgrade和Connection的头信息必须被显式的设置。

延伸理解之一

Chrome浏览器以及Springboot的服务器端其实也有类似的设置.

浏览器对并发请求的数目限制是针对域名的,即针对同一域名(包括二级域名)在同一时间支持的并发请求数量的限制。
如果请求数目超出限制,则会阻塞。因此,网站中对一些静态资源,使用不同的一级域名,可以提升浏览器并行请求的数目,加速界面资源的获取速度。 HTTP/1.1中,单个TCP连接,在同一时间只能处理一个http请求,虽然存在Pipelining技术支持多个请求同时发送
但由于实践中存在很多问题无法解决,所以浏览器默认是关闭,所以可以认为是不支持同时多个请求。
HTTP2 提供了多路传输功能,多个http请求,可以同时在同一个TCP连接中进行传输。 Chrome浏览器在http1.1协议时可以对同一个站点使用六个tcp链接进行访问.
页面资源请求时,浏览器会同时和服务器建立多个TCP连接,在同一个TCP连接上顺序处理多个HTTP请求。
所以浏览器的并发性就体现在可以建立多个TCP连接,来支持多个http同时请求。 Chrome浏览器最多允许对同一个域名Host建立6个TCP连接,不同的浏览器有所区别。 如果都是HTTPS的连接,并且在同一域名下,浏览器会先和服务器协商使用HTTP2的Multiplexing功能进行多路传输,
不过未必所有的挂在这个域名下的资源都会使用同一个TCP连接。如果用不了HTTPS或者HTTP2(HTTP2是在HTTPS上实现的),
那么浏览器会就在同一个host建立多个TCP连接,每一个TCP连接进行顺序请求资源。 来源: https://cloud.tencent.com/developer/article/1518678

延伸理解之二

Springboot 开发的应用默认内嵌的tomcat
tomcat 里面有thread的线程池, 数据库连接池, 以及 http的链接池信息
需要注意 springboot 默认的 max-keep-alive-requests 就是100 如果并发量很大, 可以适当调高一下这个数值.
建议跟nginx 里面的 upstream 里面的 keepalive 的参数值尽量保持一致, 可以尽可能的提高并发能力. (此观点待压测证明) 另外需要说明 threads.max基本上是 http-nio-port 的线程数量限制, 可以理解为是一个比较核心的工作线程数量限制.
其他的内部的线程池一般是通过 max 数值单独指定.
需要注意, 线程也会消耗内存, 默认的linux上面的 线程的栈大小是1MB. 但是一般请款下也是延迟分配的.
创建线程后可能会分配 200K左右的内存, 并不会完整使用1MB左右. 也有很多配置信息:
server:
tomcat:
# 当所有可能的请求处理线程都在使用中时,传入连接请求的最大队列长度
accept-count: 1000
# 服务器在任何给定时间接受和处理的最大连接数。一旦达到限制,操作系统仍然可以接受基于“acceptCount”属性的连接。
max-connections: 20000
threads:
# 工作线程的最小数量,初始化时创建的线程数
min-spare: 10
# 工作线程的最大数量 io密集型建议10倍的cpu数,cpu密集型建议cpu数+1,绝大部分应用都是io密集型
max: 500
# 连接器在接受连接后等待显示请求 URI 行的时间。
connection-timeout: 60000
# 在关闭连接之前等待另一个 HTTP 请求的时间。如果未设置,则使用 connectionTimeout。设置为 -1 时不会超时。
keep-alive-timeout: 60000
# 在连接关闭之前可以进行流水线处理的最大HTTP请求数量。当设置为0或1时,禁用keep-alive和流水线处理。当设置为-1时,允许无限数量的流水线处理或keep-alive请求。
max-keep-alive-requests: 1000

Nginx长连接学习之二的更多相关文章

  1. Nginx入门篇(二)之Nginx部署与配置文件解析

    一.Nginx编译安装 ()查看系统环境 [root@localhost tools]# cat /etc/redhat-release CentOS Linux release (Core) [ro ...

  2. nginx限制请求之二:(ngx_http_limit_req_module)模块

    相关文章: <高可用服务设计之二:Rate limiting 限流与降级> <nginx限制请求之一:(ngx_http_limit_conn_module)模块> <n ...

  3. spa(单页应用)中,使用history模式时,微信长按识别二维码在ios下失效的问题

    spa(单页应用,vue)中,使用history模式时,微信长按识别二维码在ios下失效的问题. 触发条件: spa单页应用: 路由模式 history 从其他页面跳转到带有微信二维码识别的页面(不是 ...

  4. iOS - WKWebView的使用和长按手势识别二维码并保存

    WKWebView的图片二维码使用: .长按手势识别二维码并保存 .识别二维码跳转;不是链接显示内容点击网址跳转 .解决url包含中文不能编码的问题 .文字带链接网址,点击跳转 .纯文本-文字html ...

  5. 移动端禁止图片长按和vivo手机点击img标签放大图片,禁止长按识别二维码或保存图片【转载】

    移动端禁止图片长按和vivo手机点击img标签放大图片,禁止长按识别二维码或保存图片 img{ pointer-events: none; } 源文地址:https://www.cnblogs.com ...

  6. 用lua nginx module搭建一个二维码

    用lua nginx module搭建一个二维码(qr code)生成器 作者 vinoca 發布於 2014年10月31日 如果有VPS,或者开源的路由器,安装一个nginx,添加lua-nginx ...

  7. Canvas与Image互相转换示例以及利用该技术实现微信长按自动识别二维码功能

    现在扫描二维码已经很普遍,微信扫一扫即可,但是如果二维码是在自己的手机上呢?那就要用到微信里的一个功能了,手指长按二维码,会弹出自动识别的选项,点确定就可以看到二维码的内容了.那么怎么通过前端实现这个 ...

  8. 使用 Docker 和 Nginx 打造高性能的二维码服务

    使用 Docker 和 Nginx 打造高性能的二维码服务 本文将演示如何使用 Docker 完整打造一个基于 Nginx 的高性能二维码服务,以及对整个服务镜像进行优化的方法.如果你的网络状况良好, ...

  9. 微信长按识别二维码,在 vue 项目中的实现

    微信长按识别二维码是 QQ 浏览器的内置功能,该功能的基础一定要使用 img 标签引入图片,其他方式的二维码无法识别. 在 vue 中使用 QrcodeVue 插件 demo1 在 template ...

  10. Nginx常用功能配置二

    Nginx常用功能配置二 Nginx location匹配设置 location作用:可以根据用户请求的URI来执行不同的应用,根据用户请求的网站的地址URL匹配. location语法: locat ...

随机推荐

  1. ElasticSearch之cat templates API

    命令样例如下: curl -X GET "https://localhost:9200/_cat/templates?v=true&pretty" --cacert $ES ...

  2. linux中iptables防火墙相关命令

    https://www.cnblogs.com/seven1979/p/4173927.html https://blog.csdn.net/shenjianxz/article/details/62 ...

  3. Blazor入门100天 : 身份验证和授权之 OpenID 与 OAuth2

    目录: OpenID 与 OAuth2 基础知识 Blazor wasm Gitee 码云登录 Blazor wasm GitHub 登录 Blazor wasm Google 登录 Blazor w ...

  4. 20、Scaffold属性 BottomNavigationBar 自定义底部导航

    BottomNavigationBar 是底部导航条,可以让我们定义底部Tab切换,bottomNavigationBar是 Scaffold组件的参数. BottomNavigationBar 常见 ...

  5. MySQL进阶篇:详解索引使用_最左前缀法则

    MySQL进阶篇:第四章_四.一_ 索引使用_最左前缀法则 最左前缀法则 如果索引了多列(联合索引),要遵守最左前缀法则.最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列.如果跳跃某一列 ...

  6. 第八部分_Shell脚本之综合案例实训

    综合案例 1. 实战案例1 ㈠ 具体需求 写一个脚本,将跳板机上yunwei用户的公钥推送到局域网内可以ping通的所有机器上 说明:主机和密码文件已经提供 10.1.1.1:123456 10.1. ...

  7. 一文详解kube-apiserver认证鉴权能力

    本文分享自华为云社区<kube-apiserver认证鉴权能力>,作者: 可以交个朋友. HTTPS为什么要进行身份验证 首先不管是kubectl还是API调用都是通过HTTPS访问kub ...

  8. ECS实践案例丨逻辑卷的创建和扩容操作指导

    摘要:实现跨硬盘使用,在传统硬盘之上的一层,在云服务器中可以实现跨EVS使用,用户在某些场景需要创建逻辑卷或者对已有的逻辑卷进行扩容处理,或者在某些时候由于误操作导致上述操作失败. [背景描述]: 实 ...

  9. DataLeap的Catalog系统近实时消息同步能力优化

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 摘要 字节数据中台DataLeap的Data Catalog系统通过接收MQ中的近实时消息来同步部分元数据.Apa ...

  10. Solon 的常用配置

    一.服务端基本属性 #服务端口(默认为8080) server.port: 8080 #服务的 http 信号端口(默认为 ${server.port}) server.http.port: 8080 ...