golang,go,博客,开源,编程

滑动窗口ip限速器

Published on with 0 views and 0 comments

在 Go(Golang)中,基于滑动窗口的 IP 限速器是一种精确且高效的限流策略,适用于需要细粒度流量控制的场景,如防止恶意攻击、限制 API 调用频率等。


滑动窗口限速算法简介

滑动窗口算法通过在每次请求时计算过去固定时间窗口内的请求数量,实现对请求速率的限制。

与固定窗口算法相比,滑动窗口算法能更平滑地限制流量,避免突发请求集中在窗口边界的问题。


Go 中的滑动窗口 IP 限速器实现

以下是几个在 Go 中实现滑动窗口 IP 限速器的开源项目:

1. RussellLuo/slidingwindow

该库实现了 Kong API 网关使用的可扩展滑动窗口限流算法,适用于分布式环境。

特点:

  • 支持本地和分布式限流。
  • 通过计算当前窗口和前一个窗口的请求数,估算当前的请求速率。
  • 适用于需要高精度限流的场景。

示例:

假设每分钟允许 100 次请求,在第 75 秒时,当前窗口内有 12 次请求,前一个窗口内有 86 次请求,则当前估算的请求数为:(GitHub)

count = 86 * ((60 - 15) / 60) + 12 = 86 * 0.75 + 12 = 76.5

如果 count 超过设定的阈值,则拒绝请求。(Go Packages)

2. Saurav1999/sliding-window-rate-limiter

该库使用 Redis 实现滑动窗口限流,支持按 IP、API 路径或用户标识进行限流。

特点:

  • 支持多种限流策略(按 IP、API、用户)。
  • 通过配置文件设置限流参数。
  • 适用于需要集中管理限流策略的应用。

配置示例:

{
  "limitsIp": {
    "limit": 20,
    "window": 1200,
    "unit": "seconds"
  }
}

上述配置表示:每个 IP 在 1200 秒(20 分钟)内最多允许 20 次请求。

3. senderista/sliding-window-rate-limiter

该库实现了基于 Redis 的分布式滑动窗口限流器,适用于需要高可用性的系统。

特点:

  • 支持分布式部署,多个实例共享限流状态。
  • 使用 Redis 存储限流数据,确保一致性。
  • 适用于微服务架构中的限流需求。

示例:

limiter, err := ratelimit.New(0, urlHash, 100, 60, 5, "localhost:6379")

上述代码创建了一个限流器,允许每 60 秒最多 100 次请求,时间精度为 5 秒。


实现滑动窗口 IP 限速器的关键步骤

  1. 请求记录: 为每个 IP 地址维护一个时间戳列表,记录其请求时间。
  2. 清理过期记录: 在每次请求时,移除超过时间窗口的旧记录。
  3. 请求计数: 计算当前时间窗口内的请求数量。
  4. 限流判断: 如果请求数量超过设定的阈值,拒绝请求;否则,允许请求并记录时间戳。

示例代码

以下是一个简单的滑动窗口 IP 限速器实现示例:

package main

import (
    "net/http"
    "sync"
    "time"
)

type RateLimiter struct {
    mu        sync.Mutex
    requests  map[string][]time.Time
    limit     int
    window    time.Duration
}

func NewRateLimiter(limit int, window time.Duration) *RateLimiter {
    return &RateLimiter{
        requests: make(map[string][]time.Time),
        limit:    limit,
        window:   window,
    }
}

func (rl *RateLimiter) Allow(ip string) bool {
    rl.mu.Lock()
    defer rl.mu.Unlock()

    now := time.Now()
    windowStart := now.Add(-rl.window)
    timestamps := rl.requests[ip]

    // 移除过期的时间戳
    var validTimestamps []time.Time
    for _, t := range timestamps {
        if t.After(windowStart) {
            validTimestamps = append(validTimestamps, t)
        }
    }

    if len(validTimestamps) >= rl.limit {
        return false
    }

    // 添加当前时间戳
    validTimestamps = append(validTimestamps, now)
    rl.requests[ip] = validTimestamps
    return true
}

func main() {
    limiter := NewRateLimiter(100, time.Minute)

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        ip := r.RemoteAddr
        if !limiter.Allow(ip) {
            http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
            return
        }
        w.Write([]byte("Hello, World!"))
    })

    http.ListenAndServe(":8080", nil)
}

上述代码创建了一个限流器,限制每个 IP 每分钟最多 100 次请求。


总结

滑动窗口 IP 限速器在 Go 中的实现可以通过多种方式完成,选择合适的库或自行实现取决于具体需求。对于分布式系统,推荐使用支持 Redis 的库,如 senderista/sliding-window-rate-limiter;对于本地应用,RussellLuo/slidingwindow 提供了高效的解决方案。

在实现时,应注意线程安全和性能优化,以确保限流器的稳定性和高效性。


标题:滑动窗口ip限速器
作者:mooncakeee
地址:http://blog.dd95828.com/articles/2025/05/22/1747897875645.html
联系:scotttu@163.com