golang官方限流器rate

包名:golang.org/x/time/rate

实现原理:令牌桶

package main

import (
	"context"
	"fmt"
	"testing"
	"time"

	"golang.org/x/time/rate"
)

func TestLimiter(t *testing.T) {
	// 第一个参数代表速率:Every代表每xx时间产生一个,比如这里的每0.1秒生产一个,换算一下就是每秒10个,
	// 第二个参数代表桶的大小:即,如果没有人来消费,那么桶里最多存着10个令牌,
	// 桶的初始状态是满的,后面才会按照速率来提供令牌,这一点很重要,
	l := rate.NewLimiter(rate.Every(time.Second/10), 10)

	for i := 0; i < 10; i++ {
		go func(i int) {
			for {
				if l.Allow() {
					fmt.Printf("allow %d\n", i)
				}
				// 每0.5秒请求一次
				time.Sleep(time.Second / 2)
			}
		}(i)
	}
	time.Sleep(time.Second * 10)
}

func TestLimiter2(t *testing.T) {
	l := rate.NewLimiter(rate.Every(time.Second), 10)

	for i := 0; i < 10; i++ {
		go func(i int) {
			for {
				ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(1*time.Second))

				// 等待获取令牌,可以传入 deadline context 设置最大等待时间
				// 这样在某些场景下我们可以让请求等待一会,而不是直接失败。
				if err := l.Wait(ctx); err != nil {
					fmt.Printf("timwout: %v\n", err)
				} else {
					fmt.Printf("allow %d\n", i)
				}
				cancel()
				time.Sleep(time.Second / 2)
			}
		}(i)
	}
	time.Sleep(time.Second * 10)
}

func TestLimiter3(t *testing.T) {
	l := rate.NewLimiter(rate.Every(time.Second), 10)

	for i := 0; i < 10; i++ {
		go func(i int) {
			for {
				// 先预留令牌,到指定时间不再需要去获取令牌,直接执行操作
				// 如果预留的令牌不想使用了,也可以使用r.Cancel()归还已预留的令牌
				if r := l.Reserve(); r.OK() {
					// 休眠直到令牌生效
					time.Sleep(r.Delay())
					fmt.Printf("allow %d\n", i)
				}
				time.Sleep(time.Second / 2)
			}
		}(i)
	}
	time.Sleep(time.Second * 10)
}

封装到 gin 中间件

func Limiter(l *rate.Limiter) gin.HandlerFunc {
	return func(c *gin.Context) {
		if !l.Allow() {
			c.AbortWithStatus(http.StatusTooManyRequests)
		}

		c.Next()
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Go 中,可以使用 channel 实现限流,即通过 channel 的容量控制并发执行的协程数。 示例代码如下: ```go package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("worker %d 执行任务 %d\n", id, j) time.Sleep(time.Second) // 模拟任务执行时间 results <- j * 2 } } func main() { jobs := make(chan int, 100) // 创建任务 channel,缓冲区大小为 100 results := make(chan int, 100) // 创建结果 channel,缓冲区大小为 100 // 创建 3 个 worker 协程,即最多同时执行 3 个任务 for w := 1; w <= 3; w++ { go worker(w, jobs, results) } // 发送 9 个任务到 jobs channel for j := 1; j <= 9; j++ { jobs <- j } close(jobs) // 关闭 jobs channel,表示所有任务已发送完毕 // 收集所有结果 for a := 1; a <= 9; a++ { <-results } } ``` 在上面的示例中,我们创建了一个 `jobs` channel 和一个 `results` channel,用于分别传递任务和结果。我们创建了 3 个 worker 协程,并将 `jobs` 和 `results` channel 分别传递给它们。在主协程中,我们向 `jobs` channel 发送 9 个任务,并关闭 `jobs` channel,表示所有任务已发送完毕。然后我们收集所有结果。 由于 `jobs` channel 的缓冲区大小为 100,即最多可以存储 100 个任务,而 `results` channel 的缓冲区也为 100,即最多可以存储 100 个结果。因此,当 worker 协程数小于等于 3 时,所有任务都可以立即执行;当 worker 协程数大于 3 时,多余的任务会被存储在 `jobs` channel 中,直到有空闲的 worker 协程可以执行它们。这样就实现了限流的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值