nginx 优化之 keepalive

目录

  • 1. nginx 优化之 keepalive
    • 1.1. nginx 之 tcp_nopush、tcp_nodelay、sendfile
      • 1.1.1. TCP_NODELAY
      • 1.1.2. TCP_NOPUSH
      • 1.1.3. sendfile
    • 1.2. nginx 长连接——keepalive
      • 1.2.1. 保持和 client 的长连接:
      • 1.2.2. 保持和 server 的长连接:
      • 1.2.3. 综上, 出现大量 TIME_WAIT 的情况

1. nginx 优化之 keepalive

1.1. nginx 之 tcp_nopush、tcp_nodelay、sendfile

1.1.1. TCP_NODELAY

你怎么可以强制 socket 在它的缓冲区里发送数据?
一个解决方案是 TCP 堆栈的 TCP_NODELAY 选项。这样就可以使缓冲区中的数据立即发送出去。

Nginx 的 TCP_NODELAY 选项使得在打开一个新的 socket 时增加了 TCP_NODELAY 选项。但这时会造成一种情况:
终端应用程序每产生一次操作就会发送一个包, 而典型情况下一个包会拥有一个字节的数据以及 40 个字节长的包头, 于是产生 4000%的过载, 很轻易地就能令网络发生拥塞。为了避免这种情况, TCP 堆栈实现了等待数据 0.2 秒钟, 因此操作后它不会发送一个数据包, 而是将这段时间内的数据打成一个大的包。这一机制是由 Nagle 算法保证。

Nagle 化后来成了一种标准并且立即在因特网上得以实现。它现在已经成为默认配置了, 但有些场合下把这一选项关掉也是合乎需要的。现在假设某个应用程序发出了一个请求, 希望发送小块数据。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。
如果我们马上发送数据, 那么交互性的以及客户/服务器型的应用程序将极大地受益。如果请求立即发出那么响应时间也会快一些。以上操作可以通过设置套接字的 TCP_NODELAY = on 选项来完成, 这样就禁用了 Nagle 算法。(不需要等待 0.2s)

1.1.2. TCP_NOPUSH

在 nginx 中, tcp_nopush 配置和 tcp_nodelay “互斥”。它可以配置一次发送数据的包大小。也就是说, 它不是按时间累计 0.2 秒后发送包, 而是当包累计到一定大小后就发送。

注: 在 nginx 中, tcp_nopush 必须和 sendfile 搭配使用。

1.1.3. sendfile

现在流行的 web 服务器里面都提供 sendfile 选项用来提高服务器性能, 那到底 sendfile 是什么, 怎么影响性能的呢?
sendfile 实际上是 Linux2.0+以后的推出的一个系统调用, web 服务器可以通过调整自身的配置来决定是否利用 sendfile 这个系统调用。先来看一下不用 sendfile 的传统网络传输过程:
read(file,tmp_buf, len);
write(socket,tmp_buf, len);

硬盘 >> kernel buffer >> user buffer>> kernel socket buffer >>协议栈

1)一般来说一个网络应用是通过读硬盘数据, 然后写数据到 socket 来完成网络传输的。上面 2 行用代码解释了这一点, 不过上面 2 行简单的代码掩盖了底层的很多操作。来看看底层是怎么执行上面 2 行代码的:

  1. 系统调用 read() 产生一个上下文切换: 从 user mode 切换到 kernel mode, 然后 DMA 执行拷贝, 把文件数据从硬盘读到一个 kernel buffer 里。
  2. 数据从 kernel buffer 拷贝到 user buffer, 然后系统调用 read() 返回, 这时又产生一个上下文切换: 从 kernel mode 切换到 user mode。
  3. 系统调用 write() 产生一个上下文切换: 从 user mode 切换到 kernel mode, 然后把步骤 2 读到 user buffer 的数据拷贝到 kernel buffer(数据第 2 次拷贝到 kernel buffer), 不过这次是个不同的 kernel buffer, 这个 buffer 和 socket 相关联。
  4. 系统调用 write() 返回, 产生一个上下文切换: 从 kernel mode 切换到 user mode(第 4 次切换了), 然后 DMA 从 kernel buffer 拷贝数据到协议栈(第 4 次拷贝了)。
    上面 4 个步骤有 4 次上下文切换, 有 4 次拷贝, 我们发现如果能减少切换次数和拷贝次数将会有效提升性能。在 kernel2.0+ 版本中, 系统调用 sendfile() 就是用来简化上面步骤提升性能的。sendfile() 不但能减少切换次数而且还能减少拷贝次数。

2)再来看一下用 sendfile() 来进行网络传输的过程:
sendfile(socket,file, len);

硬盘 >> kernel buffer (快速拷贝到 kernelsocket buffer) >>协议栈

  1. 系统调用 sendfile() 通过 DMA 把硬盘数据拷贝到 kernel buffer, 然后数据被 kernel 直接拷贝到另外一个与 socket 相关的 kernel buffer。这里没有 user mode 和 kernel mode 之间的切换, 在 kernel 中直接完成了从一个 buffer 到另一个 buffer 的拷贝。
  2. DMA 把数据从 kernelbuffer 直接拷贝给协议栈, 没有切换, 也不需要数据从 user mode 拷贝到 kernel mode, 因为数据就在 kernel 里。
    步骤减少了, 切换减少了, 拷贝减少了, 自然性能就提升了。这就是为什么说在 Nginx 配置文件里打开 sendfile on 选项能提高 web server 性能的原因。

综上, 这三个参数都应该配置成 on: sendfile on; tcp_nopush on; tcp_nodelay on;

1.2. nginx 长连接——keepalive

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

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

1.2.1. 保持和 client 的长连接:

默认情况下, nginx 已经自动开启了对 client 连接的 keep alive 支持(同时 client 发送的 HTTP 请求要求 keep alive)。一般场景可以直接使用, 但是对于一些比较特殊的场景, 还是有必要调整个别参数(keepalive_timeout 和 keepalive_requests)。

http {keepalive_timeout  120s 120s;keepalive_requests 10000;
}

1)keepalive_timeout

语法:

keepalive_timeout timeout [header_timeout];
  1. 第一个参数: 设置 keep-alive 客户端连接在服务器端保持开启的超时值(默认 75s); 值为 0 会禁用 keep-alive 客户端连接;
  2. 第二个参数: 可选、在响应的 header 域中设置一个值"Keep-Alive: timeout=time"; 通常可以不用设置;

注: keepalive_timeout 默认 75s, 一般情况下也够用, 对于一些请求比较大的内部服务器通讯的场景, 适当加大为 120s 或者 300s;

2)keepalive_requests:
keepalive_requests 指令用于设置一个 keep-alive 连接上可以服务的请求的最大数量, 当最大请求数量达到时, 连接被关闭。默认是 100。这个参数的真实含义, 是指一个 keep alive 建立之后, nginx 就会为这个连接设置一个计数器, 记录这个 keep alive 的长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时, 则 nginx 会强行关闭这个长连接, 逼迫客户端不得不重新建立新的长连接。
大多数情况下当 QPS(每秒请求数)不是很高时, 默认值 100 凑合够用。但是, 对于一些 QPS 比较高(比如超过 10000QPS, 甚至达到 30000,50000 甚至更高) 的场景, 默认的 100 就显得太低。
简单计算一下, QPS=10000 时, 客户端每秒发送 10000 个请求(通常建立有多个长连接), 每个连接只能最多跑 100 次请求, 意味着平均每秒钟就会有 100 个长连接因此被 nginx 关闭。同样意味着为了保持 QPS, 客户端不得不每秒中重新新建 100 个连接。因此, 就会发现有大量的 TIME_WAIT 的 socket 连接(即使此时 keep alive 已经在 client 和 nginx 之间生效)。因此对于 QPS 较高的场景, 非常有必要加大这个参数, 以避免出现大量连接被生成再抛弃的情况, 减少 TIME_WAIT。

1.2.2. 保持和 server 的长连接:

为了让 nginx 和后端 server(nginx 称为 upstream)之间保持长连接, 典型设置如下: (默认 nginx 访问后端都是用的短连接 (HTTP1.0), 一个请求来了, 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;        // 这个很重要! }
server {listen 8080 default_server;server_name "";location /  {proxy_pass http://BACKEND;proxy_set_header Host  $Host;proxy_set_header x-forwarded-for $remote_addr;proxy_set_header X-Real-IP $remote_addr;add_header Cache-Control no-store;add_header Pragma  no-cache;proxy_http_version 1.1;         // 这两个最好也设置proxy_set_header Connection "";}}
}

1)location 中有两个参数需要设置:

http {server {location /  {proxy_http_version 1.1; // 这两个最好也设置proxy_set_header Connection "";}}
}

HTTP 协议中对长连接的支持是从 1.1 版本之后才有的, 因此最好通过 proxy_http_version 指令设置为"1.1";
而"Connection" header 应该被清理。清理的意思, 我的理解, 是清理从 client 过来的 http header, 因为即使是 client 和 nginx 之间是短连接, nginx 和 upstream 之间也是可以开启长连接的。这种情况下必须清理来自 client 请求中的"Connection" header。

2)upstream 中的 keepalive 设置:
此处 keepalive 的含义不是开启、关闭长连接的开关; 也不是用来设置超时的 timeout; 更不是设置长连接池最大连接数。官方解释:

  1. The connections parameter sets the maximum number of idle keepalive connections to upstream servers connections(设置到 upstream 服务器的空闲 keepalive 连接的最大数量)
  2. When this number is exceeded, the least recently used connections are closed. (当这个数量被突破时, 最近使用最少的连接将被关闭)
  3. 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 服务器连接的总数量)

我们先假设一个场景: 有一个 HTTP 服务, 作为 upstream 服务器接收请求, 响应时间为 100 毫秒。如果要达到 10000 QPS 的性能, 就需要在 nginx 和 upstream 服务器之间建立大约 1000 条 HTTP 连接。nginx 为此建立连接池, 然后请求过来时为每个请求分配一个连接, 请求结束时回收连接放入连接池中, 连接的状态也就更改为 idle。我们再假设这个 upstream 服务器的 keepalive 参数设置比较小, 比如常见的 10.

A、假设请求和响应是均匀而平稳的, 那么这 1000 条连接应该都是一放回连接池就立即被后续请求申请使用, 线程池中的 idle 线程会非常的少, 趋进于零, 不会造成连接数量反复震荡。

B、显示中请求和响应不可能平稳, 我们以 10 毫秒为一个单位, 来看连接的情况(注意场景是 1000 个线程+100 毫秒响应时间, 每秒有 10000 个请求完成), 我们假设应答始终都是平稳的, 只是请求不平稳, 第一个 10 毫秒只有 50, 第二个 10 毫秒有 150:

下一个 10 毫秒, 有 100 个连接结束请求回收连接到连接池, 但是假设此时请求不均匀 10 毫秒内没有预计的 100 个请求进来, 而是只有 50 个请求。注意此时连接池回收了 100 个连接又分配出去 50 个连接, 因此连接池内有 50 个空闲连接。
然后注意看 keepalive=10 的设置, 这意味着连接池中最多容许保留有 10 个空闲连接。因此 nginx 不得不将这 50 个空闲连接中的 40 个关闭, 只留下 10 个。
再下一个 10 个毫秒, 有 150 个请求进来, 有 100 个请求结束任务释放连接。150 - 100 = 50, 空缺了 50 个连接, 减掉前面连接池保留的 10 个空闲连接, nginx 不得不新建 40 个新连接来满足要求。
C、同样, 如果假设相应不均衡也会出现上面的连接数波动情况。

造成连接数量反复震荡的一个推手, 就是这个 keepalive 这个最大空闲连接数。毕竟连接池中的 1000 个连接在频繁利用时, 出现短时间内多余 10 个空闲连接的概率实在太高。因此为了避免出现上面的连接震荡, 必须考虑加大这个参数, 比如上面的场景如果将 keepalive 设置为 100 或者 200, 就可以非常有效的缓冲请求和应答不均匀。

总结:
keepalive 这个参数一定要小心设置, 尤其对于 QPS 比较高的场景, 推荐先做一下估算, 根据 QPS 和平均响应时间大体能计算出需要的长连接的数量。比如前面 10000 QPS 和 100 毫秒响应时间就可以推算出需要的长连接数量大概是 1000. 然后将 keepalive 设置为这个长连接数量的 10%到 30%。比较懒的同学, 可以直接设置为 keepalive=1000 之类的, 一般都 OK 的了。

1.2.3. 综上, 出现大量 TIME_WAIT 的情况

1)导致 nginx 端出现大量 TIME_WAIT 的情况有两种:

  • keepalive_requests 设置比较小, 高并发下超过此值后 nginx 会强制关闭和客户端保持的 keepalive 长连接; (主动关闭连接后导致 nginx 出现 TIME_WAIT)
  • keepalive 设置的比较小(空闲数太小), 导致高并发下 nginx 会频繁出现连接数震荡(超过该值会关闭连接), 不停的关闭、开启和后端 server 保持的 keepalive 长连接;

2)导致后端 server 端出现大量 TIME_WAIT 的情况:

nginx 没有打开和后端的长连接, 即: 没有设置 proxy_http_version 1.1; 和 proxy_set_header Connection “”; 从而导致后端 server 每次关闭连接, 高并发下就会出现 server 端出现大量 TIME_WAIT

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/143517.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

基于SSM的保险业务管理系统设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…

Kubernetes(K8s):未来云原生应用的引擎

文章目录 Kubernetes的核心概念和架构为什么K8s是构建云原生应用的首选工具?云原生应用的好处和挑战容器编排的重要性:Docker和KubernetesKubernetes生态系统:核心组件和附加工具实际应用:企业如何在生产环境中使用K8s未来展望&am…

【VUE复习·2】@click 之事件处理与函数(可传参);@click 阻止事件冒泡应用场景;@click 多修饰符应用场景(高级)

总览 1.“事件处理”是什么 2.click 函数参数传递应用 3.click 阻止事件冒泡应用场景 4.click 多修饰符应用场景(高级) 一、“事件处理”是什么 1.概念 我们在和页面进行交互时,进行点击或滑动或其他动作时,我们操作的是 DOM …

MR混合现实在军事课堂教学中的应用演示

战场模拟:利用MR技术可以创建逼真的战场模拟环境,将学生置身于真实的战场场景中,可以体验和学习各种作战技巧和战术策略。学生可以通过佩戴MR头盔或眼镜,观察虚拟的场景,并与虚拟对象进行互动,如操作武器、…

深度学习|如何确定 CUDA+PyTorch 版本

对于深度学习初学者来说,配置深度学习的环境可能是一大难题,因此本文主要讲解CUDA; cuDNN; Pytorch 三者是什么,以及他们之间的依赖关系。 CUDA CUDA(Compute Unified Device Architecture)是由NVIDIA开发的用于并行计…

【C/C++笔试练习】——printf在使用%的注意事项、for循环语句的三个条件、运算符优先级、删除公共字符

文章目录 C/C笔试练习1.%符号在printf用作格式说明符的注意事项(1)输出%5.3s(2)判断%中小数点含义 2.for循环语句的三个条件(3)判断循环次数(4)判断循环次数 3.运算符优先级&#xf…

独立站引流,如何在Reddit进行营销推广?

Reddit是目前最被忽视却最具潜力的社交媒体营销平台之一,它相当于国内的百度贴吧,是美国最大的论坛,也是美国第五大网站,流量仅次于Google、Youtube、Facebook以及亚马逊。 如果会玩,Reddit也可以跟其他的社交媒体营销…

Spring 学习(九)整合 Mybatis

1. 整合 Mybatis 步骤 导入相关 jar 包 <dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency>…

Django之初入门

一&#xff09;Django简介 1.简介 Django是一个开源的Python Web框架&#xff0c;它以简洁高效的方式帮助开发者构建复杂的Web应用程序。Django采用了MVC&#xff08;Model-View-Controller&#xff09;的架构模式&#xff0c;通过强大的工具和功能&#xff0c;提供了一套完整…

PHP 变动:PHP 8 版本下字符串与数值的弱比较

文章目录 参考环境声明弱比较隐式类型转换字符串连接数学运算布尔判断相等运算符 字符串与数值的弱比较字符串转化为数值的具体规则字符串与数值的弱比较一般情况科学计数法前缀 0E 与 0e PHP8 在字符串与数值的弱比较方面做出的改动数值字符串优化 参考 项目描述搜索引擎Bing…

数据库及分类详细介绍

目录 一、数据库详细介绍1、什么是数据库2、数据库的种类3、常见的数据库4、关系型数据库4.1、关系型数据库介绍4.2、关系型数据库的优缺点4.3、关系型数据库小结 5、非关系型数据库5.1、非关系数据库诞生的背景5.2、非关系型数据库的介绍5.3、非关系型数据库的优缺点5.4、非关…

js中的类型转换

原文地址 JavaScript 中有两种类型转换&#xff1a;隐式类型转换&#xff08;强制类型转换&#xff09;和显式类型转换。类型转换是将一个数据类型的值转换为另一个数据类型的值的过程。 隐式类型转换&#xff08;强制类型转换&#xff09;&#xff1a; 隐式类型转换是 Java…

NSSCTF

[SWPUCTF 2021 新生赛]gift_F12 在源代码界面找到了flag [SWPUCTF 2021 新生赛]jicao 源码 <?php highlight_file(index.php); include("flag.php"); $id$_POST[id]; $jsonjson_decode($_GET[json],true); if ($id"wllmNB"&&$json[x]"…

【Linux】Linux环境配置安装

目录 一、双系统&#xff08;特别不推荐&#xff09; 安装双系统的缺点&#xff1a; 安装双系统优点&#xff08;仅限老手&#xff09;&#xff1a; 二、虚拟机centos7镜像&#xff08;较为推荐推荐&#xff09; 虚拟机的优点&#xff1a; 虚拟机的缺点&#xff1a; ​ …

Molecular Cancer|CDK9抑制诱导表观遗传重编程,揭示了规避淋巴瘤耐药性的策略

细胞周期蛋白依赖性激酶&#xff08;CDK&#xff09;蛋白家族在细胞周期进程&#xff08;如CDK1/2/4/6&#xff09;和RNA转录&#xff08;如CDK7/8/9/11&#xff09;的调控中起着不可或缺的作用。由于染色体区域易位或基因扩增导致的CDKs表达失调与肿瘤发生有关。在淋巴瘤细胞中…

华为云云耀云服务器L实例评测 | 实例评测使用之硬件性能评测:华为云云耀云服务器下的硬件运行评测

华为云云耀云服务器L实例评测 &#xff5c; 实例评测使用之硬件性能评测&#xff1a;华为云云耀云服务器下的硬件运行评测 介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什么华为云云耀…

爬虫获取一个网站内所有子页面的内容

上一篇介绍了如何爬取一个页面内的所有指定内容&#xff0c;本篇讲的是爬去这个网站下所有子页面的所有指定的内容。 可能有人会说需要的内容复制粘贴&#xff0c;或者直接f12获取需要的文件下载地址一个一个下载就行了&#xff0c;但是如下图十几个一级几十个二级一百多个疾病…

华为云Stack的学习(七)

八、华为云Stack存储服务介绍 1.云硬盘EVS 云硬盘&#xff08;Elastic Volume Service&#xff0c;EVS&#xff09;&#xff0c;又名磁盘&#xff0c;是一种虚拟块存储服务&#xff0c;主要为ECS&#xff08;Elastic Cloud Server&#xff09;和BMS&#xff08;Bare Metal Se…

MySQL - order by排序查询 (查询操作 四)

功能介绍&#xff1a;order by&#xff1a;对查询结果进行排序&#xff0c;执行顺序在查询完结果执行&#xff08;比如where后&#xff09; 排序方式&#xff1a;ASC&#xff1a;升序&#xff08;默认&#xff0c;不用指定&#xff09; DESC&#xff1a;降序 语法&#x…