有一个服务器,用于提供HTTP服务,但是需要限制每个用户在任意的100秒内只能请求60次,怎么实现这个功能

问题

有一个服务器,用于提供HTTP服务,但是需要限制每个用户在任意的100秒内只能请求60次,怎么实现这个功能

我的回答

嗯,这个问题其实挺经典的,就是限流嘛。任意100秒内最多60次请求,这是个滑动窗口的问题。

我觉得可以这样实现:

方案一:用队列记录时间戳

cpp 复制代码
#include <unordered_map>
#include <queue>
#include <chrono>

class RateLimiter {
private:
    std::unordered_map<std::string, std::queue<long long>> userRequests;
    
public:
    bool isAllowed(const std::string& userId) {
        auto now = std::chrono::duration_cast<std::chrono::seconds>(
            std::chrono::system_clock::now().time_since_epoch()).count();
        
        auto& requests = userRequests[userId];
        
        // 先把100秒前的请求都清掉
        while (!requests.empty() && now - requests.front() > 100) {
            requests.pop();
        }
        
        // 看看是不是已经到60次了
        if (requests.size() >= 60) {
            return false;
        }
        
        // 记录这次请求
        requests.push(now);
        return true;
    }
};

这个方案比较直观,就是每个用户维护一个时间戳队列。但是吧,如果用户很多的话,内存消耗会比较大。

方案二:滑动窗口计数器

cpp 复制代码
#include <unordered_map>
#include <chrono>

struct WindowData {
    int prevCount = 0;
    int currCount = 0;
    long long currWindowStart = 0;
};

class RateLimiter {
private:
    std::unordered_map<std::string, WindowData> windows;
    static const int WINDOW_SIZE = 100;
    static const int MAX_REQUESTS = 60;
    
public:
    bool isAllowed(const std::string& userId) {
        auto now = std::chrono::duration_cast<std::chrono::seconds>(
            std::chrono::system_clock::now().time_since_epoch()).count();
        
        auto& data = windows[userId];
        long long currentWindow = now / WINDOW_SIZE;
        
        // 如果进入了新的窗口
        if (data.currWindowStart != currentWindow) {
            data.prevCount = data.currCount;
            data.currCount = 0;
            data.currWindowStart = currentWindow;
        }
        
        // 计算滑动窗口内的估算请求数
        double prevWeight = (double)(WINDOW_SIZE - (now % WINDOW_SIZE)) / WINDOW_SIZE;
        double estimatedCount = data.prevCount * prevWeight + data.currCount;
        
        if (estimatedCount >= MAX_REQUESTS) {
            return false;
        }
        
        data.currCount++;
        return true;
    }
};

这个方案内存占用比较小,每个用户就存几个数字,但是精度会差一点点。

实际使用的话

如果在实际项目中遇到这个问题,可能还会考虑:

  1. 线程安全:加个mutex或者用原子操作
cpp 复制代码
#include <mutex>

class ThreadSafeRateLimiter {
private:
    std::mutex mtx;
    // ... 其他成员
    
public:
    bool isAllowed(const std::string& userId) {
        std::lock_guard<std::mutex> lock(mtx);
        // ... 限流逻辑
    }
};
  1. 内存清理:定期清理过期的用户数据,不然内存会一直涨

  2. 如果是分布式的:可能需要用Redis之类的外部存储

我个人比较倾向于第二种方案,因为内存效率高,而且对于大部分场景来说精度够用了。当然如果对精度要求特别高,那就用第一种。

相关推荐
fie888919 小时前
NSCT(非下采样轮廓波变换)的分解和重建程序
算法
晨晖220 小时前
单链表逆转,c语言
c语言·数据结构·算法
im_AMBER21 小时前
Leetcode 78 识别数组中的最大异常值 | 镜像对之间最小绝对距离
笔记·学习·算法·leetcode
鼾声鼾语1 天前
matlab的ros2发布的消息,局域网内其他设备收不到情况吗?但是matlab可以订阅其他局域网的ros2发布的消息(问题总结)
开发语言·人工智能·深度学习·算法·matlab·isaaclab
LYFlied1 天前
【每日算法】LeetCode 25. K 个一组翻转链表
算法·leetcode·链表
Swizard1 天前
别再迷信“准确率”了!一文读懂 AI 图像分割的黄金标尺 —— Dice 系数
python·算法·训练
s09071361 天前
紧凑型3D成像声纳实现路径
算法·3d·声呐·前视多波束
可爱的小小小狼1 天前
算法:二叉树遍历
算法
d111111111d1 天前
在STM32函数指针是什么,怎么使用还有典型应用场景。
笔记·stm32·单片机·嵌入式硬件·学习·算法
AI科技星1 天前
质量定义方程常数k = 4π m_p的来源、推导与意义
服务器·数据结构·人工智能·科技·算法·机器学习·生活