前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >分布式--OpenResty+lua+Redis实现限流与防爬虫

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

作者头像
aruba
发布2022-09-01 16:53:17
7130
发布2022-09-01 16:53:17
举报
文章被收录于专栏:android技术android技术

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

一、实现限流

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

1. 环境准备

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

代码语言:javascript
复制
touch limit_log.lua
touch limit_access.lua

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

代码语言:javascript
复制
error_log logs/error.log info;

    server {
        listen 8080;
        root html;

        location / {
                index index.html;
        }
    }
    server {
        listen 8050;

        location / {
                default_type text/html;
                # 表示连接都通过该脚本
                access_by_lua_file /usr/local/openresty/nginx/lua/limit_access.lua;
                log_by_lua_file /usr/local/openresty/nginx/lua/limit_log.lua;
                proxy_pass http://localhost:8080/;
        }
    }

测试下浏览器访问:

2. 编写lua脚本
2.1 日志脚本

limit_log.lua内容:

代码语言:javascript
复制
-- 获取ip
local ip = ngx.var.remote_addr

ngx.log(ngx.INFO,"ip limit log,ip:"..ip)
2.2 连接脚本

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

下面是我们限流的流程:

limit_access.lua内容:

代码语言:javascript
复制
-- 获取ip
local ip = ngx.var.remote_addr

-- 获取redis
local redis = require "resty.redis"
local red = redis:new()

local ok,err = red:connect("127.0.0.1",6379)
if not ok then
        return ngx.say("连接redis失败")
end

-- 判断该ip是否处于限流
local isLimitKey = ip.."limit"
local isLimit = red:get(isLimitKey)
ngx.log(ngx.INFO,"limitKey:"..isLimitKey.."  val:",isLimit)

if isLimit == '1' then
        return ngx.exit(503)
end

-- 判断访问次数是否大于阈值,阈值为1秒内访问超过2次
local countKey = ip.."limitCount"
-- 访问次数加1后返回
local limitCount = red:incr(countKey)
ngx.log(ngx.INFO,"countKey:"..countKey.."   val:"..limitCount)

if limitCount <= 2 then
        -- 没有超过,更新过期时间
        red:expire(countKey,1)
else
        -- 超过了,就设置限流,10s不可访问
        red:setex(isLimitKey,10,1)
        red:set(countKey,0)
end
3. 测试

重新加载下nginx配置:

代码语言:javascript
复制
./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

代码语言:javascript
复制
sadd black_set 192.168.42.170 192.168.42.171

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

代码语言:javascript
复制
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    # 定义共享内存 最大2mb
    lua_shared_dict black_set 2m;

    server {
        listen 8080;
        root html;

        location / {
                index index.html;
        }
    }

    # 防爬虫
    server {
        listen 8060;

        location / {
                default_type text/html;
                access_by_lua_file /usr/local/openresty/nginx/lua/http-black.lua;
                proxy_pass http://localhost:8080;
        }
    }
}
2. 编写lua脚本

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

创建脚本:

代码语言:javascript
复制
cd /usr/local/openresty/nginx/lua/
vi http-black.lua

http-black.lua内容:

代码语言:javascript
复制
-- 1. 获取共享内存黑名单

-- 获取黑名单更新时间
ngx.log(ngx.INFO,ip)
local black_set = ngx.shared.black_set
local update_time = black_set:get("update_time")

-- 如果为空,或者距离当前时间大于2s,从redis中获取
if update_time == nil or ngx.now() - update_time > 2 then
        local redis = require "resty.redis"
        local red = redis:new();
        local ok,err = red:connect("127.0.0.1",6379)

        if not ok then
                ngx.log(ngx.INFO,"redis connect failed")
        else
                local black_set_by_redis,err = red:smembers("black_set")

                -- 更新共享内存
                black_set:flush_all()
                for k,v in pairs(black_set_by_redis) do
                        black_set:set(v,true)
                        ngx.log(ngx.INFO,v)
                end

                black_set:set("update_time",ngx.now())
        end
end

-- 2. 请求ip是否在黑名单中
local ip = ngx.var.remote_addr

ngx.log(ngx.INFO,ip)
ngx.log(ngx.INFO,black_set:get(ip))

if black_set:get(ip) then
        return ngx.exit(503)
end
3. 测试

重新加载nginx配置:

代码语言:javascript
复制
./nginx -p /usr/local/openresty/nginx/ -c /usr/local/openresty/nginx/conf/nginx-lua.conf -s reload

访问结果:

redis删除黑名单的IP:

代码语言:javascript
复制
srem black_set 192.168.42.170

继续访问:

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-07-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、实现限流
    • 1. 环境准备
      • 2. 编写lua脚本
        • 2.1 日志脚本
        • 2.2 连接脚本
      • 3. 测试
      • 二、实现防爬虫
        • 1. 环境准备
          • 2. 编写lua脚本
            • 3. 测试
            相关产品与服务
            云数据库 Redis
            腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档