OpenResty之 limit.count 模块

原文: lua-resty-limit-traffic/lib/resty/limit/count.md

1. 示例

http {
    lua_shared_dict my_limit_count_store 100m;

    init_by_lua_block {
        require "resty.core"
    }

    server {
        location / {
            access_by_lua_block {
                local limit_count = require "resty.limit.count"

                -- rate: 5000 requests per 3600s
                local lim, err = limit_count.new("my_limit_count_store", 5000, 3600)
                if not lim then
                    ngx.log(ngx.ERR, "failed to instantiate a resty.limit.coutn object: ", err)
                    return ngx.exit(500)
                end

                -- use the Authorization header as the limiting key
                local key = ngx.req.get_headers()["Authorization"] or "public"
                local delay, err = lim:incoming(key, true)

                if not delay then
                    if err == "rejected" then
                        ngx.header["X-RateLimit-Limit"] = "5000"
                        ngx.header["X-RateLimit-Remaining"] = 0
                        return ngx.exit(503)
                    end
                    ngx.log(ngx.ERR, "failed to limit count: ", err)
                    return ngx.exit(500)
                end

                -- the 2nd return value holds the current remaing number
                -- of requests for the specified key.
                local remaining = err

                ngx.header["X-RateLimit-Limit"] = "5000"
                ngx.header["X-RateLimit-Remaining"] = remaining
            }
        }
    }
}

注: 该模块依赖 lua-resty-core,因此需要:

init_by_lua_block {
    require "resty.core"
}

2. 方法

2.1 new

syntax: obj, err = class.new(shdict_name, count, time_window)

实例化 class 的对象,该 class 通过 require "resty.limit.count" 返回。

该 new 方法携带的参数如下:

  • shdict_name: lua_shared_dict 声明的共享内存的名称。建议对不同的限制使用独立的共享内存。
  • count:指定的请求阈值。
  • time_window: 请求个数复位前的窗口时间,以秒为单位。

new 实现如下

local ngx_shared = ngx.shared
local setmetatable = setmetatable
local assert = assert

local _M = {
    _VERSION = ‘0.05‘
}

local mt = {
    __index = _M
}

-- the "limit" argument controls number of request allowed in a time window.
-- time "window" argument controls the time window in seconds.
function _M.new(dict_name, limit, window)
    local dict = ngx_shared[dict_name]
    if not dict then
        return nil, "shared dict not found"
    end

    assert(limit> 0 and window > 0)

    local self = {
        dict = dict,
        limit = limit,
        window = window,
    }

    return setmetatable(self, mt)
end

2.2 incoming

syntax: delay, err = obj:incoming(key, commit)

触发新请求传入事件并计算当前请求对指定 key 所需的 delay(如果有的话),或者是否立即拒绝该请求。

该方法接受如下参数:

  • key: 是用户指定限制速率的 key。

    例如,可以使用 host 或 server zone 作为 key,以便限制每个 host 的速率。此外,也可以使用 Authorization 头部值作为 key,以便可以为个人用户限制速率。

    注意该模块没有为该 key 加前缀或后缀来标志该 key,因此用户需要确保该 key 在 lua_shared_dict 共享内存中是唯一的。

  • commit:布尔值。如果设置为 true,则 obj 将会在支持该 obj 的共享内存中记录该事件;否则仅仅是 "dry run"。

该 incoming 方法的放回值依赖如下情况:

  1. 如果请求数没有超过在 new 方法中设置的 count 值,那么该 incoming 返回 0 作为 delay,并将当前时间内余下允许请求的个数作为第二个值返回。
  2. 如果请求数超过了 count 限制,则返回 nil 和错误字符串 "rejected"。
  3. 如果发生错误(如访问共享内存失败),则该方法返回 nil 和错误描述字符串。

incoming 实现如下

function _M.incoming(self, key, commit)
    local dict = self.dict
    local limit = self.limit
    local window = self.window

    local remaining, ok, err

    if commit then
        remaining, err = dict:incr(key, -1, limit)
        if not remaining then
            return nil, err
        end

        if remaining == limit - 1 then
            ok, err = dict:expire(key, window)
            if not ok then
                if err == "not found" then
                    remaining, err = dict:incr(key, -1, limit)
                    if not remaining then
                        return nil, err
                    end

                    ok, err = dict:expire(key, window)
                    if not ok then
                        return nil, err
                    end

                else
                    return nil, err
                end
            end
        end

    else
        remaining = (dict:get(key) or limit) - 1
    end

    if remaining < 0 then
        return nil, "rejected"
    end

    return 0, remaining
end

原文地址:https://www.cnblogs.com/jimodetiantang/p/9408734.html

时间: 2024-10-22 12:51:07

OpenResty之 limit.count 模块的相关文章

OpenResty之resty.limit.count 模块介绍

resty.limit.count 模块介绍: resty.limit.count 模块就是限制接口单位时间的请求数,This module depends on lua-resty-core模块,所以要在openresty 的http标签端添加 nginxinit_by_lua_block {require "resty.core"} 同时resty.limit.count模块是在OpenResty 1.13.6.1+ 引入的 openresty下开启resty.limit.coun

openresty 之resty.limit.req模块介绍

一.openresty实现限流说明: 静态拦截和动态拦截介绍:静态拦截就是限流某一个接口在一定时间单位的请求数.一般就是单位1s内的客户端的请求数.例如用户可以在系统上给他们的接口配置一个每秒最大调用量,如果超过这个限制,则拒绝服务此接口.而动态拦截其实也是基于静态拦截进行改进,我们可以依据当前系统的响应时间来动态调整限流的阈值,如果响应较快则可以把阈值调的大一些,放过更多请求,反之则自动降低限流阈值,只使少量请求通过. 其实这就是一个很简单的限流方式.但是因为这些场景在我们开发的时候经常遇到,

Nginx反向代理前端limit限速模块总结

关于ngx_http_limit_conn_module.ngx_http_limit_req_module 模块,echo(需要安装第三方模块 ngx_http_echo_module),map(默认安装ngx_http_map_module),geo(默认安装 ngx_http_geo_module)指令请查看官方文档,这里不再赘述. 有四种情况: 一,不过CDN限速配置 二,过CDN限速配置 三,不用白名单的不过CDN 四,不用白名单的过CDN 首先说明一个问题: geo 里面的IP可以是

从构建分布式秒杀系统聊聊限流的多种实现

前言 俗话说的好,冰冻三尺非一日之寒,滴水穿石非一日之功,罗马也不是一天就建成的.两周前秒杀案例初步成型,分享到了中国最大的同×××友网站-码云.同时也收到了不少小伙伴的建议和投诉.我从不认为分布式.集群.秒杀这些就应该是大厂的专利,在互联网的今天无论什么时候都要时刻武装自己,只有这样,也许你的春天就在明天. 在开发秒杀系统案例的过程中,前面主要分享了队列.缓存.锁和分布式锁以及静态化等等.缓存的目的是为了提升系统访问速度和增强系统的处理能力:分布式锁解决了集群下数据的安全一致性问题:静态化无疑

从构建分布式秒杀系统聊聊限流特技

前言 俗话说的好,冰冻三尺非一日之寒,滴水穿石非一日之功,罗马也不是一天就建成的.两周前秒杀案例初步成型,分享到了中国最大的同性交友网站-码云.同时也收到了不少小伙伴的建议和投诉.我从不认为分布式.集群.秒杀这些就应该是大厂的专利,在互联网的今天无论什么时候都要时刻武装自己,只有这样,也许你的春天就在明天. 在开发秒杀系统案例的过程中,前面主要分享了队列.缓存.锁和分布式锁以及静态化等等.缓存的目的是为了提升系统访问速度和增强系统的处理能力:分布式锁解决了集群下数据的安全一致性问题:静态化无疑是

从SpringBoot构建十万博文聊聊限流特技

前言 在开发十万博客系统的的过程中,前面主要分享了爬虫.缓存穿透以及文章阅读量计数等等.爬虫的目的就是解决十万+问题:缓存穿透是为了保护后端数据库查询服务:计数服务解决了接近真实阅读数以及数据库服务的压力. 架构图 限流 就拿十万博客来说,如果存在热点文章,可能会有数十万级别的并发用户参与阅读.如果想让这些用户正常访问,无非就是加机器横向扩展各种服务,但凡事都有一个利益平衡点,有时候只需要少量的机器保证大部分用户在大部分时间可以正常访问即可. 亦或是,如果存在大量爬虫或者恶意攻击,我们必须采取一

Android xUtils3源码解析之图片模块

初始化 x.Ext.init(this); public static void init(Application app) { TaskControllerImpl.registerInstance(); if (Ext.app == null) { Ext.app = app; } } public final class TaskControllerImpl implements TaskController { public static void registerInstance()

centos yum 安装openresty

yum 安装openresty sudo yum install yum-utils -y sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo sudo yum install openresty -y 到此,openresty就安装好了 如果想安装openresty调试工具 可以sudo yum install openresty-resty 用如下命令查看安装了哪些ope

openresty+lua在反向代理服务中的玩法

openresty+lua在反向代理服务中的玩法 phith0n · 2015/06/02 10:35 0x01 起因 几天前学弟给我介绍他用nginx搭建的反代,代理了谷歌和维基百科. 由此我想到了一些邪恶的东西:反代既然是所有流量走我的服务器,那我是不是能够在中途做些手脚,达到一些有趣的目的. openresty是一款结合了nginx和lua的全功能web服务器,我感觉其角色和tornado类似,既是一个中间件,也结合了一个后端解释器.所以,我们可以在nginx上用lua开发很多“有趣”的东