Nginx流控
流量限制(rate-limiting),是Nginx中一个非常实用,却经常被错误理解和错误配置的功能。我们可以用来限制用户在给定时间内HTTP请求的数量。请求,可以是一个简单网站首页的GET请求,也可以是登录表单的POST请求。
流量限制可以用作安全目的,比如可以减慢暴力密码破解的速率。通过将传入请求的速率限制为真实用户的典型值,并标识目标URL地址(通过日志),还可以用来抵御DDOS攻击。更常见的情况,该功能被用来保护上游应用服务器不被同时太多用户请求所压垮。
本篇文章将会介绍Nginx的 流量限制 的基础知识和高级配置,”流量限制”在Nginx Plus中也适用。
Nginx如何限流
Nginx的”流量限制”使用漏桶算法(leaky bucket algorithm),该算法在通讯和分组交换计算机网络中广泛使用,用以处理带宽有限时的突发情况。就好比,一个桶口在倒水,桶底在漏水的水桶。如果桶口倒水的速率大于桶底的漏水速率,桶里面的水将会溢出;同样,在请求处理方面,水代表来自客户端的请求,水桶代表根据”先进先出调度算法”(FIFO)等待被处理的请求队列,桶底漏出的水代表离开缓冲区被服务器处理的请求,桶口溢出的水代表被丢弃和不被处理的请求。
配置基本的限流
“流量限制”配置两个主要的指令,limit_req_zone
和limit_req
,如下所示:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s; server {
location /login/ {
limit_req zone=mylimit; proxy_pass http://my_upstream;
}
}
limit_req_zone
指令定义了流量限制相关的参数,而limit_req
指令在出现的上下文中启用流量限制(示例中,对于”/login/”的所有请求)。
limit_req_zone
指令通常在HTTP块中定义,使其可在多个上下文中使用,它需要以下三个参数:
Key - 定义应用限制的请求特性。示例中的Nginx变量
$binary_remote_addr
,保存客户端IP地址的二进制形式。这意味着,我们可以将每个不同的IP地址限制到,通过第三个参数设置的请求速率。(使用该变量是因为比字符串形式的客户端IP地址$remote_addr
,占用更少的空间)Zone - 定义用于存储每个IP地址状态以及被限制请求URL访问频率的共享内存区域。保存在内存共享区域的信息,意味着可以在Nginx的worker进程之间共享。定义分为两个部分:通过
zone=keyword
标识区域的名字,以及冒号后面跟区域大小。16000个IP地址的状态信息,大约需要1MB,所以示例中区域可以存储160000个IP地址。Rate - 定义最大请求速率。在示例中,速率不能超过每秒10个请求。Nginx实际上以毫秒的粒度来跟踪请求,所以速率限制相当于每100毫秒1个请求。因为不允许”突发情况”(见下一章节),这意味着在前一个请求100毫秒内到达的请求将被拒绝。
当Nginx需要添加新条目时存储空间不足,将会删除旧条目。如果释放的空间仍不够容纳新记录,Nginx将会返回 503状态码(Service Temporarily Unavailable)。另外,为了防止内存被耗尽,Nginx每次创建新条目时,最多删除两条60秒内未使用的条目。
limit_req_zone
指令设置流量限制和共享内存区域的参数,但实际上并不限制请求速率。所以需要通过添加limit_req
指令,将流量限制应用在特定的location
或者server
块。在上面示例中,我们对/login/
请求进行流量限制。
现在每个IP地址被限制为每秒只能请求10次/login/
,更准确地说,在前一个请求的100毫秒内不能请求该URL。
处理突发
如果我们在100毫秒内接收到2个请求,怎么办?对于第二个请求,Nginx将给客户端返回状态码503。这可能并不是我们想要的结果,因为应用本质上趋向于突发性。相反地,我们希望缓冲任何超额的请求,然后及时地处理它们。我们更新下配置,在limit_req
中使用burst
参数:
location /login/ {
limit_req zone=mylimit burst=20;
proxy_pass http://my_upstream;
}
burst
参数定义了超出zone指定速率的情况下(示例中的mylimit
区域,速率限制在每秒10个请求,或每100毫秒一个请求),客户端还能发起多少请求。上一个请求100毫秒内到达的请求将会被放入队列,我们将队列大小设置为20。
这意味着,如果从一个给定IP地址发送21个请求,Nginx会立即将第一个请求发送到上游服务器群,然后将余下20个请求放在队列中。然后每100毫秒转发一个排队的请求,只有当传入请求使队列中排队的请求数超过20时,Nginx才会向客户端返回503。
无延迟的排队
配置burst
参数将会使通讯更流畅,但是可能会不太实用,因为该配置会使站点看起来很慢。在上面的示例中,队列中的第20个包需要等待2秒才能被转发,此时返回给客户端的响应可能不再有用。要解决这个情况,可以在burst
参数后添加nodelay
参数:
location /login/ {
limit_req zone=mylimit burst=20 nodelay; proxy_pass http://my_upstream;
}
使用nodelay
参数,Nginx仍将根据burst
参数分配队列中的位置,并应用已配置的速率限制,而不是清理队列中等待转发的请求。相反地,当一个请求到达“太早”时,只要在队列中能分配位置,Nginx将立即转发这个请求。将队列中的该位置标记为”taken”(占据),并且不会被释放以供另一个请求使用,直到一段时间后才会被释放(在这个示例中是,100毫秒后)。
假设如前所述,队列中有20个空位,从给定的IP地址发出的21个请求同时到达。Nginx会立即转发这个21个请求,并且标记队列中占据的20个位置,然后每100毫秒释放一个位置。如果是25个请求同时到达,Nginx将会立即转发其中的21个请求,标记队列中占据的20个位置,并且返回503状态码来拒绝剩下的4个请求。
现在假设,第一组请求被转发后101毫秒,另20个请求同时到达。队列中只会有一个位置被释放,所以Nginx转发一个请求并返回503状态码来拒绝其他19个请求。如果在20个新请求到达之前已经过去了501毫秒,5个位置被释放,所以Nginx立即转发5个请求并拒绝另外15个。
效果相当于每秒10个请求的“流量限制”。如果希望不限制两个请求间允许间隔的情况下实施“流量限制”,nodelay
参数是很实用的。
注意: 对于大部分部署,我们建议使用burst
和nodelay
参数来配置limit_req
指令。
高级配置示例
通过将基本的“流量限制”与其他Nginx功能配合使用,我们可以实现更细粒度的流量限制。
白名单
下面这个例子将展示,如何对任何不在白名单内的请求强制执行“流量限制”:
geo $limit {
default 1;
10.0.0.0/8 0;
192.168.0.0/64 0;
} map $limit $limit_key {
0 "";
1 $binary_remote_addr;
} limit_req_zone $limit_key zone=req_zone:10m rate=5r/s; server {
location / {
limit_req zone=req_zone burst=10 nodelay; # ...
}
}
这个例子同时使用了geo
和map
指令。geo
块将给在白名单中的IP地址对应的$limit
变量分配一个值0,给其它不在白名单中的分配一个值1。然后我们使用一个映射将这些值转为key,如下:
- 如果
$limit
变量的值是0,$limit_key
变量将被赋值为空字符串 - 如果
$limit
变量的值是1,$limit_key
变量将被赋值为客户端二进制形式的IP地址
两个指令配合使用,白名单内IP地址的$limit_key
变量被赋值为空字符串,不在白名单内的被赋值为客户端的IP地址。当limit_req_zone
后的第一个参数是空字符串时,不会应用“流量限制”,所以白名单内的IP地址(10.0.0.0/8和192.168.0.0/24 网段内)不会被限制。其它所有IP地址都会被限制到每秒5个请求。
limit_req
指令将限制应用到/的location块,允许在配置的限制上最多超过10个数据包的突发,并且不会延迟转发。
location包含多limit_req
指令
我们可以在一个location块中配置多个limit_req
指令。符合给定请求的所有限制都被应用时,意味着将采用最严格的那个限制。例如,多个指令都制定了延迟,将采用最长的那个延迟。同样,请求受部分指令影响被拒绝,即使其他指令允许通过也无济于事。
扩展前面将“流量限制”应用到白名单内IP地址的例子:
http {
# ... limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s; server {
# ...
location / {
limit_req zone=req_zone burst=10 nodelay;
limit_req zone=req_zone_wl burst=20 nodelay;
# ...
}
}
}
白名单内的IP地址不会匹配到第一个“流量限制”,而是会匹配到第二个req_zone_wl
,并且被限制到每秒15个请求。不在白名单内的IP地址两个限制能匹配到,所以应用限制更强的那个:每秒5个请求。
配置相关功能
日志记录
默认情况下,Nginx会在日志中记录由于流量限制而延迟或丢弃的请求,如下所示:
2015/06/13 04:20:00 [error] 120315#0: *32086 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.2, server: nginx.com,
request: "GET / HTTP/1.0", host: "nginx.com"
日志条目中包含的字段:
- limiting requests - 表明日志条目记录的是被“流量限制”请求
- excess - 每毫秒超过对应“流量限制”配置的请求数量
- zone - 定义实施“流量限制”的区域
- client - 发起请求的客户端IP地址
- server - 服务器IP地址或主机名
- request - 客户端发起的实际HTTP请求
- host - HTTP报头中host的值
默认情况下,Nginx以error
级别来记录被拒绝的请求,如上面示例中的[error]
所示(Ngin以较低级别记录延时请求,一般是info
级别)。如要更改Nginx的日志记录级别,需要使用limit_req_log_level
指令。这里,我们将被拒绝请求的日志记录级别设置为warn
:
location /login/ {
limit_req zone=mylimit burst=20 nodelay;
limit_req_log_level warn; proxy_pass http://my_upstream;
}
发送到客户端的错误代码
一般情况下,客户端超过配置的流量限制时,Nginx响应状态码为503(Service Temporarily Unavailable)。可以使用limit_req_status
指令来设置为其它状态码(例如下面的444状态码):
location /login/ {
limit_req zone=mylimit burst=20 nodelay;
limit_req_status 444;
}
指定location
拒绝所有请求
如果你想拒绝某个指定URL地址的所有请求,而不是仅仅对其限速,只需要在location
块中配置deny
all指令:
location /foo.php {
deny all;
}
总结
前文已经涵盖了Nginx和Nginx Plus提供的“流量限制”的很多功能,包括为HTTP请求的不同loation设置请求速率,给“流量限制”配置burst
和nodelay
参数。还涵盖了针对客户端IP地址的白名单和黑名单应用不同“流量限制”的高级配置,阐述了如何去日志记录被拒绝和延时的请求。
转自:https://legolasng.github.io/2017/08/27/nginx-rate-limiting/#%E6%80%BB%E7%BB%93
https://www.nginx.com/blog/rate-limiting-nginx/
Nginx流控的更多相关文章
- 使用 NGINX 流控和 fail2ban 防止 CC 攻击
背景知识 CC 攻击 攻击者通过创建大量请求导致服务器资源耗尽,主要针对特定服务接口,属于实现 DoS 攻击的一种方式(DoS 攻击更多是针对网络端口,而不是具体服务接口). NGINX 流控 lim ...
- 统一流控服务开源-1:场景&业界做法&算法篇
最近团队在搞流量安全控制,为了应对不断增大的流量安全风险.Waf防护能做一下接入端的拦截,但是实际流量会打到整个分布式系统的每一环:Nginx.API网关.RPC服务.MQ消息应用中心.数据库.瞬间的 ...
- 又拍云张聪:OpenResty 动态流控的几种姿势
2019 年 1 月 12 日,由又拍云.OpenResty 中国社区主办的 OpenResty × Open Talk 全国巡回沙龙·深圳站圆满结束,又拍云首席架构师张聪在活动上做了< Ope ...
- 性能百万/s:腾讯轻量级全局流控方案详解【转自Wetest】
阿里用的方案是在nginx中配置限流(限流功能模块是自己开发的),流量统计线上是有监控打通的,具体的限流值是通过线上流量表现+线下性能测试(模拟线上场景)测试得出的. 全新的全局流控实现方案,既解决了 ...
- 开发者说:Sentinel 流控功能在 SpringMVC/SpringBoot 上的实践
从用户的视角来感受一个开源项目的成长,是我们推出「开发者说」专栏的初衷,即在开发者进行开源项目选型时,提供更为立体的项目信息.专栏所有内容均来自作者原创/投稿,本文是「开发者说」的第6篇,作者 Jas ...
- 详解API Gateway流控实现,揭开ROMA平台高性能秒级流控的技术细节
摘要:ROMA平台的核心系统ROMA Connect源自华为流程IT的集成平台,在华为内部有超过15年的企业业务集成经验. 本文分享自华为云社区<ROMA集成关键技术(1)-API流控技术详解& ...
- 详解ROMA Connect API 流控实现技术
摘要:本文将详细描述API Gateway流控实现,揭开高性能秒级流控的技术细节. 1.概述 ROMA平台的核心系统ROMA Connect源自华为流程IT的集成平台,在华为内部有超过15年的企业业务 ...
- [RouterOS] ROS对接碧海威或PA等流控实现完美流控详细教程(附脚本全免费)
前言: 经常在群里看到不少朋友争论海蜘蛛 ROS 维盟 爱快 碧海威 流控大师 Woyos等等软路由,哪个好.实际上,网络产品是复杂的,现在的软路由功能上已经远远不是单独的路由了.每种产品都有他本身的 ...
- rabbimq之流控
rabbitmq为了能够保证服务器在大量使用资源的情况下正常工作,会做流控. 所谓流控有以下两个方面.一是针对连接做流控,即降低某频率过快的发送消息.二是整体流控,即将所有消费者发送的消息丢掉,悄无声 ...
随机推荐
- spring事务什么时候会自动回滚
在java中异常的基类为Throwable,他有两个子类xception与Errors.同时RuntimeException就是Exception的子类,只有RuntimeException才会进行回 ...
- 【转】三个重复的ACK意味着发生拥塞?
三次重复的ACK,可能是丢包引起的,丢包可能是网络拥塞造成的,也可能是信号失真造成的. 三次重复的ACK,也有可能是乱序引起的,而乱序和网络拥塞没有直接关系. 如果就写这两行,感觉什么都没写,接下来的 ...
- c++第五次作业
运算符重载 一.运算符重载基本规则 1.运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据时导致不同的行为. 2.一般来讲,重载的功能应当与原有功能相似,不能改变原运算符的操作 ...
- 初学Django基础01 建立工程,目录结构,常用配置,上下文管理,模板渲染
django是python的web重量级框架,发展已经有10年多了,对应下面版本 Django 版本 Python 版本 1.8 2.7, 3.2 , 3.3, 3.4, 3.5 1.9, 1.10 ...
- SQL 语句中关于 NULL 的那些坑
问题描述 今天在跟进公司内部测试平台线上问题的时候,发现一个忽略已久的问题. 为了简化问题描述,将其进行了抽象. 有一张数据表qms_branch,里面包含了一批形式如下所示的数据: id name ...
- 配置jdk和环境变量
1.官网下载jdk1.8,默认安装即可 2.JAVE_HOME:jdk安装目录 path:C:;%JAVA_HOME%\bin; C:;%JAVA_HONE%\jre\bin;(当dos界面输入命令 ...
- Vue 组件生命周期钩子
Vue 组件生命周期钩子 # 1)一个组件从创建到销毁的整个过程,就称之为组件的生命周期 # 2)在组件创建到销毁的过程中,会出现众多关键的时间节点, 如: 组件要创建了.组件创建完毕了.组件数据渲染 ...
- Nutch2.1+solr3.6.1+mysql5.6问题
1.Nutch2.1问题 1.1 问题:导入完成后,Nutch2.1里面runtime仍旧不能运行,出现jobfailed等错误. 解决:runtime里的nutch调试过程和导入Eclipse差不多 ...
- 对日开发中 PG , PL , SE , PM 是什么
PG(ProGramer)指程序员. 这类人才在企业中所占数量最多,通常占到整个项目员工数的70%,也是企业中最紧缺的一类职位,一般为具有专业知识的软件工程技术人员. PL(project leade ...
- XAMPP环境搭建WordPress,DVWA
本周学习内容: 1.学习MySQL数据库.Linux.PHP开发: 2.复习等级培训内容: 3.使用xampp环境安装WordPress,学习WordPress数据库表的设计: 4.使用xampp安装 ...