OpenResty使用Lua大全(九)实战:nginx-lua-redis实现访问频率控制

@[TOC]

系列文章索引

OpenResty使用Lua大全(一)Lua语法入门实战 OpenResty使用Lua大全(二)在OpenResty中使用Lua OpenResty使用Lua大全(三)OpenResty使用Json模块解析json OpenResty使用Lua大全(四)OpenResty中使用Redis OpenResty使用Lua大全(五)OpenResty中使用MySQL OpenResty使用Lua大全(六)OpenResty发送http请求 OpenResty使用Lua大全(七)OpenResty使用全局缓存 OpenResty使用Lua大全(八)OpenResty执行流程与阶段详解 OpenResty使用Lua大全(九)实战:nginx-lua-redis实现访问频率控制 OpenResty使用Lua大全(十)实战: Lua + Redis 实现动态封禁 IP OpenResty使用Lua大全(十一)实战: nginx实现接口签名安全认证 OpenResty使用Lua大全(十二)实战: 动手实现一个网关框架

一、需求背景

在高并发场景下为了防止某个访问ip访问的频率过高,有时候会需要控制用户的访问频次 在openresty中,可以找到: set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua等方法。 那么访问控制应该是,access阶段。 我们用Nginx+Lua+Redis来做访问限制主要是考虑到高并发环境下快速访问控制的需求。

二、设计方案

1、预期结果

我们用redis的key表示用户,value表示用户的请求频次,再利用过期时间实现单位时间;

现在我们要求10秒内只能访问10次frequency请求,超过返回403

2、nginx.conf配置

lua 复制代码
location /frequency {
	access_by_lua_file /usr/local/openresty/nginx/conf/access_by_limit_frequency.lua;
	echo "success";
}

3、access_by_limit_frequency.lua

lua 复制代码
local function close_redis(red)  
    if not red then  
        return
    end  
    --释放连接(连接池实现)  
    local pool_max_idle_time = 10000 --毫秒  
    local pool_size = 100 --连接池大小  
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)  
    if not ok then  
        ngx.say("set keepalive error : ", err)  
    end  
end

local function errlog(...)
    ngx.log(ngx.ERR, "redis: ", ...)
end

local redis = require "resty.redis"  --引入redis模块
local red = redis:new()  --创建一个对象,注意是用冒号调用的

--设置超时(毫秒)  
red:set_timeout(1000) 
--建立连接  
local ip = "127.0.0.1"  
local port = 6379
local ok, err = red:connect(ip, port)
if not ok then  
	close_redis(red)
	errlog("Cannot connect");
    return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)   
end  

local key = "limit:frequency:login:"..ngx.var.remote_addr

--得到此客户端IP的频次
local resp, err = red:get(key)
if not resp then  
	close_redis(red)
    return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) --redis 获取值失败
end 

if resp == ngx.null then   
	red:set(key, 1) -- 单位时间 第一次访问
    red:expire(key, 10) --10秒时间 过期
end  

if type(resp) == "string" then 
	if tonumber(resp) > 10 then -- 超过10次
		close_redis(red)
		return ngx.exit(ngx.HTTP_FORBIDDEN) --直接返回403
	end
end

--调用API设置key  
ok, err = red:incr(key)  
if not ok then  
	close_redis(red)
    return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) --redis 报错 
end  

close_redis(red)  

4、测试

请求地址:/frequency

10秒内 超出10次 ,返回403

10秒后,又可以访问了

如果我们想整个网站 都加上这个限制条件,那只要把 access_by_lua_file /usr/local/lua/access_by_limit_frequency.lua; 这个配置,放在server部分,让所有的location 适用就行了

当然这只是简单的限流小实验,使用开源网关可以实现更复杂的限流。

相关推荐
道19931 分钟前
50 台小型无人车与50套穿戴终端 5 公里范围内通信组网方案深度研究
java·后端·struts
间彧8 分钟前
Spring Boot中,拦截器和Spring AOP有什么区别
后端
JaguarJack21 分钟前
PHP 开发者应该理解的 Linux 入门权限指南
后端·php
IT_陈寒21 分钟前
Java性能优化:这5个Spring Boot隐藏技巧让你的应用提速40%
前端·人工智能·后端
程序员爱钓鱼41 分钟前
Go语言实战案例——进阶与部署篇:使用Docker部署Go服务
后端·google·go
你的人类朋友9 小时前
先用js快速开发,后续引入ts是否是一个好的实践?
前端·javascript·后端
码事漫谈10 小时前
医疗设备控制系统中同步与异步通信的架构设计
后端
码事漫谈10 小时前
C++ 中 rfind 方法详解
后端
AAA修煤气灶刘哥11 小时前
服务器指标多到“洪水泛滥”?试试InfluxDB?
数据库·后端·面试