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限流的更多相关文章

  1. Openresty 进行限流的方法

    1.使用Openresty进行限流, 使用漏桶原理进行设计 和路由系统设计类似. LUA脚本去通过变量去redis取值,从redis中得到队列的大小.漏和桶的大小. 然后通过比较,队列大小与漏和桶进行 ...

  2. Java分布式IP限流和防止恶意IP攻击方案

    前言 限流是分布式系统设计中经常提到的概念,在某些要求不严格的场景下,使用Guava RateLimiter就可以满足.但是Guava RateLimiter只能应用于单进程,多进程间协同控制便无能为 ...

  3. OpenResty实现限流的几种方式

      在开发 api 网关的时,做过一些简单的限流,比如说静态拦截和动态拦截:静态拦截说白了就是限流某一个接口在一定时间窗口的请求数.用户可以在系统上给他们的接口配置一个每秒最大调用量,如果超过这个限制 ...

  4. 基于AOP和Redis实现对接口调用情况的监控及IP限流

    目录 需求描述 概要设计 代码实现 参考资料 需求描述 项目中有许多接口,现在我们需要实现一个功能对接口调用情况进行统计,主要功能如下: 需求一:实现对每个接口,每天的调用次数做记录: 需求二:如果某 ...

  5. 基于令牌桶算法实现的SpringBoot分布式无锁限流插件

    本文档不会是最新的,最新的请看Github! 1.简介 基于令牌桶算法和漏桶算法实现的纳秒级分布式无锁限流插件,完美嵌入SpringBoot.SpringCloud应用,支持接口限流.方法限流.系统限 ...

  6. 图解Nginx限流配置

    本文以示例的形式,由浅入深讲解Nginx限流相关配置,是对简略的官方文档的积极补充. Nginx限流使用的是leaky bucket算法,如对算法感兴趣,可移步维基百科先行阅读.不过不了解此算法,不影 ...

  7. 【分布式架构】--- 基于Redis组件的特性,实现一个分布式限流

    分布式---基于Redis进行接口IP限流 场景 为了防止我们的接口被人恶意访问,比如有人通过JMeter工具频繁访问我们的接口,导致接口响应变慢甚至崩溃,所以我们需要对一些特定的接口进行IP限流,即 ...

  8. nginx限流方案的实现(三种方式)

    通过查看nginx官方文档,小弟查看到了三种nginx限流方式. 1.limit_conn_zone 2.limit_req_zone 3.ngx_http_upstream_module 前两种只能 ...

  9. 一个轻量级的基于RateLimiter的分布式限流实现

    上篇文章(限流算法与Guava RateLimiter解析)对常用的限流算法及Google Guava基于令牌桶算法的实现RateLimiter进行了介绍.RateLimiter通过线程锁控制同步,只 ...

  10. 不死的小强 .net core 微服务 快速开发框架 Viper 限流

    1.Viper是什么? Viper 是.NET平台下的Anno微服务框架的一个示例项目.入门简单.安全.稳定.高可用.全平台可监控.底层通讯可以随意切换thrift grpc. 自带服务发现.调用链追 ...

随机推荐

  1. Java学习笔记1--JDK,JRE和JVM

    1.Java开发环境 Java开发环境是指Java程序员开发.编写.测试和调试Java程序所使用的所有工具和技术.Java开发环境通常由以下几个部分组成: JDK(Java Development K ...

  2. 9组-Alpha冲刺-6/6

    一.基本情况 队名:不行就摆了吧 组长博客: https://www.cnblogs.com/Microsoft-hc/p/15546711.html 小组人数: 8 二.冲刺概况汇报 张伟鹏 过去两 ...

  3. Linux命令cURL详解,并实现文件定时上传到ftp服务器的程序

    前言 前段时间群里讨论,想实现某个文件定时上传到服务器要怎么来实现.我记得之前做过 一个项目:为高通的iot模组编写FOTA功能:实现模组可以远程下载升级镜像包,实现版本升级功能.并当时使用的一个超级 ...

  4. 零基础学习人工智能—Python—Pytorch学习(八)

    前言 本文介绍卷积神经网络的上半部分. 其实,学习还是需要老师的,因为我自己写文章的时候,就会想当然,比如下面的滑动窗口,我就会想当然的认为所有人都能理解,而实际上,我们在学习的过程中之所以卡顿的点多 ...

  5. MATLAB 使用

    MATLAB CLI 启动 MATLAB CLI 交互式界面(需要已安装 MATLAB): matlab -nodesktop -nosplash # 无桌面环境,无启动动画 不启动 MATLAB 直 ...

  6. LaTeX 插入代码

    LaTeX 插入代码可以使用的宏包有 verbatim.fancyvrb.listings 以及 minted.个人最推荐使用 minted. verbatim verbatim 没有语法高亮功能,只 ...

  7. 【论文解读】System 2 Attention提高大语言模型客观性和事实性

    一.简要介绍       本文简要介绍了论文"System 2 Attention (is something you might need too) "的相关工作.基于trans ...

  8. Qml 实现水波进度动画条

    [写在前面] 最近看到一个非常有趣的动画效果:水波进度动画. 学习了一下实现思路,觉得很有意思. 不过原版是 HTML + CSS,我这里用的是 Qml,有一些小技巧,分享给大家~ [正文开始] 老样 ...

  9. EF Core – Table / Entity Splitting

    参考 Docs – Advanced table mapping Table Splitting Table Splitting 指的是把一个表映射到多个 Entity,或者反过来说就是把多个 Ent ...

  10. QT6窗口系统之QT底层窗口QWindow:QT框架中哪些常见窗口是基于QWindow的? 如何实现QT框架栅格窗口?如何实现QT框架OpenGL窗口?

    QT6窗口系统之QT底层窗口QWindow:QT框架中哪些常见窗口是基于QWindow的? 如何实现QT框架栅格窗口?如何实现QT框架OpenGL窗口? 简介 本文介绍了QT6窗口系统中的QT底层窗口 ...