date: 2020-07-18title: nginx限制资源的访问 #标题
tags: nginx #标签
categories: nginx # 分类

此博文记录了nginx如何设置连接请求的最大数量,或从nginx下载内容的最大速率。
参考:官方文档

nginx可以限制哪些?

nginx可以限制:
limit_conn_zone 模块: 限制同一 IP 地址并发连接数;
limit_request 模块: 限制同一 IP 某段时间的访问量;
core 模块提供: limit_rate 限制同一 IP 流量。

注意:客户端很有可能是在NAT设备后面共享IP地址,因此应谨慎使用IP地址限制。如果一定要使用,建议使用 IP + url 的方式进行判断。

限制同一IP并发连接数

  1. http {
  2. .......... # 省略部分内容
  3. limit_conn_zone $binary_remote_addr$uri zone=perip:30m;
  4. limit_conn_status 503;
  5. server {
  6. listen 80;
  7. location / {
  8. root html;
  9. index index.html index.htm;
  10. limit_conn perip 1;
  11. }

指令解释

  • 指令名称:limit_conn_zone

    • 语法: limit_conn_zone key zone=name:size;
    • 默认: no
    • 区域: http
    • 功能: 定义一个zone,用于存储会话的状态。
    • 示例: 上面的示例,就是用客户IP及访问uri作为键值进行存储。键值一样的则被认为是同一个会话。
  • 指令名称:limit_conn

    • 语法:limit_conn zone number;
    • 默认:no
    • 区域:http、server、location
    • 功能:该指令用于为一个会话设定最大并发连接数。如果并发请求超过这个限制,那么将返回预定错误( limit_conn_status )

限制请求速率

速率限制可用于防止DDoS攻击,或防止上游服务器同时被太多请求淹没。该方法基于以下算法:请求以各种速率到达存储桶,并以固定速率离开存储桶。

在使用速率限制之前,需要配置“泄漏桶”的全局参数:

  • 键:用于区分一个客户端与另一个客户端的参数,通常是一个变量。
  • 共享内存区域:保留这些键的状态的区域的名称和大小(“漏斗”)。
  • rate:以每秒请求数(r/s)或每分钟请求数(r/m)指定的请求速率限制(“漏斗排放”)。每分钟请求数用于指定小于每秒一个请求的速率。

这些参数是通过limit_req_zone指令设置的。定义在http{}字段中。

limit_req_zone被设置后,我们可以在nginx上需要限制的地方指定limit_req,比如:server{}、location{}以及http{}。

  1. http {
  2. .................
  3. limit_req_zone $binary_remote_addr zone=limit_html:10m rate=1r/s;
  4. ................
  5. server {
  6. .................
  7. location / {
  8. limit_req zone=limit_html burst=5 delay=3;
  9. }
  10. .................
  11. }
  12. }

上述部分配置解释:

  • burst:当前请求已经超过了rate限制的链接数,但是nignx不会直接拒绝,而是放入队列进行排队,如上述指定了burst=5,表示队列的大小为5个请求,如果此时同一个客户端进来10个请求,那么只有5个请求可以按每秒一个的频率被处理,剩下五个将会拒绝处理,返回503状态码。
  • delay:由于上述指定的burst是将队列的大小设置了为5,但客户端请求的一个页面上可能会访问到nginx多次,那这样的话,假设客户端要的资源是访问nignx 3次才能获取到完整内容的,那么整个过程就需要3秒,非常影响客户体验,此时可以通过delay参数来实现不延迟的处理队列中一定数量的请求,在上述配置中,delay=3,表示会立即处理队列中的3个请求,而不用每秒一个的去处理。如果我们想让队列中的所有连接都无需延迟,那么可以直接设置为nodelay即可。

上述配置,将创建名为limit_html,大小为10MB的共享内存区域,该区域保留使用nginx限制资源的访问 - 图1binary_remote_addr以二进制的方式保存IP地址,比较省内存空间。

$binary_remote_addr对于IPv4地址,值的大小为4个字节,在64位平台上,存储状态占128个字节。因此,大约16000个IP地址的状态信息占用该区域的1MB。

如果NGINX需要添加新条目时存储空间已用尽,它将删除最旧的条目。如果释放的空间仍然不足以容纳新记录,则NGINX返回状态码。可以使用伪指令limit_req_status重新定义状态代码,如:limit_req_status 503;

示例摘要

  1. # uri 访问速率限制
  2. # 说明: uri匹配.html结尾, 则赋值变量 $limit_html 为客户端IP+uri
  3. map $uri $limit_html {
  4. ~\.html$ $binary_remote_addr$uri;
  5. }
  6. # 访问 次/秒
  7. # 说明: 结合上面的map, 限制为1次/秒相同 (IP + uri <.html> )
  8. limit_req_zone $limit_html zone=limit_html:50m rate=1r/s;
  9. limit_req_status 503;

限制带宽

如果要限制带宽(是不是想起某盘了),可以使用limit_rate指令。

  1. http {
  2. limit_req_zone $binary_remote_addr zone=limit_html:10m;
  3. server {
  4. location /download {
  5. # auto开头的是配置文件服务器
  6. autoindex on; # 开启索引功能
  7. autoindex_exact_size off; # 关闭计算文件确切大小(单位bytes),只显示大概大小(单位kb、mb、gb)
  8. autoindex_localtime on; # 显示本机时间而非 GMT 时间
  9. # 下面才是带宽限制相关配置
  10. limit_conn limit_html 1; # 限制同一个IP只能建立一个连接
  11. limit_rate_after 100m; # 在100M以后的数据才开始进行速率限制
  12. limit_rate 50k; # 限制速率为50k
  13. }
  14. }
  15. }
  16. $ ll /usr/share/nginx/html/download/ # 文件服务根目录内容如下
  17. 总用量 289532
  18. -rw-r--r-- 1 root root 296477546 5 1 14:24 elasticsearch-7.6.2-linux-x86_64.tar.gz

浏览器下载测试:

nginx限制资源的访问 - 图2

由于我是本地虚拟机做的nginx,所以前100M很快,然后,接下来就一直是四五十kb的样子。

如果你还想再打开个窗口下载此文件,只会看到一个error页面,因为设置了limit_conn limit_html 1;。齐活!