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. QT的基础设置(菜单栏、状态栏、任务栏。。。。)

    Qt [1] 是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架.它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器.下面介绍QT的基础配置 1 ...

  2. SMU 2024 spring 天梯赛自主训练3

    SMU 2024 spring 天梯赛自主训练3 7-1 2018我们要赢 - SMU 2024 spring 天梯赛自主训练3 (pintia.cn) 2018 wo3 men2 yao4 ying ...

  3. disconf分布式配置管理(一) 安装与配置

    一.背景 在生产部署过程中,遇到以下问题: 1.由于节点较多,每次增量修改配置文件后都需要每个节点替换配置文件. 2.有些动态配置修改后,需要重启服务. 二.解决方案 1.使用linux文件共享配置文 ...

  4. C# 使用HtmlAgilityPack 抓取 网站链接

    今天在找电视剧下载链接的时候,找了一个整部剧的下载地址,但是有40多集,链接地址较长且不好复制,于是就想到了HtmlAgilityPack抓取的方式. 先看实现效果: 使用到的NUGET包: Html ...

  5. ICMAN触摸滑条滚轮方案

    ICMAN触摸滑条滚轮调光是一种利用触摸技术实现的调光控制方式,是一种更简单.直观且节能的调光方式,有效改善了用户的照明体验,并在智能家居和节能照明领域发挥着重要作用. 基于厦门晶尊微电子(ICMAN ...

  6. C语言浮点数转字符串实现函数

    C语言浮点数转字符串可用库函数sprintf,此处为编写的简单函数. 小数部分最多显示六位. pOut:输出字符串缓冲区 f:浮点数值 isize:输出字符串缓冲区大小 char * Funftoa( ...

  7. android 反编译APK取源代码。

    坑,自己写的Android APK 程序,发现线上版本是 1.9.4 ,本地的代码版本却是 1.9.1.不知道到底怎么回事,svn里面也没有日志记录.....只能从线上apk反编译来看看了,幸好这个升 ...

  8. 火山引擎数智平台:高性能ChatBI的技术解读和落地实践

    导读:大模型能力的发展和成熟,催生出新一代智能化 BI-- ChatBI,即通过自然语言处理(NLP)与大型语言模型(LLMs)的结合,极大简化数据分析过程,提高效率并降低分析门槛.火山引擎数智平台旗 ...

  9. SpringMVC —— REST风格简介

    REST风格简介 REST(Representational State Transfer),表现形式转换 传统风格资源描述形式 REST风格描述形式 优点 隐藏资源的访问行为,无法通过地址得知对资源 ...

  10. 基于RHEL 9 搭建 KVM 虚拟化环境

    一.准备工作 1. 检查硬件虚拟化支持 KVM 要求处理器支持硬件虚拟化技术:Intel VT-x(虚拟化技术扩展)或 AMD-V(虚拟化技术扩展). 检查方法: 使用以下命令检查 CPU 是否支持虚 ...