nginx+lua+redis防刷,lua代码
来源:互联网 发布:缩鼻翼手术多少钱知乎 编辑:程序博客网 时间:2024/05/12 23:41
问题:
当机器过多时,在每台机器的nginx上用nginx自带防刷模块,往往限制太松。
思路:
多台机器,通过nginx-lua模块,连接redis,以ip(记得nginx安装real-ip模块,取到x-forword-for字段对应的真实用户ip)来更新访问次数,并根据redis设置的阀值进行比较,决定是否限流,不考虑并发更新丢值情况,因为访问次数足够时,总能到达阀值。
优化:
1、redis发送请求,需要查访问次数和阀值,发两次请求浪费资源,可进行合并。
通过eval指令,执行redis-lua脚本,一次性返回两个字段。
2、每次发送redis执行的lua脚本,传输字符较多,浪费带宽。
通过evalsha执行脚本对应的校验码。若redis执行过一个lua脚本后,会记录脚本,并生成对应的校验码,可通过evalsha指令,参数为对应的校验码,即可执行脚本。
3、(不采用)建立nginx到redis的连接池,防止每次都建立链接。但考虑到以下两点,所以放弃使用,如有错误和方案,麻烦指正。
(1)、连接池不应该很大,所以在并发量很大的时候,大多数请求还是要从新建立连接。
(2)、要维护连接池,保证同一时刻没有两个请求共用一个链接,造成连接关闭的异常。没有一个变量,用来标识调用不同的连接。
lua代码(可直接使用,亲测可用):
首先需要在nginx上输入下面的配置:
lua_shared_dict ngx_shared_redis 1m;
建立一个从nginx启动后便存在的共享变量,1m大小(可以小点),名字叫redis。用于存储生成的redis-lua叫的校验码,用ngx.shared.redis:get("redis")方式来获取。
以上若有错误,欢迎指正,谢谢!
当机器过多时,在每台机器的nginx上用nginx自带防刷模块,往往限制太松。
思路:
多台机器,通过nginx-lua模块,连接redis,以ip(记得nginx安装real-ip模块,取到x-forword-for字段对应的真实用户ip)来更新访问次数,并根据redis设置的阀值进行比较,决定是否限流,不考虑并发更新丢值情况,因为访问次数足够时,总能到达阀值。
优化:
1、redis发送请求,需要查访问次数和阀值,发两次请求浪费资源,可进行合并。
通过eval指令,执行redis-lua脚本,一次性返回两个字段。
2、每次发送redis执行的lua脚本,传输字符较多,浪费带宽。
通过evalsha执行脚本对应的校验码。若redis执行过一个lua脚本后,会记录脚本,并生成对应的校验码,可通过evalsha指令,参数为对应的校验码,即可执行脚本。
3、(不采用)建立nginx到redis的连接池,防止每次都建立链接。但考虑到以下两点,所以放弃使用,如有错误和方案,麻烦指正。
(1)、连接池不应该很大,所以在并发量很大的时候,大多数请求还是要从新建立连接。
(2)、要维护连接池,保证同一时刻没有两个请求共用一个链接,造成连接关闭的异常。没有一个变量,用来标识调用不同的连接。
lua代码(可直接使用,亲测可用):
首先需要在nginx上输入下面的配置:
lua_shared_dict ngx_shared_redis 1m;
建立一个从nginx启动后便存在的共享变量,1m大小(可以小点),名字叫redis。用于存储生成的redis-lua叫的校验码,用ngx.shared.redis:get("redis")方式来获取。
--关闭连接local function close_redis(red) if not red then return end red:close()end--主要处理函数local function get_limit() --加载模块 local red = require("resty.redis"):new() red:set_timeout(100) local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.log(ngx.ERR, "redis_connect:"..err) return close_redis(red) end --根据ip作为key,来防刷 local key = ngx.var.remote_addr --判断之前是否已经执行过redis-lua脚本,且有对应的校验码值用于调用 if not ngx.shared.redis:get("redis") then --若之前未执行过,第一次执行 local script = table.concat({ --手动拼接为一行,两个参数,第一个为key即ip,第二个为限制阀值key "local val = redis.call('get',KEYS[1]) ", "if val then ", "val = redis.call('incr',KEYS[1]) ", "return {val,redis.call('get', KEYS[2])} ", "end ", "redis.call('set',KEYS[1],1) ", "redis.call('expire',KEYS[1],60) ", "return {0,nil}" }) --在redis加载对应的lua脚本 local sha1, err = red:script("load", script) if not sha1 then ngx.log(ngx.ERR, "load_script:"..err) return close_redis(red) end --拿到生成的校验码,更新对应的字段 ngx.shared.redis:set("redis", sha1) end --根据校验码,执行脚本,cart_limit为限制阀值key local resp, err = red:evalsha(ngx.shared.redis:get("redis"), 2, key, "cart_limit"); if not resp then --若执行失败,一种情况为,redis清空了脚本缓存,此情况下,退出并删除存储的校验码,等下次执行,再更新 ngx.log(ngx.ERR, "not_resp:"..err) ngx.shared.redis:delete("redis") return close_redis(red) end --限制值若redis没有,默认为1200,若有则判断是否大于120 local limit = 1200 if resp[2] then local new_limit = tonumber(resp[2]) limit = new_limit > 120 and new_limit or 1200 end --若次数超过限制,则拦截 if resp[1] > limit then ngx.exit(ngx.HTTP_FORBIDDEN) end close_redis(red)end--用pcall,调用脚本,类似try-catch,如有错误,打印脚本if not pcall(get_limit) then ngx.log(ngx.ERR, "lua error")end
以上若有错误,欢迎指正,谢谢!
阅读全文
0 0
- nginx+lua+redis防刷,lua代码
- nginx + lua + redis 防刷和限流
- redis+lua实现防刷
- nginx+lua+redis实现验证码防采集
- nginx+lua+redis
- Nginx+Lua+Redis实例
- nginx+lua+redis 使用方法
- nginx+lua+redis安装
- nginx+lua+redis搭建
- nginx+lua+redis
- Nginx+Lua访问Redis
- nginx + lua + redis
- Nginx + Lua + Redis
- nginx+lua+redis使用
- nginx lua redis 测试
- Lua:Nginx Lua环境配置,第一个Nginx Lua代码
- Nginx + Lua + redis (一)
- nginx+lua+redis(openresty)配置
- VS 破解
- 麦子学院C++学习笔记
- 回合制游戏指令的执行机制
- css 属性选择器学习+css3 混合模式+css background
- day1
- nginx+lua+redis防刷,lua代码
- opencv3/C++霍夫圆/直线检测
- Android_黑马视频学习_day04
- 感知机
- echarts -- 使用 formatter 修改鼠标悬浮事件信息
- 浅析前端工程化
- 2017年个人总结-程序员的中年焦虑症
- disruptor源码解读
- 站在巨人的肩膀上