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. 自带服务发现.调用链追 ...
随机推荐
- 解密Prompt系列36. Prompt结构化编写和最优化算法UNIPROMPT
上一章我们聊了标准化的Prompt生成方案DSPy,但DSPy还是更多依赖few-shot的Prompt编写范式,在纯任务描述型指令上的优化效果有限.这一章我们就重点关注描述性指令优化.我们先简单介绍 ...
- C#/.NET/.NET Core技术前沿周刊 | 第 1 期(2024年8.12-8.18)
前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用的技术文章.社区动态.优质项目和学习资源等.让你时刻站 ...
- OpenPCDet复现过程记录
0.前言 OpenPCDet项目之前我就复现过,一个很优秀的项目,这几天又需要用到这个项目,再次复现遇到了不少问题,特此记录复现的流程 1.环境准备 1.1.前置条件 以下是我安装的版本 CUDA 1 ...
- 十五张图带你快速入门 shardingsphere-proxy
Apache ShardingSphere 是一款分布式的数据库生态系统,它包含两大产品: ShardingSphere-Proxy ShardingSphere-JDBC 很多同学对于 Shardi ...
- Oracle数据库安装与还原
安装Oracle 11g数据库 安装数据库参考这位大佬的文章:(135条消息) Oracle 11g版本下载及安装超详细教程图解_oracle11g下载_田夜的博客-CSDN博客 非常详细 利用dmp ...
- Conda 使用
简介 Conda 和 Anaconda.Miniconda 的关系 Conda 是一个包管理器及环境管理器. Anaconda 和 Miniconda 都是一种 Python 和 R 发行版,其包括了 ...
- Python 版本管理工具选择与 Pyenv 使用说明
Python 版本管理工具的主要作用是帮助开发者在同一台机器上管理多个 Python 版本和环境.这对于开发和部署不同项目非常有用,因为不同项目可能依赖不同的 Python 版本或者不同的包版本. 具 ...
- springboot 集成 onlyoffice 实现文档预览、编辑、pdf转化、缩略图生成
开源地址 https://gitee.com/lboot/lucy-onlyoffice 介绍 lucy-onlyoffice是依赖于onlyoffice的springboot文档预览编辑集成解决方案 ...
- postgresql 查询包含某字段的表
查询包含某字段的表都有哪些 查询SQL如下: SELECT b.oid, b.relname, att.attname, b.relkind, attinhcount, atttypmod FROM ...
- 小tips:vue结合百度UEditor富文本编辑器实现vue-ueditor-wrap
1.下载vue-ueditor-wrap cnpm i vue-ueditor-wrap -S 下载最新的 UEditor 资源文件放入你项目的静态资源目录中(比如 static 或者 public, ...