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. 自带服务发现.调用链追 ...
随机推荐
- maven 打包 pom build
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframew ...
- Gson toJson 忽略 long 为 0的数据
起因于数据id过大,所以将对应int , Integer都修改为long, 测试过程中发现 Gson toJson时,字段将int为0的数据忽略,但long 没有, 所以 1. 新增适配器 impor ...
- android 反编译APK取源代码。
坑,自己写的Android APK 程序,发现线上版本是 1.9.4 ,本地的代码版本却是 1.9.1.不知道到底怎么回事,svn里面也没有日志记录.....只能从线上apk反编译来看看了,幸好这个升 ...
- P3792 由乃与大母神原型和偶像崇拜
题意 给定长为 \(n\) 的数组 \(a\),支持单点修改,\(q\) 次查询区间 \([l,r]\) 是否可以重排为值域上连续的一段. \(n,q\le5\times10^5,a_i\le2.5\ ...
- CCIA数安委等组织发起“个人信息保护影响评估专题工作”,合合信息首批入选试点
近期,"个人信息保护影响评估专题工作"(简称"PIA专题工作")试点评估结果正式发布.PIA专题工作组由中国电子技术标准化研究院.中国信息通信院等单位的法律与技 ...
- Angular – Language Service
介绍 Angular Language Service 是一个针对 Angular 项目的程序静态分析 (Program Static Analysis) 工具,它的作用是提升开发体验. 很多 IDE ...
- Nuxt Kit 实用工具的使用示例
title: Nuxt Kit 实用工具的使用示例 date: 2024/9/25 updated: 2024/9/25 author: cmdragon excerpt: 摘要:本文介绍了Nuxt ...
- [TK] CF1526B I Hate 1111
给定一个数,将它表示成若干个形如 \(11,111,1111\cdots\) 之类的数之和,判断有没有可行解 考虑到一种贪心,即从高位开始依次向下减去每位数字,判断还能不能减动,减不动或者没减完就报告 ...
- ARM SMMU的原理与IOMMU
首先放一个社区iommupatch的网址:https://lore.kernel.org/linux-iommu/ 1: arm smmu的原理 1.1: smmu 基本知识 如上图所示,smmu 的 ...
- android IO Prefetch源码分析
I/O Prefetcher是高通本身提供的一套优化方案,可以用在Android手机App冷启动的时候.本文基于android Q 主要分libqti-iopd.vendor.qti.hardware ...