golang实现限制每秒多少次的限频操作

前言

一些函数的执行可能会限制频率,比如某个api接口要求每秒最大请求30次。下面记录了自己写的限频和官方的限频

代码

// 加锁限频,输出次数大概率小于最大值
func ExecLimit(lastExecTime *time.Time, l *sync.RWMutex ,maxTimes int, perDuration time.Duration, f func()) {
    l.Lock()
    defer l.Unlock()
	// per times cost time(s)
	SecondsPerTimes := float64(perDuration) / float64(time.Second) / float64(maxTimes)

	now := time.Now()
	interval := now.Sub(*lastExecTime).Seconds()
	if interval < SecondsPerTimes {
		time.Sleep(time.Duration(int64((SecondsPerTimes-interval)*1000000000)) * time.Nanosecond)
	}
	f()
	*lastExecTime = time.Now()
}

// 官方的,需要引用	"golang.org/x/time/rate"
// 基本上可以达到满值,比自己写的更优
func ExecLimit2(l *rate.Limiter, f func()) {
	go func() {
		l.Wait(context.Background())
		f()
	}()
}

使用

func TestExecLimit(t *testing.T) {
	runtime.GOMAXPROCS(runtime.NumCPU())
	go func() {
		var lastExecTime time.Time
		var l sync.RWMutex
		for {
			ExecLimit(&lastExecTime, &l, 10, time.Second, func() {
				fmt.Println("do")
			})
		}
	}()

	select {
	case <-time.After(1 * time.Second):
		fmt.Println("1秒到时")
	}
}

func TestExecLimit2(t *testing.T) {
	runtime.GOMAXPROCS(runtime.NumCPU())

	l := rate.NewLimiter(1, 30)

	go func() {
		for {
            ExecLimit2(l, func() {
				fmt.Println("do")
			})
		}
	}()

	select {
	case <-time.After(1 * time.Second):
		fmt.Println("1秒到时")
	}
}

输出:

一秒内输出了<=10次 "do"

如何在多节点服务中限制频

上述使用,定义在某个服务节点的全局变量lastExecTime仅仅会对该服务的函数f()操作限频,如果在负载均衡后,多个相同服务的节点,对第三方的接口累计限频,比如三个服务共同拉取第三方接口,合计限频为30次/s.
则,必须将lastExecTime的获取,从redis等共享中间件中获取,而不应该从任何一个单点服务获取。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章