• 分布式--OpenResty+lua+Redis实现限流与防爬虫


    上篇分布式--OpenResty+lua+Redis中,我们了解了nginx结合lua脚本的强大之处,lua结合反向代理,可以对http请求提前做一些处理,来保证业务服务器的安全性和单一职责原则,以及结合Redis提升读写缓存的效率与持久化能力

    一、实现限流

    DOS攻击是常见的攻击服务器的方式,限流可以做到防止暴力访问服务器,可以从流量方面进行限制,也可以从请求次数方面进行限制,下面为使用lua对http请求次数进行限制

    1. 环境准备

    我们需要两个lua脚本,一个用于记录日志,一个用于限流实现:

    1. touch limit_log.lua
    2. touch limit_access.lua

    修改nginx配置文件,对8050端口的http访问进行限流:

    1. error_log logs/error.log info;
    2. server {
    3. listen 8080;
    4. root html;
    5. location / {
    6. index index.html;
    7. }
    8. }
    9. server {
    10. listen 8050;
    11. location / {
    12. default_type text/html;
    13. # 表示连接都通过该脚本
    14. access_by_lua_file /usr/local/openresty/nginx/lua/limit_access.lua;
    15. log_by_lua_file /usr/local/openresty/nginx/lua/limit_log.lua;
    16. proxy_pass http://localhost:8080/;
    17. }
    18. }

    测试下浏览器访问:

    2. 编写lua脚本

    2.1 日志脚本

    limit_log.lua内容:

    1. -- 获取ip
    2. local ip = ngx.var.remote_addr
    3. ngx.log(ngx.INFO,"ip limit log,ip:"..ip)
    2.2 连接脚本

    需要实现的功能为:一个ip,1s内不得访问超过2次,否则限制访问10s
    下面是我们限流的流程:

    limit_access.lua内容:

    1. -- 获取ip
    2. local ip = ngx.var.remote_addr
    3. -- 获取redis
    4. local redis = require "resty.redis"
    5. local red = redis:new()
    6. local ok,err = red:connect("127.0.0.1",6379)
    7. if not ok then
    8. return ngx.say("连接redis失败")
    9. end
    10. -- 判断该ip是否处于限流
    11. local isLimitKey = ip.."limit"
    12. local isLimit = red:get(isLimitKey)
    13. ngx.log(ngx.INFO,"limitKey:"..isLimitKey.." val:",isLimit)
    14. if isLimit == '1' then
    15. return ngx.exit(503)
    16. end
    17. -- 判断访问次数是否大于阈值,阈值为1秒内访问超过2次
    18. local countKey = ip.."limitCount"
    19. -- 访问次数加1后返回
    20. local limitCount = red:incr(countKey)
    21. ngx.log(ngx.INFO,"countKey:"..countKey.." val:"..limitCount)
    22. if limitCount <= 2 then
    23. -- 没有超过,更新过期时间
    24. red:expire(countKey,1)
    25. else
    26. -- 超过了,就设置限流,10s不可访问
    27. red:setex(isLimitKey,10,1)
    28. red:set(countKey,0)
    29. end

    3. 测试

    重新加载下nginx配置:

    ./nginx -p /usr/local/openresty/nginx -c /usr/local/openresty/nginx/conf/nginx-lua.conf -s reload

    效果:

    服务器日志:

    二、实现防爬虫

    爬虫有好的,也有坏的,恶意的爬虫会不断爬取网站信息,导致服务器性能下降,解决爬虫的方式有限制user_agent、限制ip、添加验证码、限制cookie。下面是限制ip的方式

    1. 环境准备

    首先我们的黑名单集合是从Redis中获取的,在Redis中新增一个set

    sadd black_set 192.168.42.170 192.168.42.171

    我们也不需要一直都从redis获取,nginx有自带的共享内存,定时从Redis中获取,然后存入共享内存中,在有效时限内使用共享内存中的数据即可,下面是nginx的配置:

    1. http {
    2. include mime.types;
    3. default_type application/octet-stream;
    4. sendfile on;
    5. keepalive_timeout 65;
    6. # 定义共享内存 最大2mb
    7. lua_shared_dict black_set 2m;
    8. server {
    9. listen 8080;
    10. root html;
    11. location / {
    12. index index.html;
    13. }
    14. }
    15. # 防爬虫
    16. server {
    17. listen 8060;
    18. location / {
    19. default_type text/html;
    20. access_by_lua_file /usr/local/openresty/nginx/lua/http-black.lua;
    21. proxy_pass http://localhost:8080;
    22. }
    23. }
    24. }

    2. 编写lua脚本

    我们需要实现,当一个ip进行http访问时,判断如果该ip处于黑名单中,那么拦截该请求

    创建脚本:

    1. cd /usr/local/openresty/nginx/lua/
    2. vi http-black.lua

    http-black.lua内容:

    1. -- 1. 获取共享内存黑名单
    2. -- 获取黑名单更新时间
    3. ngx.log(ngx.INFO,ip)
    4. local black_set = ngx.shared.black_set
    5. local update_time = black_set:get("update_time")
    6. -- 如果为空,或者距离当前时间大于2s,从redis中获取
    7. if update_time == nil or ngx.now() - update_time > 2 then
    8. local redis = require "resty.redis"
    9. local red = redis:new();
    10. local ok,err = red:connect("127.0.0.1",6379)
    11. if not ok then
    12. ngx.log(ngx.INFO,"redis connect failed")
    13. else
    14. local black_set_by_redis,err = red:smembers("black_set")
    15. -- 更新共享内存
    16. black_set:flush_all()
    17. for k,v in pairs(black_set_by_redis) do
    18. black_set:set(v,true)
    19. ngx.log(ngx.INFO,v)
    20. end
    21. black_set:set("update_time",ngx.now())
    22. end
    23. end
    24. -- 2. 请求ip是否在黑名单中
    25. local ip = ngx.var.remote_addr
    26. ngx.log(ngx.INFO,ip)
    27. ngx.log(ngx.INFO,black_set:get(ip))
    28. if black_set:get(ip) then
    29. return ngx.exit(503)
    30. end

    3. 测试

    重新加载nginx配置:

    ./nginx -p /usr/local/openresty/nginx/ -c /usr/local/openresty/nginx/conf/nginx-lua.conf -s reload

    访问结果:

    redis删除黑名单的IP:

    srem black_set 192.168.42.170

    继续访问:

    以上就是nginx使用lua脚本结合redis实现限流和防爬虫

  • 相关阅读:
    数据库系统课设——基于python+pyqt5+mysql的酒店管理系统(可直接运行)--GUI编程(2)
    110道Java初级面试题及答案(最新Java初级面试题大汇总)
    Django+Celery框架自动化定时任务开发
    采集数据重复解决方法
    Maven系列第8篇:大型Maven项目,快速按需任意构建
    一篇文章扒掉“桥梁Handler”的底裤
    《Java并发编程实战》第4章-对象的组合
    2.绘制一个点(参数)
    马上2023年了,Vue还有人用吗?
    水库大坝实时安全监测特点分析
  • 原文地址:https://blog.csdn.net/qq_24000367/article/details/125536798