• Linux系统:Centos 7 x64
  • Nginx版本:1.11.5

Nginx 是一款面向性能设计的 HTTP 服务器,能反向代理 HTTP,HTTPS 和邮件相关(SMTP,POP3,IMAP)的协议链接。并且提供了负载均衡以及 HTTP 缓存。它的设计充分使用异步事件模型,削减上下文调度的开销,提高服务器并发能力。采用了模块化设计,提供了丰富模块的第三方模块。所以关于 Nginx,有这些标签:「异步」「事件」「模块化」「高性能」「高并发」「反向代理」「负载均衡」「长连接」。本章内容主要就是针对于长连接请求模块。

为什么要单独讲解keepalive指令?

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

大多数未仔细研读过nginx的同学通常都会误解这个参数,有些人理解为这里的keepalive是设置是否打开长连接,以为应该设置为on/off。有些人会被前面的keepalive_timeout误导,以为这里也是设置keepalive的timeout。

但是实际上这个keepalive参数的含义非常的奇特,请小心看nginx文档中的说明,因为Keepalive长连接非常重要而且容易理解错误,所以专门做连一期专门讲解keepalive的文章。

本文介绍如何使用Nginx进行配置和实现长连接Keepalive,并介绍如何设置 nginx 以提供静态内容服务,如何配置 nginx 作为代理服务器。

keepalive指令

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

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

从HTTP协议的角度看,Nginx在这个过程中,对于客户端它扮演着HTTP服务器端的角色。而对于真正的服务器端(在nginx的术语中称为upstream)Nginx又扮演着HTTP客户端的角色,keepalive指令出现在版本1.1.4。

keepalive指令格式

  • keepalive不是on/off之类的开关
  • keepalive不是timeout,不是用来设置超时值
Syntax:    keepalive connections;
Default: —
Context: upstream
Activates the cache for connections to upstream servers.

connections的取值代表着连接到upstream服务器的持续连接(即长连接)的数量。很多人都会有一个误解:认为这个参数是设置到upstream服务器的长连接的数量,分歧在于是最大连接数还是最小连接数

官方文档的介绍

The connections parameter sets the maximum number of idle keepalive connections to upstream servers

  • connections参数设置到upstream服务器的空闲keepalive连接的最大数量,这个”idle”的概念,何为idle。大多数人之所以误解为是到upstream服务器的最大长连接数,一般都是因为看到了文档中的这句话,而漏看了这个”idle”一词。

When this number is exceeded, the least recently used connections are closed.

  • 当这个数量被突破时,最近使用最少的连接将被关闭。

Nginx的官方文档给出了指示,否定了最大连接数的可能:keepalive指令不会限制一个nginx worker进程到upstream服务器连接的总数量请注意空闲keepalive连接的最大数量中空闲这个关键字

keepalive实际场景分析

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

  • 之后假设这个upstream服务器的keepalive参数设置比较小,比如常见的10.

  • 再次假设请求和响应是均匀而平稳的,那么这1000条连接应该都是一放回连接池就立即被后续请求申请使用,线程池中的idle线程会非常的少,趋进于零。我们以10毫秒为一个单位,来看连接的情况(注意场景是1000个线程+100毫秒响应时间,每秒有10000个请求完成):

    • 每10毫秒有100个新请求,需要100个连接
    • 每10毫秒有100个请求结束,可以释放100个连接
    • 如果请求和应答都均匀,则10毫秒内释放的连接刚好够用,不需要新建连接,连接池空闲连接为零
  • 如果请求通常不是足够的均匀和平稳,为了简化问题,我们假设应答始终都是平稳的,只是请求不平稳,第一个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个新连接来满足要求

    • 可以看到,在短短的20毫秒内,仅仅因为请求不够均匀,就导致nginx在前10毫秒判断空闲连接过多关闭了40个连接,而后10毫秒又不得不新建40个连接来弥补连接的不足。

    • 再来一次类似的场景,假设请求是均匀的,而应答不再均匀,前10毫秒只有50个请求结束,后10毫秒有150个:

  • 前10毫秒,进来100个请求,结束50个请求,导致连接不够用,nginx为此新建50个连接

  • 后10毫秒,进来100个请求,结束150个请求,导致空闲连接过多,ngixn为此关闭了150-100-10=40个空闲连接

    • 第二个应答不均匀的场景实际上是对应第一个请求不均匀的场景:正是因为请求不均匀,所以导致100毫秒之后这些请求的应答必然不均匀

现实世界中的请求往往和理想状态有巨大差异,请求不均匀,服务器处理请求的时间也不平稳,这理论上的大概1000个连接在反复的回收和再分配的过程中,必然出现两种非常矛盾场景在短时间内反复:

  1. 连接不够用,造成新建连接
  2. 连接空闲,造成关闭连接。从而使得总连接数出现反复震荡,不断的创建新连接和关闭连接,使得长连接的效果被大大削弱。

Keepalive参数建议

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

keepalive参数分析

  • 对应的参数设置为每个worker子进程在缓冲中保持的到upstream服务器的空闲keepalive连接的最大数量。当超过这个数量值的时候,会将最近最少使用(LRU)的连接进行关闭。需要考虑的是keepalive指令不会限制一个worker进程到upstream服务器连接的总数量。其参数应该设置为一个足够小的数字来让upstream服务器来处理新进来的连接。
  • 如果想让upstream每次都处理新的进来的连接,就应该将这个值放的足够小。反过来理解,就是如果不想让upstream服务器处理新连接,就应该放大一些?

Keepalive的使用案例

Keepalive对接memcached服务

使用keepalive连接的memcached upstream配置的例子:

upstream memcached_backend {
server 127.0.0.1:11211;
server 10.0.0.2:11211;
keepalive 32;
}
server {
...
location /memcached/ {
set $memcached_key $uri;
memcached_pass memcached_backend;
}
}
Keepalive对接Http1.1的Web服务

对于HTTP,proxy_http_version指定应该设置为”1.1”,而”Connection” header应该被清理:

upstream http_backend {
server 127.0.0.1:8080;
keepalive 16;
}
server {
...
location /http/ {
proxy_pass http://http_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
...
}
}
Keepalive对接Http1.0的Web服务

HTTP/1.0 持久连接可以通过传递”Connection: Keep-Alive” header 到upstream server, 但是不推荐使用这种方法。

Keepalive对接FastCGI的Web服务

对于FastCGI服务器,要求设置fastcgi_keep_conn来让长连接工作:

upstream fastcgi_backend {
server 127.0.0.1:9000;
keepalive 8;
}
server {
...
location /fastcgi/ {
fastcgi_pass fastcgi_backend;
fastcgi_keep_conn on;
...
}
}

当使用默认的round-robin之外的负载均衡算法时,必须在keepalive指令之前激活他们。SCGI 和 uwsgi 协议没有keepalive连接的概念。

保持和client的长连接

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

  • client发送的HTTP请求要求keep alive
  • nginx设置上支持keep alive

HTTP配置

默认情况下,Nginx已经自动开启了对client连接的keep alive支持。一般场景可以直接使用,但是对于一些比较特殊的场景,还是有必要调整个别参数。需要修改nginx的配置文件(在nginx安装目录下的conf/nginx.conf):

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

keepalive_timeout指令

keepalive_timeout指令的语法:

Syntax:    keepalive_timeout timeout [header_timeout];
Default: keepalive_timeout 75s;
Context: http, server, location
  1. 第一个参数设置keep-alive客户端连接在服务器端保持开启的超时值。值为0会禁用keep-alive客户端连接。
  2. 可选的第二个参数在响应的header域中设置一个值“Keep-Alive: timeout=time”。这两个参数可以不一样。

注:默认75s一般情况下也够用,对于一些请求比较大的内部服务器通讯的场景,适当加大为120s或者300s。第二个参数通常可以不用设置

keepalive_requests指令

keepalive_requests指令用于设置一个keep-alive连接上可以服务的请求的最大数量。当最大请求数量达到时,连接被关闭。默认是100。

keepalive_requests指令的实现原理
  • 指一个keepalive请求建立之后,Nginx就会为这个连接设置一个计数器,记录这个keep alive的长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则Nginx会强行关闭这个长连接,逼迫客户端不得不重新建立新的长连接。

这个参数往往被大多数人忽略,因为大多数情况下当QPS(每秒请求数)不是很高时,默认值100凑合够用。但是,对于一些QPS比较高(比如超过10000QPS,甚至达到30000,50000甚至更高) 的场景,默认的100就显得太低

  • 简单计算一下,QPS=10000时,客户端每秒发送10000个请求(通常建立有多个长连接),每个连接只能最多跑100次请求,意味着平均每秒钟就会有100个长连接因此被nginx关闭

  • 为了保持QPS,客户端不得不每秒中重新新建100个连接。因此,如果用netstat命令看客户端机器,就会发现有大量的TIME_WAIT的socket连接(即使此时keep alive已经在client和nginx之间生效)。

因此对于QPS较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少TIME_WAIT

保持和server的长连接

为了让Nginx和server(Nginx称为upstream)之间保持长连接,典型设置如下:

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 "";
client_max_body_size 3072k;
client_body_buffer_size 128k;
}
}
}

总结

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

  • 如果不喜欢计算的同学也可以直接设置为keepalive=1000之类的,一般都OK的了。

深入浅出学习透析Nginx服务器的基本原理和配置指南「Keepalive性能分析实战篇」的更多相关文章

  1. Nginx总结(九)Nginx服务器高性能优化的配置--轻松实现10万并发访问量

    前面讲了如何配置Nginx虚拟主机,如何配置服务日志等很多基础的内容,大家可以去这里看看nginx系列文章:https://www.cnblogs.com/zhangweizhong/category ...

  2. Nginx 核心配置-location的登录账户认证实战篇

    Nginx 核心配置-location的登录账户认证实战篇 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.使用ab命令模拟网站攻击 1>.安装httpd-tools工具 ...

  3. nginx服务器中的安全配置

    一.关闭SELinux 安全增强型Linux(SELinux)的是一个Linux内核的功能,它提供支持访问控制的安全政策保护机制. 但是,SELinux带来的附加安全性和使用复杂性上不成比例,性价比不 ...

  4. windows环境下nginx服务器的安装与配置

    转载至:http://www.cnblogs.com/hxxy2003/archive/2012/09/20/2695254.html nginx服务器是一个高性能的HTTP和反向代理服务器,它以稳定 ...

  5. Linux中Nginx服务器的部署和配置

    目录 Nginx安装方式: yum源安装 目录结构: 源码包安装 目录结构: Nginx中支持PHP Nginx中配置php对mysql数据库的支持 Nginx配置反向代理服务器 正常代理 根据不同端 ...

  6. JSP学习笔记(二):Tomcat服务器的安装及配置

    一.Tomcat的下载及安装. 前往Tomcat官网下载安装包或者免安装压缩包.链接http://tomcat.apache.org/ 这里,我选择的是Tomcat8.0,而不是最新的Tomcat9. ...

  7. Linux系统上Nginx服务器的安装与配置

    前言: 详细步骤移步菜鸟教程 一. 安装Nginx,注意虚拟机与宿主机的网络连通性 l  安装编译工具及库文件(zlib库. ssl) yum -y install make zlib zlib-de ...

  8. [Nginx]实战Nginx:Nginx服务器的安装与配置

    ----------------------------------------------------------------------------------------------- Ngin ...

  9. JAVA WEB学习笔记(二):Tomcat服务器的安装及配置

    一.Tomcat的下载及安装. 前往Tomcat官网下载安装包或者免安装压缩包.链接http://tomcat.apache.org/ 这里,我选择的是Tomcat8.0,而不是最新的Tomcat9. ...

  10. Nginx https免费SSL证书配置指南

    生成证书 $ cd /usr/local/nginx/conf $ openssl genrsa -des3 -out server.key 1024 $ openssl req -new -key  ...

随机推荐

  1. C语言输入输出格式符

    C语言输入输出格式符 printf函数(格式输出函数) 1.一般格式 printf(格式控制,输出表列) 例如:printf("i=%d,ch=%c\n",i,ch); 说明: ( ...

  2. 【loj2538】 【PKUWC 2018】Slay the Spire dp

    我们不难发现,假设抽了x张攻击牌,y张强化牌,那么肯定是打出尽可能多张的强化牌后,再开始出攻击牌(当然最少要一张攻击牌) 我们设G(i,j)表示:所有(抽到的攻击牌牌数为i,打出的攻击牌牌数为j)的方 ...

  3. 3_MyBatis

    一. 引言 1.1 什么是框架? 软件的半成品, 解决了软件开发过程中的普适性问题, 从而简化了开发步骤, 提升了开发效率 1.2 什么是ORM框架? ORM(Object Relational Ma ...

  4. Kafka Eagle 3.0.1功能预览

    1.概述 最近有同学留言,关于Kafka Eagle的一些使用问题.今天笔者就为大家来详细介绍Kafka Eagle 3.0.1的功能以及使用方法. 2.内容 在3.0.1版本中,EFAK优化了分布式 ...

  5. MySQL数据库-数据表(上)

    数据表的基本操作. MySQL 数据库支持多种数据类型,大致可以分为 3 类:数值类型.日期和时间类型.字符串(字符)类型. (1)数值类型 数值类型用于存储数字型数据,这些类型包括整数类型(TINY ...

  6. PAT (Basic Level) Practice 1024 科学计数法 分数 20

    科学计数法是科学家用来表示很大或很小的数字的一种方便的方法,其满足正则表达式 [+-][1-9].[0-9]+E[+-][0-9]+,即数字的整数部分只有 1 位,小数部分至少有 1 位,该数字及其指 ...

  7. python实验报告(第五周)

    一.实验目的和要求 学会使用字符串的常用操作方法和正确应用正则表达式. 二.实验环境 软件版本:Python 3.10 64_bit 三.实验过程 1.实例1:使用字符串拼接输出一个关于程序员的笑话 ...

  8. python流程控制下-for、while循环补充

    循环结构之for循环 实现循环结构还可以用关键字for. for关键字 我们来看这一段代码: emotions = ['smile', 'laugh', 'cry', 'angry'] for emo ...

  9. DVWA File Upload level high 通关

    由于level 是 high 1.代码审计 <?php if( isset( $_POST[ 'Upload' ] ) ) { // Where are we going to be writi ...

  10. Request method ‘POST‘ not supported。 Failed to load resource: net::ERR_FAILED

    1.问题描述 技术栈:前后端项目分离(Springboot+Vue+MybatisPlus) 前端报错信息: 后端报错信息: 2.问题分析 这里使用了ResultFul风格的接口设计方式.增删改查对应 ...