【nginx实战】通过nginx实现http请求的keep alive长连接

文章目录

  • 一. 概述
  • 二. nginx与client的长连接
    • 1. keepalive_timeout指令
    • 2. keepalive_requests指令
      • 场景分析
  • 三. 保持和server的长连接
    • 1. location设置
      • 场景分析
    • 2. upstream设置
      • 3. 场景分析
        • 场景1:
        • 场景2:
        • 场景3:

一. 概述

当使用nginx作为反向代理时,为了支持长连接,需要做到:1. 从client到nginx的连接是长连接; 2. 从nginx到server的连接是长连接。

从HTTP协议的请求过程中,对于client,nginx扮演着HTTP服务器端的角色;对于服务器端(在nginx中upstream中配置的server),nginx扮演着HTTP客户端的角色。

 

二. nginx与client的长连接

为了在client和nginx之间保持长连接,有两个要求:

  1. client发送的HTTP请求要求keep alive
  2. nginx设置上支持keep alive:默认情况下,nginx已经自动开启了对client连接的keep alive支持。

一般场景可以直接使用,但是对于一些比较特殊的场景,还是有必要调整个别参数。需要修改nginx的配置文件nginx.conf:

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

 

1. keepalive_timeout指令

keepalive_timeout指令的语法:

Syntax:    keepalive_timeout timeout [header_timeout];
Default:    keepalive_timeout 75s;
Context:    http, server, location1. 第一个参数设置keep-alive客户端连接在服务器端保持开启的超时值。值为0会禁用keep-alive客户端连接。默认75s一般情况下也够用,对于一些请求比较大的内部服务器通讯的场景,适当加大为120s或者300s。2. (可选的)第二个参数在响应的header域中设置一个值“Keep-Alive: timeout=time”。第二个参数通常可以不用设置。

 

2. keepalive_requests指令

使用语法:

Syntax:	keepalive_requests number;
Default:	keepalive_requests 1000;
Context:	http, server, location;该指令首次出现在版本0.8.0中。在版本1.19.10之前,默认值为1001.参数的作用
>一个TCP(keep-alive)连接上最多执行多少个HTTP请求。
>当达到这个参数设置的最大值(默认是1000)时,则nginx会强行关闭这个长连接,
>逼迫客户端不得不重新建立新的长连接。注意:
定期关闭连接是为了释放每个连接的内存分配。
因此,设置过高的最大请求数可能导致过多的内存使用,这是不推荐的。

 

场景分析

这个参数往往被大多数人忽略,因为大多数情况下当QPS(每秒请求数)不是很高时,默认值100凑合够用。但是,对于一些QPS比较高(比如超过10万QPS,甚至达到30万,50万甚至更高) 的场景,默认的1000就不适用了。因为此时会频繁创建长连接。

出现频繁的关闭、创建连接:

当QPS=10万/s时,客户端每秒发送10万个请求(通常建立有多个长连接),每个连接只能最多跑1000次请求,意味着平均每秒钟就会有100个长连接因此被nginx关闭。同样意味着为了保持QPS,客户端不得不每秒中重新新建100个连接。

大量的TIME_WAIT

如果用netstat命令看客户端机器,就会发现有大量的TIME_WAIT的socket连接(即使此时keep alive已经在client和nginx之间生效)。因此对于QPS较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少TIME_WAIT。

 

三. 保持和server的长连接

1. location设置

为了让nginx和server(upstream块中的servers)之间保持长连接,典型设置如下:

http {upstream  BACKEND {server   192.168.0.18080  weight=1 max_fails=2 fail_timeout=30s;server   192.168.0.28080  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 "";client_max_body_size  3072k;client_body_buffer_size 128k;}}
}

location中的两个参数:

  proxy_http_version 1.1;                   proxy_set_header Connection "";
  1. HTTP协议中对长连接的支持是从1.1版本之后才有的,因此最好通proxy_http_version指令设置为"1.1";
  2. 代表来自client的请求Connection header会被清理。清理 Connection 头可以确保代理层完全控制连接的生命周期。

 

场景分析

假设client和nginx之间是短连接,nginx和upstream之间可以开启长连接。这种情况下必须清理来自client请求中的Connection header,如果不清理,则会将client的header传递过来导致不能开启长连接。

 

2. upstream设置

upstream设置中,有个参数要特别的小心,就是这个keepalive。

nginx keepalive语法:

Syntax:   keepalive connections;
Default:  —
Context:  upstream

keepalive参数作用

connections参数设置每个worker进程与upstream server建立的最多空闲 的keepalive连接数量。当这个数量被突破时,最近使用最少的连接将被关闭。

特别提醒:keepalive指令不会限制一个nginx worker进程到upstream服务器连接的总数量。connections参数应该设置为一个足够小的数字来让upstream服务器来处理新进来的连接。

 

3. 场景分析

场景描述

有一个HTTP服务,作为upstream server接收请求,响应时间为100毫秒。如果要达到10000 QPS的性能,就需要在nginx和upstream服务器之间建立大约1000条HTTP连接。nginx为此建立连接池,然后请求过来时为每个请求分配一个连接,请求结束时回收连接放入连接池中,连接的状态也就更改为idle。

我们再假设这个upstream服务器的keepalive参数设置比较小,比如常见的10.
 

场景1:

假设请求和响应是均匀而平稳的,那么这1000条连接应该都是一放回连接池就立即被后续请求申请使用,线程池中的idle线程会非常的少,趋进于零。
我们以10毫秒为一个单位,来看连接的情况:

  • 每10毫秒有100个新请求,需要100个连接
  • 每10毫秒有100个请求结束,可以释放100个连接
  • 如果请求和应答都均匀,则10毫秒内释放的连接刚好够用,不需要新建连接,连接池也不空闲。
场景2:

回到现实世界,请求通常不是足够的均匀和平稳,为了简化问题,我们假设应答始终都是平稳的,只是请求不平稳:

  1. 假设此时10毫秒内只有50个请求。此时连接池内有50个空闲连接。注意看keepalive=10的设置,这意味着连接池中最多容许保留有10个空闲连接。因此nginx不得不将这50个空闲连接中的40个关闭,只留下10个。
  2. 再下一个10个毫秒,有150个请求进来,有100个请求结束任务释放连接。150 - 100 = 50,空缺了50个连接,减掉前面连接池保留的10个空闲连接,nginx不得不新建40个新连接来满足要求。
    我们可以看到,在短短的20毫秒内,仅仅因为请求不够均匀,就导致nginx在前10毫秒判断空闲连接过多关闭了40个连接,而后10毫秒又不得不新建40个连接来弥补连接的不足。
场景3:

假设请求是均匀的,而应答不再均匀,前10毫秒只有50个请求结束,后10毫秒有150个:

  1. 前10毫秒,进来100个请求,结束50个请求,导致连接不够用,nginx为此新建50个连接
  2. 后10毫秒,进来100个请求,结束150个请求,导致空闲连接过多,ngixn为此关闭了150-10=140个空闲连接

 

小结

现实世界中请求往往不均匀,服务器处理请求的时间也不平稳,当qps很高时,就会在短时间内导致两种非常矛盾现象: 1. 连接不够用,造成新建连接;2. 连接空闲,造成关闭连接。从而使得总连接数出现反复震荡,不断的创建新连接和关闭连接,使得长连接的效果被大大削弱。
涉及的主要设置keepalive的最大空闲连接数。毕竟连接池中的1000个连接在频繁利用时,出现短时间内多余10个空闲连接的概率实在太高。因此为了避免出现上面的连接震荡,我们将keepalive设置为200,300,就可以非常有效的缓冲请求和应答不均匀。

keepalive参数设置方法:比如前面10000 QPS和100毫秒响应时间就可以推算出需要的长连接数量大概是1000. 然后将keepalive设置为这个长连接数量的10%到30%。
比较懒的同学,可以直接设置为keepalive=1000之类的,一般都OK的了。

 

参考:
https://www.jianshu.com/p/142b35998947

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

神经网络的一些常规概念

epoch:是指所有样本数据在神经网络训练一次(单次epoch(全部训练样本/batchsize)/iteration1)或者(1个epochiteration数 batchsize数) batch-size:顾名思义就是批次大小,也就是一次训练选取的样…

Vue中使用定义的函数时,无法访问到data()里面的数据

const translateItems1 () > {this.translatedItems this.items1.map(item > {return {...item,label: this.$t(item.labelKey)};}); items1是我们data()里面的数据,无法访问到 解决办法 把箭头函数替换为普通函数 const translateItems1 function() {th…

EXCEL VBA实现重复字段出现次数并列显示

EXCEL VBA实现重复字段出现次数并列显示 Sub dotest() Dim arr, dApplication.ScreenUpdating FalseSet d CreateObject("Scripting.Dictionary")With Sheets("Sheet2")r .Cells(.Rows.Count, "a").End(xlUp).Rowarr .[a1].Resize(r, 1)En…

HTML标签 - 1

文章目录 HTML标签简介HTML书写规范常见网页制作软件常用标签结构标签排版标签标题标签容器标签字体标签文本格式化标签列表标签图片标签 HTML标签 简介 一门使用标记标签来描述网页,展示信息给用户的语言。 超文本标记语言(Hyper Text Markup Langua…

保障网络环境清朗与安全:非法关键字过滤的重要性与实现方法

在当今数字化时代,网络已经成为人们获取信息、交流思想的主要平台。然而,随着互联网的普及,一些不法分子也越发倾向于通过网络渠道散布有害信息。为了维护网络环境的清朗与安全,非法关键字过滤技术应运而生。本文将探讨非法关键字…

WMS系统与电商平台快速拉通库存数量

什么是WMS系统 WMS系统是指仓储管理系统(Warehouse Management System)。它是一种用于管理和控制仓库运营的软件系统。WMS系统通过集成信息技术,提供仓库内货物的存储、出入库、库存管理、订单处理等功能,优化仓库的运作效率和准…

Flask 入门3:Flask 请求上下文与请求

1. 前言 Flask 在处理请求与响应的过程: 首先我们从浏览器发送一个请求到服务端,由 Flask 接收了这个请求以后,这个请求将会由路由系统接收。然后在路由系统中,还可以挂入一些 “勾子”,在进入我们的 viewFunction …

ubuntu22.04 安装conda

要在Ubuntu 22.04上安装Anaconda,可以遵循以下步骤: 首先,打开终端并更新系统包仓库,也需要安装curl工具,这可以通过以下命令完成: sudo apt update && sudo apt install curl -y使用curl命令行工具…

adb 无线连接 操作Android设备

最近集五福活动比较热门 可以用这个工具 用自己擅长的语言写一个循环程序 运行起来就可以 自动帮我们 看视频得福卡了 很方便 while (true) {sleep(mt_rand(15, 25));system(adb shell input swipe 500 2000 500 1000 100); } 1. 首先下载 安卓开发工具 adb adb网盘链接 链接…

Django中的模板

目录 一:基本概念 二:模板继承 在Django中,模板是用于呈现动态内容的HTML文件。它们允许你将动态数据与静态模板结合起来,生成最终的HTML页面。 Django模板使用特定的语法和标签来插入动态内容。你可以在模板中使用变量、过滤器和标签来控…

【HarmonyOS】鸿蒙开发之HTTP网络请求——第5章

HTTP网络请求封装 network/request.ets import { configInterface } from ./type import http from ohos.net.http import { getToken } from ../utils/storage//网络请求封装 export const request (config:configInterface)>{let httpRequest:http.HttpRequest http.c…

Python爬虫存储库安装

如果你还没有安装好MySQL、MongoDB、Redis 数据库,请参考这篇文章进行安装: Windows、Linux、Mac数据库的安装(mysql、MongoDB、Redis)-CSDN博客 存储库的安装 上节中,我们介绍了几个数据库的安装方式,但…

IDEA 取消参数名称提示、IDEA如何去掉变量类型提醒

一、IDEA 取消参数名称显示 取消显示形参名提示 例如这样的提示信息 二、解决方法 1、File—>Setting–>Editor—>Inlay Hints—>Java 去掉 Show Parameter hints for 前面的勾即可,然后Apply—>Ok 2、右键Disable Hints

Go语言基础之函数

1. golang函数特点: • 无需声明原型。• 支持不定 变参。• 支持多返回值。• 支持命名返回参数。 • 支持匿名函数和闭包。• 函数也是一种类型,一个函数可以赋值给变量。• 不支持 嵌套 (nested) 一个包不能有两个名字一样的函数。• 不支持 重载 (ov…

强敌环伺:金融业信息安全威胁分析——整体态势

从早期的Zeus和其他以银行为目标的特洛伊木马程序,到现在的大规模分布式拒绝服务(DDoS)攻击,再到新颖的钓鱼攻击和勒索软件,金融服务业已成为遭遇网络犯罪威胁最严重的行业之一。金融服务业的重要性不言而喻&#xff0…

【C语言】(14)结构体

结构体是C语言中一种允许将多个不同类型的数据项组合成一个单一的复合类型的数据结构。通过结构体,可以更加方便地管理和组织复杂的数据。 1. 结构体的定义 结构体通过关键字 struct 定义。结构体定义不会占用内存空间,它只是定义了一个模板。 struct…

H12-821_320

320.关于VRRP slave设备的描述,正确的是 A.当收到优先级为0的VRRP报文时,Slave会直接切换为 Master B.slave响应目的IP地址为虚拟IP地址的IP报文 C.当slave收到 Master发送的VRRP报文时,可判断 Master的状态是否正常 D.slave会丢弃目的MAC地址为虚拟MAC地址的IP报文 答案&#…

【AI视野·今日NLP 自然语言处理论文速览 第七十七期】Mon, 15 Jan 2024

AI视野今日CS.NLP 自然语言处理论文速览 Mon, 15 Jan 2024 Totally 57 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Machine Translation Models are Zero-Shot Detectors of Translation Direction Authors Michelle Wastl, Ja…

docker私有库

1.registry私有仓库 拉取registry镜像 docker pull registry 修改docker配置文件并重启 vim /etc/docker/daemon.json {"insecure-registries": ["172.16.23.23:5000"], #添加,注意用逗号结尾"registry-mirrors": ["ht…

MongoDB 中的事务

前言 在 MongoDB 中,对单个文档的操作都是原子的。因为可以在单个文档结构中使用内嵌文档和数据获得数据之间的关系,所以不必跨多个文档和集合进行范式化,这种结构特性,避免了很多场景中的对多文档事务的需求。 对于需要多个文档…