使用 Nginx 限流

一、简介

Nginx不仅可以做Web服务器、做反向代理、负载均衡,还可以做限流系统。此处我们就Nginx为例,介绍一下如何配置一个限流系统。

Nginx使用的限流算法是漏桶算法。

(1)安装Nginx。
如果你的Linux是Ubuntu或Debian,使用apt-get安装,在命令行中输入以下命令:

$ sudo apt-get update
$ sudo apt-get install nginx

如果是CentOS,使用yum安装,在命令行中输入以下命令:

$ sudo yum install epel-release
$ sudo yum update
$ sudo yum install nginx

(2)找到Nginx所使用的配置文件所在的位置。在Ubuntu和Debian是在如下位置:

$ cd /etc/nginx/sites-available/

而CentOS则是在如下位置:

$ cd /etc/nginx/conf.d/

(3)在http块中,配置基础的限流配置:

 http
{     limit_req_zone$binary_remote_addr zone=mylimit:10m rate=10r/s;

     server {
         location /test/ {
             limit_reqzone=mylimit;

             proxy_passhttp://backend;
         }
     }
 }

其中4到8行定义的是一个服务器接口。而第2行和第6行配合完成了一个限流设置,下面解释一下这两行做的事情:

limit_req_zone命令在Nginx的配置文件中专门用于定义限流,它必须被放在http块中,否则无法生效,因为该命令只在http中被定义。

该字段包含三个参数:

第一个参数,就是键(key),即值$binary_remote_addr所在的位置,它代表的是我们的限流系统限制请求所用的键。

此处,我们使用了$binary_remote_addr,它是Nginx内置的一个值,代表的是客户端的IP地址的二进制表示。因此换言之,我们的示例配置,是希望限流系统以客户端的IP地址为键进行限流。

对Nginx有经验的读者可能还知道有一个Nginx内置值为binary_remote_addr是Nginx的社区推荐用值,因为它是二进制表达,占用的空间一般比字符串表达的$remote_addr要短一些,在寸土寸金的限流系统中尤为重要。

第二个参数是限流配置的共享内存占用(zone)。为了性能优势,Nginx将限流配置放在共享内存中,供所有Nginx的进程使用,因为它占用的是内存,所以我们希望开发者能够指定一个合理的、既不浪费又能存储足够信息的空间大小。根据实践经验,1MB的空间可以储存16000个IP地址。

该参数的语法是用冒号隔开的两个部分,第一部分是给该部分申请的内存一个名字,第二部分是我们希望申请的内存大小。

因此,在该声明中,我们声明了一个名叫mylimit(我的限制)的内存空间,然后它的大小是10M,即可以存储160000个IP地址,对于实验来说足够了。

第三个配置就是访问速率(rate)了,格式是用左斜杠隔开的请求数和时间单位。这里的访问速率就是最大速率,因此10r/s就是每秒10个请求。通过这台Nginx服务器访问后端服务器的请求速率无法超过每秒10个请求。

注意到第5行声明了一个资源位置/test/,因此我们第6行的配置就是针对这个资源的,通俗地说,我们在第6行的配置是针对特定API的,这个API就是路径为/test/的API,而其真正路径就是第8行声明的http://backend。注意,这个URL是不存在的,实际操作中,读者需要将它换成你已经开发好的业务逻辑所在的位置,Nginx在这里的作用只是一个反向代理,它自己本身没有资源。

第6行中,我们使用limit_req命令,声明该API需要一个限流配置,而该限流配置所在位置(zone)就是mylimit。

这样一来,所有发往该API的请求会先读到第6行的限流配置,然后根据该限流配置mylimit的名称找到声明在第2行的参数,然后决定该请求是否应该被拒绝。

但是这样还不够。不要忘了,Nginx使用的漏桶算法,不是时间窗口算法,我们前文介绍中说过,漏桶算法是有两个参数可以配置的!

(4)配置峰值。Nginx漏桶算法的峰值属性在API中设置。参数名为burst。如下:

 http {
     limit_req_zone$binary_remote_addr zone=mylimit:10m rate=10r/s;

     server {
         location /test/ {
             limit_reqzone=mylimit burst=20;

             proxy_passhttp://backend;
         }
     }
 }

在第6行中,我们只需要在声明limit_req的同时,指定burst就可以了,此处我们指定burst为20,即漏桶算法中我们的“桶”最多可以接受20个请求。

这样一个Nginx的限流系统就配置完毕了,但实际操作中,我们还可能需要很多别的功能,下面笔者就介绍几个很有用的配置技巧。

二、加快Nginx转发速度

相对于传统的漏桶算法慢吞吞地转发请求的缺陷,Nginx实现了一种漏桶算法的优化版,允许开发者指定快速转发,而且还不影响正常的限流功能。开发者只需要在指定limit_req的一行中指定burst之后指定另一个参数nodelay,就可以在请求总数没有超过burst指定值的情况下,迅速转发所有请求了。如下所示:

 http {
     limit_req_zone$binary_remote_addr zone=mylimit:10m rate=10r/s;

     server {
         location /test/ {
             limit_reqzone=mylimit burst=20 nodelay;

             proxy_passhttp://backend;
         }
     }
 }

您可能会担忧:这种情况下,会不会出现所有请求都被快速转发,然后接下来又有没有超过burst数量的请求出现,再次被快速转发,就好像固定窗口算法的漏洞一样,从而超过我们本来希望它能限制到的上限数量呢?答案是不会。Nginx的快速转发是这样实现的:

  • 当有没有超过burst上限的请求数量进入系统时,快速转发,然后将当前桶中可以填充的数量标为0;

  • 按照我们设置的rate在1秒中内缓慢增加桶中余额,以我们的配置为例,每隔100毫秒,增加1个空位;

  • 在增加空位的过程中,进来超过空位数量的请求,直接拒绝。

举例而言,配置如上所示,假如在某个瞬时有25个请求进入系统,Nginx会先转发20个(或21个,取决于瞬时情况),然后拒绝剩下的4个请求,并将当前桶中数量标为0,然后接下来的每100毫秒,缓慢恢复1个空位。

这样我们可以看到,Nginx既做到了快速转发消息,又不会让后端服务器承担过多的流量。

三、 为限流系统配置日志级别

限流系统会提前拒绝请求,因此,我们在业务服务器上是肯定看不到这些请求的。假如我们收到一个报告说某用户在使用网站的时候出现错误,但是我们在业务服务器上又找不到相关的日志,我们如何确定是不是限流造成的呢?

只有限流系统的日志才能说明问题。因此,我们需要Nginx打印出它拒绝掉的请求的信息。但同时,Nginx打印的限流日志默认是错误(error),如果我们设置了一个基于日志错误扫描的警报,它扫到的限流错误,真的是我们希望给自己发警报的情况吗?

配置请求的位置就在资源中,使用的命令是limit_req_log_level,如下:

 http {
     limit_req_zone$binary_remote_addr zone=mylimit:10m rate=10r/s;

     server {
         location /test/ {
             limit_reqzone=mylimit burst=20;
             limit_req_log_level warn;

             proxy_passhttp://backend;
         }
     }
 }

在第7行中,我们将Nginx的日志改为了警告(warn)。

四、修改Nginx的限流响应状态码

限流的HTTP标准响应状态码是429,但是如果读者拿上述的配置文件直接去测试,会发现Nginx返回的是503(服务不可用)。到底应该返回什么状态码,是一个偏程序哲学的问题,此处我们不讨论,我们只讨论:如何让Nginx返回我们指定的状态码?

答案也是在同一个资源中,它的配置命令是limit_req_status,然后我们指定它为429即可:

 http {
     limit_req_zone$binary_remote_addr zone=mylimit:10m rate=10r/s;

     server {
         location /test/ {
             limit_reqzone=mylimit burst=20;
            limit_req_log_level warn;
            limit_req_status 429;

             proxy_passhttp://backend;
         }
     }
 }

除了以上功能以外,Nginx还支持很多复杂先进的限流功能,可以访问https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-proxied-http/作进一步的了解。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容