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

gin中间件之基于 IP 的并发限制中间件

Published on with 0 views and 0 comments

Gin 中实现基于 IP 的请求并发限制可以使用自定义中间件来限制每个 IP 地址的并发请求数。这样,每个 IP 地址的请求会有独立的并发限制,而不会影响其他 IP 的请求。通常,可以使用 map 存储每个 IP 的并发状态,并用信号量或计数器来限制并发请求数。

1. 基于 IP 的并发限制中间件

以下是一个示例代码,展示如何根据 IP 限制请求的并发数:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"sync"
	"time"
)

// 并发限制结构体,保存每个 IP 的并发请求信息
type IPConcurrencyLimiter struct {
	mu       sync.Mutex
	limitMap map[string]chan struct{}
	maxConcurrency int
}

// 创建新的并发限制器
func NewIPConcurrencyLimiter(maxConcurrency int) *IPConcurrencyLimiter {
	return &IPConcurrencyLimiter{
		limitMap:      make(map[string]chan struct{}),
		maxConcurrency: maxConcurrency,
	}
}

// 请求并发限制中间件
func (limiter *IPConcurrencyLimiter) LimitConcurrencyByIP() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 获取客户端的 IP 地址
		clientIP := c.ClientIP()

		// 锁定 limiter 结构,确保线程安全
		limiter.mu.Lock()
		// 如果该 IP 的信号通道还没有创建,则创建
		if _, exists := limiter.limitMap[clientIP]; !exists {
			limiter.limitMap[clientIP] = make(chan struct{}, limiter.maxConcurrency)
		}
		// 解锁
		limiter.mu.Unlock()

		// 获取该 IP 的信号通道
		ipSemaphore := limiter.limitMap[clientIP]

		// 如果通道已满,表示该 IP 的并发请求数已达上限,拒绝请求
		select {
		case ipSemaphore <- struct{}{}:
			// 该 IP 请求被允许,继续处理请求
			c.Next()

			// 请求处理完后,释放该 IP 的一个并发请求信号
			<-ipSemaphore
		default:
			// 如果并发请求数已达上限,返回 429 Too Many Requests 错误
			c.JSON(http.StatusTooManyRequests, gin.H{
				"message": "Too many requests from your IP, please try again later.",
			})
			c.Abort() // 终止请求
		}
	}
}

func main() {
	// 创建并发限制器,最大并发数为 2
	limiter := NewIPConcurrencyLimiter(2)

	// 创建 Gin 实例
	r := gin.Default()

	// 应用并发限制中间件
	r.Use(limiter.LimitConcurrencyByIP())

	// 示例路由
	r.GET("/example", func(c *gin.Context) {
		// 模拟处理请求的时间
		time.Sleep(2 * time.Second)
		c.JSON(http.StatusOK, gin.H{
			"message": "Request processed successfully",
		})
	})

	// 启动服务器
	r.Run(":8080")
}

2. 代码解析

  • IPConcurrencyLimiter 结构体
    • limitMap:一个 map 用于存储每个 IP 的并发请求通道。每个 IP 地址都会有一个独立的 chan struct{} 类型的通道,用来限制该 IP 的并发请求数。
    • maxConcurrency:每个 IP 地址的最大并发请求数。
  • LimitConcurrencyByIP 中间件
    • 在每个请求到达时,首先会获取请求的客户端 IP 地址。
    • 根据 IP 地址查找或创建一个对应的信号通道(chan struct{}),每个通道的容量限制了该 IP 的最大并发请求数。
    • 如果该 IP 的并发请求数没有达到上限,允许请求继续处理,并在请求结束时释放一个通道信号。
    • 如果并发数已达到上限,拒绝请求并返回 HTTP 429 错误。
  • 信号量机制
    • 使用通道(chan struct{})作为信号量,每当一个请求到达时,尝试向该 IP 地址的通道发送一个信号。如果通道满了,表示并发数已达上限,拒绝请求。

3. 启动应用

运行程序后,尝试发送多个请求:

  1. 正常请求(并发请求数未超过限制):

    curl http://localhost:8080/example
    
  2. 超过并发限制的请求(例如,发送多个并发请求):

    curl http://localhost:8080/example
    curl http://localhost:8080/example
    curl http://localhost:8080/example
    

    假设每个 IP 的最大并发限制为 2,那么前三个请求中,只有前两个会被处理,第三个请求将会被拒绝并返回如下响应:

    {
      "message": "Too many requests from your IP, please try again later."
    }
    

4. 优化与扩展

  • 清理 IP 记录:如果某个 IP 长时间没有请求,可能会导致无用的并发通道占用内存。在这种情况下,你可以定期清理 limitMap 中的无效 IP 记录。
  • 限制全局并发:如果你需要限制整个系统的最大并发请求数,可以增加一个全局的并发控制信号量,避免系统在高并发场景下崩溃。
  • 动态配置:你可以将 IP 白名单和并发限制数值存储在配置文件或数据库中,并根据需要动态调整。
  • 支持负载均衡:如果你的应用部署在多个服务器上,使用负载均衡时,需要考虑如何同步各个服务器之间的并发限制。你可以使用 Redis 等共享存储来跨服务器同步 IP 并发限制。

总结

通过在 Gin 中实现基于 IP 的并发限制,你可以有效地控制每个 IP 的请求并发数,避免某个 IP 过度占用服务器资源,防止恶意攻击或滥用。同时,你可以灵活调整并发限制,增强应用的可扩展性和可靠性。


标题:gin中间件之基于 IP 的并发限制中间件
作者:mooncakeee
地址:http://blog.dd95828.com/articles/2025/01/07/1736218355508.html
联系:scotttu@163.com