openresty开发系列37--nginx-lua-redis实现访问频率控制
openresty开发系列37--nginx-lua-redis实现访问频率控制
一)需求背景
在高并发场景下为了防止某个访问ip访问的频率过高,有时候会需要控制用户的访问频次
在openresty中,可以找到:
set_by_lua,rewrite_by_lua,access_by_lua,content_by_lua等方法。
那么访问控制应该是,access阶段。
我们用Nginx+Lua+Redis来做访问限制主要是考虑到高并发环境下快速访问控制的需求。
二)设计方案
我们用redis的key表示用户,value表示用户的请求频次,再利用过期时间实现单位时间;
现在我们要求10秒内只能访问10次frequency请求,超过返回403
1)首先为nginx.conf配置文件,nginx.conf部分内容如下:
location /frequency {
access_by_lua_file /usr/local/lua/access_by_limit_frequency.lua;
echo "访问成功";
}
2)编辑/usr/local/lua/access_by_limit_frequency.lua
- local function close_redis(red)
- if not red then
- return
- end
- --释放连接(连接池实现)
- local pool_max_idle_time = --毫秒
- local pool_size = --连接池大小
- 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()
- --建立连接
- local ip = "10.11.0.215"
- local port =
- 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, ) -- 单位时间 第一次访问
- red:expire(key, ) --10秒时间 过期
- end
- if type(resp) == "string" then
- if tonumber(resp) > 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)
# 当redis设置了密码时,需要用red:auth() 方法进行验证
- # vim /usr/local/lua/access_by_limit_frequency.lua
- local function close_redis(red)
- if not red then
- return
- end
- local pool_max_idle_time =
- local pool_size =
- local ok, err = red:set_keepalive(pool_max_idle_tme, pool_size)
- if not ok then
- ngx.say("set keepalive err : ", err)
- end
- end
- local function errlog(...)
- ngx.log(ngx.ERR, "redis: ", ...)
- end
- local redis = require "resty.redis"
- local red = redis:new()
- red:set_timeout()
- local ip = "10.11.0.215"
- local port =
- local ok,err = red:connect(ip, port)
- if not ok then
- close_redis(red)
- errlog("connot connect");
- return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
- end
- local count, err = red:get_reused_times()
- if == count then ----新建连接,需要认证密码
- ok, err = red:auth("redis123")
- if not ok then
- ngx.say("failed to auth: ", err)
- return
- end
- elseif err then ----从连接池中获取连接,无需再次认证密码
- ngx.say("failed to get reused times: ", err)
- return
- 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)
- end
- if resp == ngx.null then
- red:set(key, )
- red:expire(key, ) -- 设置过期时间,即返回403的时间为100秒
- end
- --ngx.say("connect ok")
- --ngx.say("resp:",resp)
- if type(resp) == "string" then
- if tonumber(resp) > then
- close_redis(red)
- return ngx.exit(ngx.HTTP_FORBIDDEN)
- end
- end
- ok, err = red:incr(key)
- if not ok then
- close_redis(red)
- return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
- end
- close_redis(red)
请求地址:/frequency
10秒内 超出10次 ,返回403
10秒后,又可以访问了
在Nginx需要限速的location中引用上述脚本配置示例:
location /user/ {
set $business "USER";
access_by_lua_file /usr/local/openresty/nginx/conf/lua/access.lua;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://user_224/user/;
}
注:对于有大量静态资源文件(如:js、css、图片等)的前端页面可以设置只有指定格式的请求才进行访问限速,示例代码如下:
location /h5 {
if ($request_uri ~ .*\.(html|htm|jsp|json)) {
set $business "H5";
access_by_lua_file /usr/local/openresty/nginx/conf/lua/access.lua;
}
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://h5_224/h5;
}
如果我们想整个网站 都加上这个限制条件,那只要把
access_by_lua_file /usr/local/lua/access_by_limit_frequency.lua;
这个配置,放在server部分,让所有的location 适用就行了
openresty开发系列37--nginx-lua-redis实现访问频率控制的更多相关文章
- openresty开发系列38--通过Lua+Redis 实现动态封禁IP
openresty开发系列38--通过Lua+Redis 实现动态封禁IP 一)需求背景为了封禁某些爬虫或者恶意用户对服务器的请求,我们需要建立一个动态的 IP 黑名单.对于黑名单之内的 IP ,拒绝 ...
- openresty开发系列27--openresty中封装redis操作
openresty开发系列27--openresty中封装redis操作 在关于web+lua+openresty开发中,项目中会大量操作redis, 重复创建连接-->数据操作-->关闭 ...
- openresty开发系列24--openresty中lua的引入及使用
openresty开发系列24--openresty中lua的引入及使用 openresty 引入 lua 一)openresty中nginx引入lua方式 1)xxx_by_lua ---> ...
- openresty开发系列26--openresty中使用redis模块
openresty开发系列26--openresty中使用redis模块 在一些高并发的场景中,我们常常会用到缓存技术,现在我们常用的分布式缓存redis是最知名的, 操作redis,我们需要引入re ...
- openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息
openresty开发系列40--nginx+lua实现获取客户端ip所在的国家信息 为了实现业务系统针对不同地区IP访问,展示包含不同地区信息的业务交互界面.很多情况下系统需要根据用户访问的IP信息 ...
- openresty开发系列28--openresty中操作mysql
openresty开发系列28--openresty中操作mysql Mysql客户端 应用中最常使用的就是数据库了,尤其mysql数据库,那openresty lua如何操作mysql呢? ...
- openresty开发系列10--openresty的简单介绍及安装
openresty开发系列10--openresty的简单介绍及安装 一.Nginx优点 十几年前,互联网没有这么火,软件外包开发,信息化建设,帮助企业做无纸化办公,收银系统,工厂erp,c/s架构偏 ...
- openresty开发系列36--openresty执行流程之6日志模块处理阶段
openresty开发系列36--openresty执行流程之6日志模块处理阶段 一)header_filter_by_lua 语法:header_filter_by_lua <lua-scri ...
- openresty开发系列35--openresty执行流程之5内容content阶段
openresty开发系列35--openresty执行流程之5内容content阶段 content 阶段 ---init阶段---重写赋值---重写rewrite---access content ...
随机推荐
- 什么影响了mysql的性能-存储引擎层
5.6版本以前默认是MyISam存储引擎,5.6版本之后默认支持的Innodb存储引擎,这两种也是最常用的. 存储引擎层 MyISAM 5.5之前版本默认存储引擎 存储引擎表由MYD和MYI组成 特性 ...
- MySQL/MariaDB数据库的PROXY实现读写分离
MySQL/MariaDB数据库的PROXY实现读写分离 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.ProxySQL概述 1>.各家互联网公司读写分离的解决方案 m ...
- Python的路径操作(os模块与pathlib模块)
Python的路径操作(os模块与pathlib模块) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.os.path模块(Python 3.4版本之前推荐使用该模块) #!/u ...
- MySQL:查询、修改(二)
干货: 使用SELECT查询的基本语句SELECT * FROM <表名>可以查询一个表的所有行和所有列的数据.SELECT查询的结果是一个二维表. 使用SELECT *表示查询表的所有列 ...
- httprunner学习1-环境与登录接口案例
前言 HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架,只需编写维护一份 YAML/JSON 脚本,即可实现自动化测试. 具有以下优点: 继承 Requests 的全部特性,轻松实 ...
- [转载]Linux进程调度原理
[转载]Linux进程调度原理 Linux进程调度原理 Linux进程调度的目标 1.高效性:高效意味着在相同的时间下要完成更多的任务.调度程序会被频繁的执行,所以调度程序要尽可能的高效: 2.加强交 ...
- test201909027 老Z
30+100+40=170.数据出锅*2,也是没谁了. 装饰 快要到 Mope 的生日了,Mope 希望举行一场盛大的生日派对. 派对的准备工作中当然有装饰房间啦.Mope 的房间里有按从左到右的顺序 ...
- NPM——npm|cnpm如何升级
前言 手动更新了node.js版本后,想要升级下npm的版本 步骤 其实无论npm还是cnpm升级的命令都是一样的,除了需要指定包名. 升级npm $ npm install -g npm 查看npm ...
- 2019.12.06 java基础
JRE:运行环境(包含JVM(Java Virtual Machine)- Java虚拟机和核心类库) JDK: JAVA语言的软件开发工具包(Java Development Kit) Dos命令行 ...
- CTF入门(一)
ctf入门指南 如何入门?如何组队? capture the flag 夺旗比赛 类型: Web密码学pwn 程序的逻辑分析,漏洞利用windows.linux.小型机等misc 杂项,隐写,数据还原 ...