OpenResty是一个基于Nginx与Lua的高性能Web平台,它通过LuaJIT在Nginx中运行高效的Lua脚本和模块,可以用来处理复杂的网络请求,并且支持各种流量控制和限制的功能。

近期研究在OpenResty中如何实现,按QPS、时间范围、来源IP进行限流,以及动态更新限流策略。今天将实现方案分享给大家。

一、在OpenResty中如何实现,按QPS、时间范围、来源IP进行限流

使用OpenResty进行限流的几种常见方法:

  1. 按QPS(每秒查询率)限流:
    使用ngx_http_limit_req_module模块,可以限制每个客户端的请求速率。这个模块使用漏桶算法来控制请求的速率。

    在Nginx配置文件中,你可以这样设置:

    http {
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s; server {
    location / {
    limit_req zone=mylimit burst=5 nodelay;
    }
    }
    }

    上面的配置定义了一个名为mylimit的区域,它根据客户端的IP地址来限流,并且设置了每秒可以处理的请求数(rate)为1。burst参数定义了可以累积的最大请求数,而nodelay表示不对超出速率的请求进行延迟处理。

  2. 按时间范围限流:
    如果你想在特定时间范围内限流,你可能需要编写一些Lua脚本来实现这个逻辑。例如,你可以使用lua-resty-limit-traffic库的limit.req模块,并结合时间判断逻辑:

    local limit_req = require "resty.limit.req"
    local lim, err = limit_req.new("my_limit_req_store", 2, 0)
    if not lim then
    ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
    return ngx.exit(500)
    end local key = ngx.var.binary_remote_addr
    local delay, err = lim:incoming(key, true) -- 检查当前时间是否在限流时间范围内
    local current_hour = os.date("%H")
    if current_hour >= "09" and current_hour <= "18" then
    -- 在工作时间进行限流
    if delay then
    if delay >= 0.001 then
    -- 延迟处理
    ngx.sleep(delay)
    end
    else
    if err == "rejected" then
    -- 请求超出速率限制
    return ngx.exit(503)
    end
    ngx.log(ngx.ERR, "failed to limit req: ", err)
    return ngx.exit(500)
    end
    end
  3. 按来源IP限流:
    使用ngx_http_limit_conn_module模块,可以限制同时处理的连接数。如果你想根据来源IP地址进行限流,可以像这样配置:

    http {
    limit_conn_zone $binary_remote_addr zone=addr:10m; server {
    location / {
    limit_conn addr 3;
    }
    }
    }

    这个配置限制了每个IP地址同时只能有3个活跃连接。

在实际部署时,需要根据自己的业务需求调整这些配置参数。需要注意的是,对于复杂的限流规则,可能需要结合多个Nginx模块和Lua脚本来实现。

而且,由于限流策略可能会影响用户体验,应谨慎设计限流规则,确保它们既能保护后端服务,又不会对合法用户造成不必要的麻烦。

二、限流后提示信息处理和请求状态

在OpenResty中,如果你使用了内置的限流模块(如ngx_http_limit_req_modulengx_http_limit_conn_module)并且请求被限流,你可以通过返回特定的状态码和错误页面来通知用户。

例如,如果使用limit_reqlimit_conn指令,你可以设置返回503状态码(服务不可用),然后定义一个自定义的错误页面:

http {
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=addr:10m; server {
location / {
limit_req zone=one burst=5 nodelay;
limit_conn addr 3; error_page 503 /custom_503.html;
} location = /custom_503.html {
root /path/to/your/error/pages;
internal;
}
}
}

在上面的配置中,当请求被限流并返回503状态码时,Nginx将会发送/path/to/your/error/pages/custom_503.html文件的内容作为响应。

如果你使用Lua脚本来处理限流,你可以更加灵活地设置返回的内容。例如,你可以使用ngx.exit来返回状态码,同时使用ngx.sayngx.send_headers来发送自定义的响应体或者响应头。

access_by_lua_block {
-- 假设你已经进行了一些限流判断...
if should_limit then
ngx.status = ngx.HTTP_SERVICE_UNAVAILABLE
ngx.header.content_type = 'text/html'
ngx.say("<html><body>Sorry, we are currently receiving too many requests. Please try again later.</body></html>")
ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
end
}

在这个Lua代码块中,如果should_limit变量为true,则返回503状态码,并显示一个自定义的HTML错误消息。

要注意的是,返回给用户的信息应该既明确又友好,以确保用户理解为什么他们的请求没有成功,并且知道下一步该做什么。对于API服务,通常返回一个JSON对象,包含错误码和错误信息会更加合适:

access_by_lua_block {
-- 假设你已经进行了一些限流判断...
if should_limit then
ngx.status = ngx.HTTP_TOO_MANY_REQUESTS -- 429 Too Many Requests
ngx.header.content_type = 'application/json'
ngx.say([[{"error": "rate_limit", "error_description": "Too many requests. Please try again later."}]])
ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS)
end
}

在这个例子中,我们使用了429状态码(太多请求),这是一个更具体的状态码,用来表示客户端发送的请求已经超过了服务器愿意处理的频率。

三、如何动态更新限流策略,实时生效,不需要重启Nginx

动态更新限流策略而不重启Nginx服务,可以通过以下几种方式实现:

  1. Lua共享字典(shared dictionaries):
    OpenResty提供了共享内存字典,这是一种在Nginx工作进程之间共享数据的机制。你可以使用共享字典来存储限流配置,并且在Lua代码中动态读取这些配置。这样,当你更新了共享字典中的配置时,不需要重启Nginx,新的请求将会使用新的限流配置。

    例如,你可以定义一个共享字典来存储限流速率:

    http {
    lua_shared_dict my_limit_req_store 10m; init_by_lua_block {
    local dict = ngx.shared.my_limit_req_store
    dict:set("rate", 1) -- 设置每秒请求数为1
    } server {
    location / {
    access_by_lua_block {
    local dict = ngx.shared.my_limit_req_store
    local rate = dict:get("rate") -- 动态获取当前的限流速率
    -- 接下来使用这个rate值来进行限流...
    }
    }
    }
    }

    当你需要更新限流策略时,只需修改共享字典中的值即可。

  2. OpenResty的控制API:
    OpenResty提供了一个控制API,可以用来动态地调整运行时的Nginx配置。这个API可以通过Lua代码来调用,从而实现不重启服务的情况下更新配置。

  3. 外部配置服务:
    你可以将限流配置存储在外部服务中,比如数据库、配置文件或者分布式配置系统(如etcd、Consul)。然后在Nginx的Lua代码中定期轮询这些服务,获取最新的限流配置。

    access_by_lua_block {
    local http = require "resty.http"
    local httpc = http.new()
    local res, err = httpc:request_uri("http://config-service/get_rate_limit", {
    method = "GET",
    headers = {
    ["Content-Type"] = "application/json",
    }
    }) if not res then
    ngx.log(ngx.ERR, "failed to request: ", err)
    return
    end local rate_limit = tonumber(res.body)
    if rate_limit then
    -- 应用新的限流策略...
    end
    }
  4. 信号控制:
    Nginx支持通过信号来进行控制,例如重新加载配置文件(nginx -s reload)。虽然这不是实时的,但是它不需要完全重启Nginx进程,只是重新加载配置文件。如果限流策略是通过Nginx配置文件中的参数来控制的,这是一个可行的方法。

选择哪种方法取决于你的具体需求和环境。如果你需要非常快速地更新配置,并且配置更新操作非常频繁,那么使用Lua共享字典或者外部配置服务可能是更好的选择。如果配置更新不是很频繁,使用信号控制来重新加载Nginx配置可能就足够了。

周国庆

2024/2/25

OpenResty中如何实现,按QPS、时间范围、来源IP进行限流的更多相关文章

  1. ASP.NET Core中如何对不同类型的用户进行区别限流

    老板提出了一个新需求,从某某天起,免费用户每天只能查询100次,收费用户100W次. 这是一个限流问题,聪明的你也一定想到了如何去做:记录用户每一天的查询次数,然后根据当前用户的类型使用不同的数字做比 ...

  2. 构造HTTP请求Header实现"伪造来源IP"

    构造 HTTP请求 Header 实现“伪造来源 IP ” 在阅读本文前,大家要有一个概念,在实现正常的TCP/IP 双方通信情况下,是无法伪造来源 IP 的,也就是说,在 TCP/IP 协议中,可以 ...

  3. 转:造HTTP请求Header实现“伪造来源IP”

    构造 HTTP请求 Header 实现“伪造来源 IP ” 在阅读本文前,大家要有一个概念,在实现正常的TCP/IP 双方通信情况下,是无法伪造来源 IP 的,也就是说,在 TCP/IP 协议中,可以 ...

  4. 构造HTTP请求Header实现“伪造来源IP”(转)

    原文:http://zhangxugg-163-com.iteye.com/blog/1663687 构造 HTTP请求 Header 实现“伪造来源 IP ” 在阅读本文前,大家要有一个概念,在实现 ...

  5. 构造HTTP请求Header实现“伪造来源IP”

    在阅读本文前,大家要有一个概念,在实现正常的TCP/IP 双方通信情况下,是无法伪造来源 IP 的,也就是说,在 TCP/IP 协议中,可以伪造数据包来源 IP ,但这会让发送出去的数据包有去无回,无 ...

  6. 【转】构造HTTP请求Header实现“伪造来源IP”

    构造 HTTP请求 Header 实现“伪造来源 IP ” 在阅读本文前,大家要有一个概念,在实现正常的TCP/IP 双方通信情况下,是无法伪造来源 IP 的,也就是说,在 TCP/IP 协议中,可以 ...

  7. spring中实现基于注解实现动态的接口限流防刷

    本文将介绍在spring项目中自定义注解,借助redis实现接口的限流 自定义注解类 import java.lang.annotation.ElementType; import java.lang ...

  8. ASP.NET Core中使用固定窗口限流

    算法原理 固定窗口算法又称计数器算法,是一种简单的限流算法.在单位时间内设定一个阈值和一个计数值,每收到一个请求则计数值加一,如果计数值超过阈值则触发限流,如果达不到则请求正常处理,进入下一个单位时间 ...

  9. ASP.NET Core中使用滑动窗口限流

    滑动窗口算法用于应对请求在时间周期中分布不均匀的情况,能够更精确的应对流量变化,比较著名的应用场景就是TCP协议的流量控制,不过今天要说的是服务限流场景中的应用. 算法原理 这里假设业务需要每秒钟限流 ...

  10. ASP.NET Core中使用漏桶算法限流

    漏桶算法是限流的四大主流算法之一,其应用场景各种资料中介绍的不多,一般都是说应用在网络流量控制中.这里举两个例子: 1.目前家庭上网都会限制一个固定的带宽,比如100M.200M等,一栋楼有很多的用户 ...

随机推荐

  1. [转帖]Shell三剑客之sed

    目录 Shell三剑客 sed工具 sed 流编辑器的工作过程 sed命令格式与选项操作符 sed命令的常用选项 sed命令的打印功能 默认打印方式 sed命令的寻址打印 文本模式过滤行内容 sed的 ...

  2. JavaScript一种新的数据结构类型Map

    什么是map 它类似于对象,是键值对的集合,但键的范围不局限在于字符串.各种类型的值(包含对象)都可以作为键. 如果同一个键被多次赋值,后面的值将会覆盖其那面的值.如果读取一个未知的键,返回的是und ...

  3. TypeScript接口的讲解-强制约束-可选属性-任意多个属性-只读属性

    接口 接口:可以描述类的一部分抽象行为, 也可以描述数据的结构形状 接口一般首字母大写, 接口中 可以定义为 强制约束 可选属性 只读属性 任意属性 # 强制约束 // 定义接口 interface ...

  4. PLC检测

    填空题1 填空A-E 程序图一 第一训练题的程序,一个是用常开触点,一个是用上升沿指令,建议选上升沿指令编程 程序二 程序图三 程序四 程序五

  5. windows加壳程序WinLicense与Themida

    反调试提示 开了Procmon.exe之后启动游戏会弹出这个框,这个是程序加壳之后的反外挂,反调试提示框. WinLicense A monitor program has been found ru ...

  6. 小白学k8s(4)使用k8s发布go应用

    k8s发布go应用 前言 部署 镜像打包 编写yaml文件 使用ingress 什么是ingress呢 ingress与ingress-controller ingress 部署ingress 配置i ...

  7. C/C++ 搜索缝隙并插入ShellCode

    将ShellCode放入变量中,然后修改插入可执行文件名称,运行后即可将shellCode插入到EXE中,并设置好装载地址,程序运行后会先上线,然后在执行原始的代码,在使用metaspoit生成she ...

  8. 多路io复用epoll [补档-2023-07-20]

    多路io- epoll 4-1简介 ​ 它是linux中内核实现io多路/转接复用的一个实现.(epoll不可跨平台,只能用于Linux)io多路转接是指在同一个操作里,同时监听多个输入输出源,在其中 ...

  9. 月薪10K码农,跳槽到40K架构师,技术学习路线图汇总

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.介绍 Hey there! Roadmap to becoming a web devel ...

  10. 俄大神 lopatkin Windows 精简优化系统 - 工具软件

    昨天有个网友邮件我,说是想找个Tiny7 Rev2的ISO操作系统文件,但是我找了下,以前的那些文件有些已经删除了,所以就在网上搜到了俄大神 lopatkin Windows 精简优化系统,特此放到网 ...