如何使用滑动窗口限流优化网站性能 —— 安企CMS中的实践

如何优雅地处理高频访问?

今天早上,我收到了一条客户反馈,说网站打开很卡。我立刻打开服务器进行监控,发现服务器的负载异常高。经过一番排查,我发现在极短的时间内,某个IP以非常规律的频率访问着网站的多个页面。几乎一眼就能看出,网站被恶意的采集工具盯上了。这个IP通过不断请求页面,极大地消耗了服务器资源,导致正常用户无法访问。

面对此类高频请求问题,如果不采取有效的限流措施,网站不仅会出现性能问题,还可能在遭受持续攻击的情况下直接崩溃。于是,我决定着手处理这个问题,设计一套高效的请求限流方案。

传统限流方式面临的挑战

很多现有的限流方案都基于固定时间窗口逐次记录请求时间戳。这些方法虽然能解决部分问题,但在实际项目中有几个明显的缺点:

  1. 固定时间窗口:它按固定时段(如1分钟、5分钟)计数,当时间窗口切换时,所有请求记录清零。这样很容易导致“突发请求”问题:刚刚进入新窗口时,计数器归零,瞬间允许大量请求通过。

  2. 时间戳记录:逐次记录每次请求的时间戳虽然精确,但它要求对每个IP记录大量请求数据,内存占用较大,特别是在高流量网站上,容易出现性能瓶颈。

显然,这些传统方法难以应对我的需求。于是,我开始寻找一种更高效且灵活的方案。

如何选择最优解?

在进行方案对比时,我考虑了以下几种解决方案:

  • 漏桶算法(Leaky Bucket):将请求当成水滴,滴入“桶”中,按照固定速率“漏”出。这个方法虽然能平滑处理请求流量,但对于高频突发的请求依然存在难以控制的情况。

  • 令牌桶算法(Token Bucket):类似于漏桶算法,但它允许在短时间内处理请求的“突发”,只要有足够的“令牌”。然而,令牌桶算法相对复杂,且需要不断生成令牌,管理难度较大。

  • 滑动窗口计数法(Sliding Window):通过动态滑动的时间窗口统计请求,不仅能够灵活应对突发流量,还能保证整个窗口期内的请求量精确计算,避免传统固定时间窗口的缺点。

经过对比,我最终选择了滑动窗口计数法。这种方法既能有效限制请求频率,又不会像记录时间戳那样占用大量内存。

滑动窗口 + 时间桶

为了优化滑动窗口的内存使用,我设计了一个基于时间桶的滑动窗口算法。该方案不需要逐次记录每个请求的时间戳,而是将整个窗口期分成多个“时间桶”,每个桶记录1分钟内的请求总数。通过动态滑动这些桶,我们可以精准控制5分钟内的请求总量。

核心思路:

  1. 滑动窗口:将时间窗口分成5个1分钟的桶,每当新的一分钟开始时,移除最早的1分钟数据,动态计算最新的5分钟请求总量。
  2. 时间桶:每个桶存储该分钟内的请求数量,而不是每个请求的时间戳。这极大降低了内存占用。
  3. IP白名单:同时,我还引入了IP白名单,内网和本地IP无需受到限流控制,确保正常流量不受影响。
  4. UA白名单:同样,我引入了UA白名单,对特定UA的请求不做限流控制,避免了搜索引擎蜘蛛被误伤。

实现代码:

定义数据结构
type VisitInfo struct {Buckets     [5]int    // 每分钟一个桶,共5个桶LastVisit   int64     // 上次请求的时间戳CurrentIdx  int       // 当前时间对应的桶索引TotalCount  int       // 当前窗口期内的请求总数
}var ipVisits = make(map[string]*VisitInfo)
var blockedIPs = make(map[string]time.Time)
var mu sync.Mutexconst WindowSize = 5 * time.Minute  // 窗口大小为5分钟
const MaxRequests = 100             // 5分钟内最大请求次数
const BlockDuration = 1 * time.Hour // 封禁时长为1小时var whiteListIPs = []string{"127.0.0.1", "192.168.0.0/16"} // 内网和本地IP白名单func isWhitelisted(ip string) bool {for _, cidr := range whiteListIPs {_, subnet, _ := net.ParseCIDR(cidr)if subnet.Contains(net.ParseIP(ip)) {return true}}return false
}
记录请求并清理过期记录
func recordIPVisit(ip string) bool {mu.Lock()defer mu.Unlock()now := time.Now().Unix() // 当前的Unix时间(秒)currentMinute := now / 60 % 5 // 当前在5个桶中的索引// 检查是否已有该IP的访问记录visitInfo, exists := ipVisits[ip]if !exists {visitInfo = &VisitInfo{Buckets:    [5]int{},LastVisit:  now,CurrentIdx: int(currentMinute),}ipVisits[ip] = visitInfo}// 计算时间差,更新桶的状态elapsedMinutes := int(now/60 - visitInfo.LastVisit/60)// 如果时间超过了窗口大小,重置所有桶if elapsedMinutes >= 5 {visitInfo.Buckets = [5]int{}visitInfo.TotalCount = 0} else {// 依次清理过期的桶for i := 1; i <= elapsedMinutes; i++ {idx := (visitInfo.CurrentIdx + i) % 5visitInfo.TotalCount -= visitInfo.Buckets[idx]visitInfo.Buckets[idx] = 0}}// 更新当前桶的索引和计数visitInfo.CurrentIdx = int(currentMinute)visitInfo.Buckets[visitInfo.CurrentIdx]++visitInfo.TotalCount++// 更新最后访问时间visitInfo.LastVisit = now// 检查是否超过最大请求次数if visitInfo.TotalCount > MaxRequests {return false // 超过最大请求次数,应该封禁}return true
}
处理请求逻辑
func handleRequest(w http.ResponseWriter, r *http.Request) {ip := r.RemoteAddr// 检查并跳过白名单和搜索引擎if isWhitelisted(ip) || !isUAWhitelisted(r.UserAgent()) {...}// 检查IP是否已被封禁if isIPBlocked(ip) {http.Error(w, "Your IP is blocked.", http.StatusForbidden)return}// 记录IP访问,并检查是否超出阈值if !recordIPVisit(ip) {blockIP(ip)http.Error(w, "Too many requests from this IP.", http.StatusTooManyRequests)return}// 正常处理请求...
}
定时清理封禁的IP
func cleanupExpiredRecords() {mu.Lock()defer mu.Unlock()now := time.Now()// 清理过期的封禁记录for ip, unblockTime := range blockedIPs {if now.After(unblockTime) {delete(blockedIPs, ip)}}// 清理过期的IP访问记录,这里只回收最后一次访问超过5分钟的记录for ip, visitInfo := range ipVisits {if now.After(time.Unix(visitInfo.LastVisit, 0).Add(WindowSize)) {delete(ipVisits, ip)}}
}func startCleanupTask() {ticker := time.NewTicker(1 * time.Minute)go func() {for range ticker.C {cleanupExpiredRecords()}}()
}

当请求到来时,系统首先检查该IP是否在白名单中。如果是白名单IP,直接放行;如果不是,则使用滑动窗口算法动态统计请求数量。

判断UA,如果是搜索引擎蜘蛛,则也同样跳过后续的检查,直接放行。

将滑动窗口集成到安企CMS中

将滑动窗口限流方案集成到安企CMS时,我主要关注以下几点:

  1. 高效性:确保限流逻辑在高并发情况下依然能够快速处理,不影响正常请求。
  2. 灵活性:通过调节时间桶数量和每个桶的大小,适应不同的流量场景。例如,系统默认5分钟内允许100次请求,但可以根据业务需求灵活调整。
  3. 稳定性:对封禁的IP进行1小时的封禁处理,并定期清理过期的封禁记录,确保系统长时间稳定运行。

总结:滑动窗口限流在安企CMS中的应用

通过滑动窗口和时间桶相结合的方法,我成功解决了安企CMS中的恶意请求问题。该方案不仅显著降低了内存开销,还使得系统在高流量下表现稳定。特别是在集成了IP白名单功能后,内网和本地IP用户可以免受限流影响,保证了系统对内部流量的友好性。

优点:

  • 高效:相比逐次记录请求时间戳的传统方法,内存占用和计算量大幅减少。
  • 灵活:可以根据业务需求灵活调整限流策略和封禁时长。
  • 安全:封禁机制有效防止恶意用户对系统发起过多请求,提升整体安全性。

这次滑动窗口限流方案的实践,不仅提升了安企CMS的性能和稳定性,也为其他开发者提供了一个简单易用的高效限流方案。如果你也在开发过程中遇到类似的高频请求问题,希望这篇文章能为你提供一些参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/429052.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Golang | Leetcode Golang题解之第423题从英文中重建数字

题目&#xff1a; 题解&#xff1a; func originalDigits(s string) string {c : map[rune]int{}for _, ch : range s {c[ch]}cnt : [10]int{}cnt[0] c[z]cnt[2] c[w]cnt[4] c[u]cnt[6] c[x]cnt[8] c[g]cnt[3] c[h] - cnt[8]cnt[5] c[f] - cnt[4]cnt[7] c[s] - cnt[6]…

机器翻译之Bahdanau注意力机制在Seq2Seq中的应用

目录 1.创建 添加了Bahdanau的decoder 2. 训练 3.定义评估函数BLEU 4.预测 5.知识点个人理解 1.创建 添加了Bahdanau的decoder import torch from torch import nn import dltools#定义注意力解码器基类 class AttentionDecoder(dltools.Decoder): #继承dltools.Decoder写…

LabVIEW提高开发效率技巧----使用事件结构优化用户界面响应

事件结构&#xff08;Event Structure&#xff09; 是 LabVIEW 中用于处理用户界面事件的强大工具。通过事件驱动的编程方式&#xff0c;程序可以在用户操作时动态执行特定代码&#xff0c;而不是通过轮询&#xff08;Polling&#xff09;的方式不断检查界面控件状态。这种方式…

【学习笔记】 使用AD24完成相同电路的自动布线布局(相同模块布局布线ROOM布线快速克隆)

【学习笔记】 使用AD24完成相同电路的自动布线布局 一、适用基本条件二、基于ROOM的自动布局/布线的方法三、可能出现的报错四、ROOM自动布局的一些优点和缺点 当面对多个相同电路模块时&#xff0c;使用 ROOM 可以一次性对一个模块进行精心布局&#xff0c;然后将该布局快速复…

粒子向上持续瀑布动画效果(直接粘贴到记事本改html即可)

代码&#xff1a; 根据个人喜好修改即可 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>宽粒子向上…

正则表达式匹配整数与浮点数失败与解决方案

正则表达式匹配整数与浮点数失败与解决方案 问题描述问题分析解决方案总结 问题描述 在处理数据的时候需要提取文本内整数与浮点数&#xff0c;这个时候想到使用正则表达式&#xff0c;咨询百度文心一言给出以下方案及参考代码 import re text "我有100元&#xff0c;…

解决mac下 Android Studio gradle 下载很慢,如何手动配置

抓住人生中的一分一秒&#xff0c;胜过虚度中的一月一年! 小做个动图开篇引题 前言 平时我们clone git 上项目&#xff0c;项目对应gradle版本本地没有&#xff0c;ide编译会自动下载&#xff0c;但是超级慢可能还下载失败&#xff0c;下面讲解下此问题如 如下图所示&#xff…

ML 系列:机器学习和深度学习的深层次总结(04)多元线性回归 (MLR)

图 1.多元线性回归与简单线性回归 一、说明 线性回归从一维推广到多维&#xff0c;这与单变量线性回归有很多不同&#xff0c;情况更加复杂&#xff0c;而在梯度优化也需要改成向量梯度&#xff0c;同时&#xff0c;数据预处理也成了必要步骤。 二、综述 多元线性回归是简单线性…

【AI算法岗面试八股面经【超全整理】——深度学习】

AI算法岗面试八股面经【超全整理】 概率论【AI算法岗面试八股面经【超全整理】——概率论】信息论【AI算法岗面试八股面经【超全整理】——信息论】机器学习【AI算法岗面试八股面经【超全整理】——机器学习】深度学习【AI算法岗面试八股面经【超全整理】——深度学习】CVNLP …

【RabbitMQ】⾼级特性

RabbitMQ ⾼级特性 1. 消息确认1.1 消息确认机制1.2 代码示例 2. 持久化2.1 交换机持久化2.2 队列持久化2.3 消息持久化 3. 发送⽅确认3.1 confirm确认模式3.2 return退回模式3.3 问题: 如何保证RabbitMQ消息的可靠传输? 4. 重试机制5. TTL5.1 设置消息的TTL5.2 设置队列的TTL…

vue3集成google第三方登陆

网上资源很多&#xff0c;但乱七八糟&#xff0c;踩坑几小时后&#xff0c;发现下面的方式没问题。 npm install vue3-google-login 插件文档&#xff1a;vue3-google-登录 (devbaji.github.io) 修改main.js import ./assets/main.css import { createApp } from vue impor…

医院伤员消费点餐限制———未来之窗行业应用跨平台架构

一、点餐上限 医院点餐上限具有以下几方面的意义&#xff1a; 1. 控制成本 - 有助于医院合理规划餐饮预算&#xff0c;避免食物的过度供应造成浪费&#xff0c;从而降低餐饮成本。 2. 保障饮食均衡 - 防止患者或陪护人员过度点餐某一类食物&#xff0c;有利于引导合…

【AcWing】873. 欧拉函数

#include<iostream> using namespace std;int main(){int n;cin>>n;while(n--){int x;cin>>x;int resx;for(int i2;i<x/i;i){if(x%i0){//resres*(1-1/i);整数1/i等于0&#xff0c;算不对且会溢出//以下几种都能ac//resres/i*(i-1);i*(1-1/i)i-1&#xff0…

[JavaEE] TCP协议

目录 一、TCP协议段格式 二、TCP确保传输可靠的机制 2.1 确认应答 2.2 超时重传 2.3 连接管理 2.3.1 三次握手 2.3.2 四次挥手 2.4 滑动窗口 2.4.1 基础知识 2.4.2 两种丢包情况 2.4.2.1 数据报已经抵达&#xff0c;ACK丢包 2.4.2.2 数据包丢包 2.5 流量控制…

音视频入门基础:AAC专题(7)——FFmpeg源码中计算AAC裸流每个packet的size值的实现

音视频入门基础&#xff1a;AAC专题系列文章&#xff1a; 音视频入门基础&#xff1a;AAC专题&#xff08;1&#xff09;——AAC官方文档下载 音视频入门基础&#xff1a;AAC专题&#xff08;2&#xff09;——使用FFmpeg命令生成AAC裸流文件 音视频入门基础&#xff1a;AAC…

Vue3:v-model实现组件通信

目录 一.性质 1.双向绑定 2.语法糖 3.响应式系统 4.灵活性 5.可配置性 6.多属性绑定 7.修饰符支持 8.defineModel使用 二.使用 1.父组件 2.子组件 三.代码 1.父组件代码 2.子组件代码 四.效果 一.性质 在Vue3中&#xff0c;v-model指令的性质和作用主要体现在…

AI健身之俯卧撑计数和姿态矫正-角度估计

在本项目中&#xff0c;实现了Yolov7-Pose用于人体姿态估计。以下是如何在Windows 11操作系统上设置和运行该项目的详细步骤。 环境准备 首先&#xff0c;确保您的计算机已经安装了Anaconda。Anaconda是一个开源的Python发行版本&#xff0c;它包含了conda、Python以及众多科…

【滑动窗口】算法总结

文章目录 滑动窗口算法总结1.暴力求解vs滑动窗口2.需要注意的细节问题 2.滑动窗口的基本模板1.非固定窗口大小的滑动窗口2.固定窗口大小的滑动窗口细节 滑动窗口算法总结 1.暴力求解vs滑动窗口 遇到那些可以转化成一个子数组的长度的问题时&#xff0c;往往需要用到双指针。 …

安全基础学习-AES128加密算法

前言 AES&#xff08;Advanced Encryption Standard&#xff09;是对称加密算法的一个标准&#xff0c;主要用于保护电子数据的安全。AES 支持128、192、和256位密钥长度&#xff0c;其中AES-128是最常用的一种&#xff0c;它使用128位&#xff08;16字节&#xff09;的密钥进…

探索人工智能绘制宇宙地图的实现

人工智能 (AI) 已成为了解世界的重要工具。现在&#xff0c;随着人们对太空探索的兴趣重新升温&#xff0c;人工智能也可能对其他世界产生同样的影响。 尽管经过了几十年的研究&#xff0c;科学家们对地球大气层以外的宇宙仍然知之甚少。绘制行星、恒星、星系及其在太空中的运…