golang简单高效的请求速率限制中间件插件Limiter的使用

Golang简单高效的请求速率限制中间件插件Limiter的使用

Limiter是一个简单高效的Go语言速率限制中间件,具有以下特点:

  • 简单易用的API
  • 支持"Store"模式的存储后端
  • 支持Redis(但不局限于Redis)
  • 提供HTTP、FastHTTP和Gin等多种中间件

安装

使用Go Modules安装:

go get github.com/ulule/limiter/v3@v3.11.2

使用示例

以下是使用Limiter的完整示例:

package main

import (
	"net/http"
	"time"
	
	"github.com/gin-gonic/gin"
	"github.com/ulule/limiter/v3"
	"github.com/ulule/limiter/v3/drivers/store/memory"
	"github.com/ulule/limiter/v3/drivers/middleware/gin"
)

func main() {
	// 1. 定义速率限制规则
	// 每小时1000次请求
	rate := limiter.Rate{
		Period: 1 * time.Hour,
		Limit:  1000,
	}
	
	// 或者使用简化格式:"<limit>-<period>"
	// 支持的周期:
	// * "S": 秒
	// * "M": 分钟
	// * "H": 小时
	// * "D": 天
	// 例如:"1000-H" 表示每小时1000次请求
	rate, err := limiter.NewRateFromFormatted("1000-H")
	if err != nil {
		panic(err)
	}
	
	// 2. 创建存储(Store)
	// 这里使用内存存储
	store := memory.NewStore()
	
	// 如果是Redis存储,可以这样创建:
	// store, err := redis.NewStore(client)
	
	// 3. 创建Limiter实例
	limiterInstance := limiter.New(store, rate)
	
	// 4. 创建Gin中间件
	middleware := ginlimiter.NewMiddleware(limiterInstance)
	
	// 5. 初始化Gin并应用中间件
	router := gin.Default()
	router.Use(middleware)
	
	// 示例路由
	router.GET("/", func(c *gin.Context) {
		c.String(http.StatusOK, "Hello World!")
	})
	
	// 启动服务器
	router.Run(":8080")
}

工作原理

Limiter的工作原理如下:

  1. 使用请求的IP地址作为存储中的键
  2. 如果键不存在,则设置一个默认值并设置过期时间
  3. 提供两种存储方式:
    • Redis: 依赖TTL并在每个请求上递增速率限制
    • 内存: 依赖go-cache的分支,使用goroutine清除过期的键
  4. 当达到限制时,返回429 HTTP状态码

反向代理后使用Limiter

如果你的Limiter在反向代理后面,获取"真实"客户端IP可能会比较困难。Limiter提供了以下解决方案:

  1. X-Forwarded-For

    • 不建议直接信任此头部,因为它可以被伪造
    • 只有在你的反向代理保证会清除/重写此头部时才能启用TrustForwardHeader选项
  2. 自定义头部

    • 许多CDN和云提供商添加自定义头部来定义客户端IP
    • 例如:Fastly的Fastly-Client-IP、Cloudflare的CF-Connecting-IP
    • 可以使用ClientIPHeader选项指定这些头部
  3. 自定义KeyGetter

    • 如果上述方案都不适用,可以使用自定义的KeyGetter函数

为什么选择Limiter

Limiter与其他速率限制包相比有以下优势:

  1. 简单清晰的API设计
  2. 灵活的存储后端支持
  3. 多种框架中间件支持
  4. 活跃的维护状态
  5. 完善的文档和示例

Limiter特别适合需要简单高效实现速率限制的场景,尤其适合Gin等Web框架的用户。


更多关于golang简单高效的请求速率限制中间件插件Limiter的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang简单高效的请求速率限制中间件插件Limiter的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang请求速率限制中间件Limiter使用指南

在Go语言中实现请求速率限制是保护API服务免受过载攻击的重要手段。下面我将介绍几种简单高效的速率限制中间件实现方式。

1. 使用golang.org/x/time/rate包

Go标准库中的golang.org/x/time/rate提供了令牌桶算法的实现,非常适合做速率限制。

package main

import (
	"net/http"
	"golang.org/x/time/rate"
	"time"
)

var limiter = rate.NewLimiter(rate.Every(time.Second), 10) // 每秒10个请求

func limitMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !limiter.Allow() {
			http.Error(w, "Too many requests", http.StatusTooManyRequests)
			return
		}
		next.ServeHTTP(w, r)
	})
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello, World!"))
	})
	
	limitedMux := limitMiddleware(mux)
	http.ListenAndServe(":8080", limitedMux)
}

2. 使用github.com/ulule/limiter/v3

ulule/limiter是一个功能更丰富的第三方限流库,支持多种存储后端。

package main

import (
	"net/http"
	"github.com/ulule/limiter/v3"
	"github.com/ulule/limiter/v3/drivers/middleware/stdlib"
	"github.com/ulule/limiter/v3/drivers/store/memory"
)

func main() {
	// 定义限流规则: 10 requests per minute
	rate := limiter.Rate{
		Period: 1 * time.Minute,
		Limit:  10,
	}
	
	store := memory.NewStore()
	instance := limiter.New(store, rate)
	
	middleware := stdlib.NewMiddleware(instance)
	
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello, World!"))
	})
	
	http.ListenAndServe(":8080", middleware.Handler(mux))
}

3. 基于IP的速率限制

更实用的场景是基于客户端IP进行限流:

package main

import (
	"net/http"
	"sync"
	"time"
	
	"golang.org/x/time/rate"
)

type IPRateLimiter struct {
	ips map[string]*rate.Limiter
	mu  *sync.RWMutex
	r   rate.Limit
	b   int
}

func NewIPRateLimiter(r rate.Limit, b int) *IPRateLimiter {
	return &IPRateLimiter{
		ips: make(map[string]*rate.Limiter),
		mu:  &sync.RWMutex{},
		r:   r,
		b:   b,
	}
}

func (i *IPRateLimiter) AddIP(ip string) *rate.Limiter {
	i.mu.Lock()
	defer i.mu.Unlock()
	
	limiter := rate.NewLimiter(i.r, i.b)
	i.ips[ip] = limiter
	
	return limiter
}

func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter {
	i.mu.Lock()
	limiter, exists := i.ips[ip]
	
	if !exists {
		i.mu.Unlock()
		return i.AddIP(ip)
	}
	
	i.mu.Unlock()
	return limiter
}

func limitMiddleware(next http.Handler) http.Handler {
	limiter := NewIPRateLimiter(1, 5) // 每秒1个请求,桶大小5
	
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ip := r.RemoteAddr
		if !limiter.GetLimiter(ip).Allow() {
			http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
			return
		}
		
		next.ServeHTTP(w, r)
	})
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello, World!"))
	})
	
	http.ListenAndServe(":8080", limitMiddleware(mux))
}

4. 分布式限流

对于分布式系统,可以使用Redis作为存储后端:

package main

import (
	"net/http"
	"github.com/ulule/limiter/v3"
	"github.com/ulule/limiter/v3/drivers/middleware/stdlib"
	"github.com/ulule/limiter/v3/drivers/store/redis"
)

func main() {
	// 定义限流规则
	rate := limiter.Rate{
		Period: 1 * time.Hour,
		Limit:  1000,
	}
	
	// 创建Redis存储
	store, err := redis.NewStoreWithOptions(redis.Options{
		Address: "localhost:6379",
		Prefix:  "limiter_",
	})
	if err != nil {
		panic(err)
	}
	
	// 创建限流器实例
	instance := limiter.New(store, rate)
	
	// 创建中间件
	middleware := stdlib.NewMiddleware(instance)
	
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello, World!"))
	})
	
	http.ListenAndServe(":8080", middleware.Handler(mux))
}

最佳实践建议

  1. 根据API端点的重要性设置不同的限流阈值
  2. 生产环境建议使用Redis等分布式存储
  3. 对认证用户和匿名用户使用不同的限流策略
  4. 在响应头中添加限流信息(X-RateLimit-Limit, X-RateLimit-Remaining等)
  5. 考虑实现滑动窗口算法以获得更精确的控制

以上示例提供了从简单到复杂的多种限流实现方式,您可以根据项目需求选择合适的方案。

回到顶部