openresty IP限流
1、针对大流量大并发网络请求下,为了保证服务的正常运行,不得不针对性采取限流的方式来解决大流量带来的服务器的压力。
2、在目前项目中对于接入了不同的平台,所以需要针对具体的平台做相对应的限流,或者针对所有的平台做ip白名单的限制,针对ip限流。
3、以下代码是通过平台上报的ip对平台做相对应的限流,主要使用的是redis+openresty来做处理;涉及代码只做过基本的压测,未投入实际生产
相关代码记录如下:
1 --
2 -- Created by IntelliJ IDEA.
3 -- User: tiemeng
4 -- Date: 2019/3/3
5 -- Time: 10:00
6 -- To change this template use File | Settings | File Templates.
7 --
8 ngx.header.content_type = "text/html;charset=utf8"
9
10
11 -- redis配置
12 local redisConfig = {
13 redis_a = {
14 host = '127.0.0.1',
15 port = 6379,
16 pass = '',
17 timeout = 200,
18 database = 0,
19 }
20 }
21
22 local limitCount = 5
23
24 local time = 10000 -- 时间,单位为毫秒
25
26 --[[
27 获取请求IP
28 ]]
29 local function getIp()
30 local headers = ngx.req.get_headers()
31 local ip = headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
32 return ip
33 end
34
35
36
37 --[[
38 连接redis
39 ]]
40 local function redisConn()
41 local redis = require('resty.redis_factory')(redisConfig)
42 local ok, redis_a = redis:spawn('redis_a')
43 if ok ~= nil then
44 return redis, redis_a
45 end
46
47 return redis, nil
48 end
49
50
51 --[[
52 通过ip获取平台名称
53 ]]
54 local function getPlatformNameByIp(ip)
55 local handle, redis = redisConn()
56 if redis == nil then
57 return nil
58 end
59 local platform = redis:hget('iplist', ip)
60 handle.destruct()
61 if platform ~= ngx.null then
62 return platform
63 end
64 ngx.log(ngx.ERR, "ip:" .. ip .. ",未在白名单中,禁止访问")
65 return nil
66 end
67
68 local function forbid2()
69 local ip = getIp();
70 -- 2、获取当前ip是那个平台
71 local platfromName = getPlatformNameByIp(ip)
72 if platfromName == nil then
73 return false
74 end
75 -- 3、获取当前平台的总数
76 local key = 'forbid_' .. platfromName
77 local handle, redis = redisConn()
78 if redis == nil then
79 return nil
80 end
81 local curTime = ngx.now() * 1000
82 local ok, err = redis:eval([[
83 local len = redis.call('llen',KEYS[1])
84 if len < 10 then
85 redis.call('rpush',KEYS[1],ARGV[2])
86 return true
87 end
88 local times = redis.call('lrange',KEYS[1],0,0)
89 local timeSum = tonumber(times[1])+tonumber(ARGV[1])
90 if timeSum > tonumber(ARGV[2]) then
91 return false
92 end
93 redis.call('lpop',KEYS[1])
94 redis.call('rpush',KEYS[1],ARGV[2])
95 return true
96 ]], 1, key, time, curTime)
97 handle.destruct()
98 return ok
99 end
100
101
102
103 if forbid2() ~= 1 then
104 ngx.exit(403)
105 end
测试中出现的问题:
起初是使用以下代码实现的,从代码表面看是没有任何问题,但是在压力测试下并发数达到50的时候就会出现限流失效;出现失效的主要原因是,在redis中list的操作并不是所谓的原子操作,所以通过翻阅相关资料了解到,可以在redis中嵌入相关的lua脚本,可以达到原子的操作;所以在一开始的代码82-96行使用redis的eval函数来调用lua的脚本,已达到原子操作的要求;修改后经过压测后达到相对用的效果
1 local function isForbid()
2 local ip = getIp();
3 -- 2、获取当前ip是那个平台
4 local platfromName = getPlatformNameByIp(ip)
5 if platfromName == nil then
6 return false
7 end
8 -- 3、获取当前平台的总数
9 local key = 'forbid_' .. platfromName
10 local handle, redis = redisConn()
11 if redis == nil then
12 return nil
13 end
14 -- 4、校验是否超过限制
15 local len = redis:llen(key)
16
17 if len < limitCount then
18 redis:rpush(key, ngx.now() * 1000)
19 handle.destruct()
20 return true
21 end
22 local times = redis:lrange(key, 0, 0)
23 if times == ngx.null then
24 return false
25 end
26
27 if tonumber(times[1]) + time >= ngx.now() * 1000 then
28 ngx.log(ngx.ERR, "forbid_platform :" .. platfromName)
29 return false
30 end
31 os.execute("sleep " .. 1)
32 redis:lpop(key)
33 redis:rpush(key, ngx.now() * 1000)
34 handle.destruct()
35 return true
36 end
nginx部分相关配置:
http {
include mime.types;
default_type application/octet-stream;
lua_package_path '/websys/nginx/lua/?.lua;/websys/lualib/?/init.lua;;';
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
#keepalive_timeout 65;
# resolver 127.0.0.1 192.168.1.1 8.8.8.8;
#gzip on;
access_by_lua_file lua/access.lua;
此限流主要在openresty的access层做了限制,主要引入方式为上方红色字体
起初想的是通过redis的incr来实现针对ip做限流,但是其中会有键失效的时间问题;如果使用incr做相对应的操作,如果10秒钟请求量为50的话,是没法保证时间的连续性;所以最后采用了通过list来保证了时间的连续性;
本文主要记录相关的问题及知识点,如简述和实现方式有问题欢迎吐槽
openresty IP限流的更多相关文章
- Openresty 进行限流的方法
1.使用Openresty进行限流, 使用漏桶原理进行设计 和路由系统设计类似. LUA脚本去通过变量去redis取值,从redis中得到队列的大小.漏和桶的大小. 然后通过比较,队列大小与漏和桶进行 ...
- Java分布式IP限流和防止恶意IP攻击方案
前言 限流是分布式系统设计中经常提到的概念,在某些要求不严格的场景下,使用Guava RateLimiter就可以满足.但是Guava RateLimiter只能应用于单进程,多进程间协同控制便无能为 ...
- OpenResty实现限流的几种方式
在开发 api 网关的时,做过一些简单的限流,比如说静态拦截和动态拦截:静态拦截说白了就是限流某一个接口在一定时间窗口的请求数.用户可以在系统上给他们的接口配置一个每秒最大调用量,如果超过这个限制 ...
- 基于AOP和Redis实现对接口调用情况的监控及IP限流
目录 需求描述 概要设计 代码实现 参考资料 需求描述 项目中有许多接口,现在我们需要实现一个功能对接口调用情况进行统计,主要功能如下: 需求一:实现对每个接口,每天的调用次数做记录: 需求二:如果某 ...
- 基于令牌桶算法实现的SpringBoot分布式无锁限流插件
本文档不会是最新的,最新的请看Github! 1.简介 基于令牌桶算法和漏桶算法实现的纳秒级分布式无锁限流插件,完美嵌入SpringBoot.SpringCloud应用,支持接口限流.方法限流.系统限 ...
- 图解Nginx限流配置
本文以示例的形式,由浅入深讲解Nginx限流相关配置,是对简略的官方文档的积极补充. Nginx限流使用的是leaky bucket算法,如对算法感兴趣,可移步维基百科先行阅读.不过不了解此算法,不影 ...
- 【分布式架构】--- 基于Redis组件的特性,实现一个分布式限流
分布式---基于Redis进行接口IP限流 场景 为了防止我们的接口被人恶意访问,比如有人通过JMeter工具频繁访问我们的接口,导致接口响应变慢甚至崩溃,所以我们需要对一些特定的接口进行IP限流,即 ...
- nginx限流方案的实现(三种方式)
通过查看nginx官方文档,小弟查看到了三种nginx限流方式. 1.limit_conn_zone 2.limit_req_zone 3.ngx_http_upstream_module 前两种只能 ...
- 一个轻量级的基于RateLimiter的分布式限流实现
上篇文章(限流算法与Guava RateLimiter解析)对常用的限流算法及Google Guava基于令牌桶算法的实现RateLimiter进行了介绍.RateLimiter通过线程锁控制同步,只 ...
- 不死的小强 .net core 微服务 快速开发框架 Viper 限流
1.Viper是什么? Viper 是.NET平台下的Anno微服务框架的一个示例项目.入门简单.安全.稳定.高可用.全平台可监控.底层通讯可以随意切换thrift grpc. 自带服务发现.调用链追 ...
随机推荐
- 数字名片工具 BBlog:使用一个链接,快速创建和分享你的信息主页和数字花园
数字名片 BBlog:使用一个链接,快速创建和分享你的信息主页和数字花园 随着移动互联网技术的快速发展,数字名片产品已成为现代社交和网络营销的重要工具.数字名片可以帮助个人和企业在各种场合中展示和分享 ...
- 05-canvas绘制简单图形之三角形
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...
- docker部署宝塔面板
环境准备: 系统 rocky:9.2 部署流程: 1.安装docker dnf -y install yum-utils yum-config-manager --add-repo http://mi ...
- 线性dp:编辑距离
编辑距离 本题与力扣72.编辑距离题意一样,阅读完本文可以尝试leetcode72. 力扣题目链接 题目叙述 输入两个字符串a,b.输出从字符串a修改到字符串b时的编辑距离 输入 NOTV LOVER ...
- equals与hashCode关系梳理
目录 equals用法 hashCode用法 总结 为什么一个类中需要两个比较方法 为什么重写 equals 方法时必须同时重写 hashCode 方法? Reference 这个并不是一个通用性编程 ...
- .NET 6 使用Nlog 记录日志到本地并写入SQLserver数据库
1. 安装Nlog 对应Nuget包版本 NLog:5.0.4 NLog.Database:5.0.4 NLog.Web.AspNetCore:5.1.4 Microsoft.Data.SqlClie ...
- 2023.7.2-3-4Mssql xp_cmdshell提权
1.概念 Mssql和SQL sever的一个产品的不同名称.都属于微软公司旗下.而上述Mssql xp_cmdshell提权也属于数据库提权的一种. 主要依赖于sql server自带的存储过程. ...
- ST-SSL: 用于交通流量预测的时空自监督学习《Spatio-Temporal Self-Supervised Learning for Traffic Flow Prediction》(交通流量预测、时空异质性、自监督、数据增强)
2023年10月23日,继续论文,好困,想发疯. 论文:Spatio-Temporal Self-Supervised Learning for Traffic Flow Prediction Git ...
- Open Graph protocol
大纲介绍 Open Graph protocol 是 facebook 2010 年推出的一套规范, 用途是通过一些简单的 meta tag 来描述网站的内容. 这样在 sharing 网址的时候就可 ...
- IDEA如何自动导入依赖的jar包
前言 我们在使用IDEA开发时,会引入第三方的jar包,这些第三方的jar包使我们可以快速的使用别人开发好的功能,而不用重复造轮子了. 这大大提高了我们的开发效率. 但是,有时候我们一下子需要导入太多 ...