我们知道,在 nginx 的 if 中是不能写 limit_req 和 limit_conn 的。也就是说在 nginx 的配置文件中,我们无法通过 if 对请求参数做逻辑判断,从而实现对复杂请求参数的精准限流。

应用场景:针对某一个流量特别大的入口的某一个特定 GET 请求参数做限流,比如来自微信小程序的 API 请求入口(https://example.com/app/index.php?i=99&v=9.9.9&m=xxxx&from=wxapp),我希望 from = wxapp 时进行限流,若 from 不等于 wxapp 则不进行限流。

一、使用 map 针对请求参数限流

在 nginx 的 http 部分写入:

limit_req_zone $paramFrom zone=from:10m rate=30r/s;
map $arg_from $paramFrom {
  wxapp wxapp;
}

这里的 rate 我设为了每秒处理 30 个请求,如果 limit_req 没有设置 burst,则默认为 0。
rate 支持 r/s 和 r/m 两种模式。

在 server 部分写入:

location ^~ /app/index.php {
  limit_req zone=from;
  #limit_rate 512k;
  
  #宝塔PHP文件处理
  try_files $uri =404;
  fastcgi_pass unix:/tmp/php-cgi-56.sock;
  fastcgi_index index.php;
  include fastcgi.conf;
  include pathinfo.conf;
}

二、关于 Nginx 的 map 模块

使用 map 模块,可以根据一个或多个变量组合成一个新的变量。通过判断新的变量,我们可以处理非常复杂的业务逻辑。本文是以 map 模块用于限流为示例,简单展示一下 map 的使用方法。

三、原理

http_limit_req_module 和 http_limit_conn_module 两个模块是在 nginx 的 preaccess 阶段对请求做拦截,也就是说使用 limit_req_zone 或是 limit_conn_zone 对请求都是可以的。

但两者有本质上的区别,request 和 connection 是不同的,在实际应用中应注意区别。

  • connection 是连接,即常说的 tcp 连接,通过三次握手而建立的。

  • request 是指请求,即 http 请求,(注意,tcp 连接是有状态的,而构建在 tcp 之上的 http 却是无状态的协议)。

limit_req_zone 或是 limit_conn_zone 对请求的限制有效性取决于 key 的设计,通常使用 realip 模块获取到的客户端 IP。但 IP 是针对全体用户做的限流,显然不能满足我们的需求。所以我们需要使用 map 模块来匹配特定的请求参数生成一个变量,将这个变量作为 limit_req_zone 或是 limit_conn_zone 的 key,这样就可以实现我们的需求了。