package main
import (
"log"
"net"
"net/http"
"time"
)
// 漏桶结构体
type LeakyBucket struct {
rate int // 漏桶的流出速率,单位:个/秒
bucket chan struct{} // 漏桶,用于存放请求
}
// 初始化漏桶
func NewLeakyBucket(rate int) *LeakyBucket {
return &LeakyBucket{
rate: rate,
bucket: make(chan struct{}, rate),
}
}
// 处理HTTP请求
func handler(w http.ResponseWriter, r *http.Request, lb *LeakyBucket) {
if r.URL.Path == "/index" && r.Method == "GET" {
// 如果桶中还有剩余的请求,则放行
select {
case lb.bucket <- struct{}{}:
defer func() { <-lb.bucket }()
default:
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
return
}
}
w.Write([]byte("Hello, world!"))
}
func main() {
// 创建一个新的漏桶,设置速率为每秒10个请求
lb := NewLeakyBucket(10)
// 创建一个HTTP服务器,监听在localhost:8080地址上
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
defer listener.Close()
server := http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handler(w, r, lb)
}),
}
// 启动HTTP服务器,并使用漏桶算法进行限流
go func() {
err := server.Serve(listener)
if err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}()
// 漏桶流出速率为1个/秒
interval := time.Second / time.Duration(lb.rate)
ticker := time.NewTicker(interval)
defer ticker.Stop()
// 漏桶流出请求
for range ticker.C {
select {
case <-lb.bucket:
default:
}
}
}
我们首先定义了一个LeakyBucket结构体,包含漏桶的流出速率和桶本身。使用NewLeakyBucket函数初始化一个新的LeakyBucket对象,并设置漏桶的流出速率和桶的大小。在处理HTTP请求时,我们使用handler函数进行限流,对请求路径为/index和请求方法为GET的请求进行限流,其他请求则不限流。如果漏桶中还有剩余的请求,则将请求放入桶中,否则返回HTTP状态码429(Too Many Requests)。
在main函数中,我们首先初始化一个新的漏桶对象,然后使用net包创建一个TCP listener对象,监听在localhost:8080地址上。然后创建一个HTTP服务器,并使用handler函数作为HTTP请求的处理函数。在启动HTTP服务器之前,我们使用goroutine来定时从漏桶中流出请求,以控制请求的速率。具体地,我们使用time包创建一个ticker对象,每隔一定时间流出一个请求,直到漏桶被耗尽为止。