Nginx长连接学习之二


背景

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

问题场景描述

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

基础知识说明

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

问题现象

  1. 通过如下命令在linux服务器上面查询
  2. netstat -ano |grep ^tcp |awk '{print $6}'|sort |uniq -c|sort -k1hr
  3. 发现有较多的 time_wait 相关的信息
  4. 114229 TIME _WAIT
  5. 因为后面有四到五台应用服务器 平均米格应用服务器已经快3万个 time wait的链接对应了.
  6. 所以nginx 会出现 cannot assign port的问题.

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

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

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

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

插播-长连接的优势

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

解决问题的方式-长连接

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

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

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

解决问题的方式-长连接-nginx作为客户端

  1. http {
  2. upstream backend {
  3. server 192.168.0.18080 weight=1 max_fails=2 fail_timeout=30s;
  4. server 192.168.0.28080 weight=1 max_fails=2 fail_timeout=30s;
  5. keepalive 300; #这个很重要!
  6. keepalive_requests 10000;
  7. }
  8. server {
  9. listen 8080 default_server;
  10. server_name "";
  11. location / {
  12. proxy_pass http://backend;
  13. proxy_http_version 1.1; #设置http版本为1.1
  14. proxy_set_header Connection ""; #设置Connection为长连接(默认为no)
  15. proxy_set_header Host $host;
  16.  proxy_set_header X-Real-IP $remote_addr;
  17.  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  18. proxy_read_timeout 600s; #新增配置1
  19. proxy_send_timeout 120s; #新增配置2
  20. }
  21. }
  22. }

注意事项

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

延伸理解之一

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

延伸理解之二

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

    1.安装必要的组件.升级 yum -y install wget vim cd /etc/yum.repos.d/ rm -rf /etc/yum.repos.d/*.repo wget http:/ ...

  2. CTFHub XSS DOM跳转 WriteUp

    前文:DOM反射XSS 进入网站,直接查看源代码,下面是关键代码,这里有xss漏洞: <script> var target = location.search.split("= ...

  3. 玩转Python:数据可视化,一个很高级的交互式Python库,附代码

    在数据科学和分析的世界里,将数据可视化是至关重要的一步,它能帮助我们更好地理解数据,发现潜在的模式和关系.Python 提供了多种可视化工具,HvPlot 是其中一个出色的库,专为简单且高效的交互式可 ...

  4. 提取 PE文件 / 目标程序 的各种信息

    前段时间项目需要实现对 Windows PE 文件版本信息的提取,如文件说明.文件版本.产品名称.版权.原始文件名等信息.获取这些信息在 Windows 下当然有一系列的 API 函数供调用,简单方便 ...

  5. git blame 用法小记

    1.概述 git管理的代码仓库,在协作开发中不可避免地会出现代码冲突,或者有新手错误地提交代码.出现问题不可怕,可怕的是找不到问题出在哪里.有时候找到出问题的代码,却不知道是谁提交的.git提供了一个 ...

  6. 文心一言 VS 讯飞星火 VS chatgpt (50)-- 算法导论6.2 2题

    二.参考过程 MAX-HEAPIFY,写出能够维护相应最小堆的 MIN-HEAPIFY(A,i)的伪代码,并比较 MIN-HEAPIFY 与 MAX-HEAPIFY 的运行时间. 文心一言: MIN- ...

  7. MySQL 是如何实现RC事务隔离级别的

    摘要:Read Committed,事务运行期间,只要别的事务修改数据并提交,即可读到人家修改的数据,所以会有不可重复读.幻读问题. 本文分享自华为云社区<MySQL RC事务隔离级别的实现&g ...

  8. 提速 10 倍!深度解读字节跳动新型云原生 Spark History Server

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 前不久,在 6月29日 Databricks 举办的 Data + AI Summit 上,火山引擎向大家首次介绍 ...

  9. 初探: 通过pyo3用rust为python写扩展加速

    众所周知,python性能比较差,尤其在计算密集型的任务当中,所以机器学习领域的算法开发,大多是将python做胶水来用,他们会在项目中写大量的C/C++代码然后编译为so动态文件供python加载使 ...

  10. ABAP 辨析 标准表|排序表|哈希表

    1.文档介绍 本文档将介绍内表的区别和用法,涉及标准表.排序表.哈希表 2.用法与区别 2.1.内表种类 内表顶层为任意表,任意表分为索引表和哈希表,索引表又可分为标准表和排序表,结构如图: 2.2. ...