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 适用就行了

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

相关推荐
Marktowin4 小时前
Mybatis-Plus更新操作时的一个坑
java·后端
赵文宇4 小时前
CNCF Dragonfly 毕业啦!基于P2P的镜像和文件分发系统快速入门,在线体验
后端
程序员爱钓鱼4 小时前
Node.js 编程实战:即时聊天应用 —— WebSocket 实现实时通信
前端·后端·node.js
Libby博仙5 小时前
Spring Boot 条件化注解深度解析
java·spring boot·后端
源代码•宸5 小时前
Golang原理剖析(Map 源码梳理)
经验分享·后端·算法·leetcode·golang·map
小周在成长5 小时前
动态SQL与MyBatis动态SQL最佳实践
后端
瓦尔登湖懒羊羊6 小时前
TCP的自我介绍
后端
小周在成长6 小时前
MyBatis 动态SQL学习
后端
子非鱼9216 小时前
SpringBoot快速上手
java·spring boot·后端