iT邦幫忙

2023 iThome 鐵人賽

DAY 28
0
自我挑戰組

Concurrency in go 讀書心得系列 第 28

28.Rate limiting-2

  • 分享至 

  • xImage
  •  

下面的程式碼提供方法,使得在多個時間粒度上都能夠實現速率限制。這樣可以為你的API或其他資源設置多層的保護機制,以避免被過度使用或遭受攻擊。

  1. 複雜的速率限制:這段程式的目的是為了實現複雜的速率限制,即在不同的時間粒度上設置不同的速率限制(例如:每秒、每分鐘)。

  2. APIConnection:這是一個模擬的API連接。這個連接有兩個方法:ReadFileResolveAddress,兩者在被調用前都會進行速率限制的檢查。

  3. 速率限制策略

    • 限制每秒最多2次的請求,並允許1個請求進入。
    • 限制每分鐘最多10次的請求,並允許10個請求進入。
      這兩個限制器被組合在 MultiLimiter 中,該限制器會對多個限制器進行排序和管理。
  4. RateLimiter 接口:定義了限制器需要實現的方法,包括 WaitLimit。其中,Wait 用於阻止或延遲請求,以確保不超過設定的速率;Limit 返回當前的限制速率。

  5. multiLimiter 結構:一個自定義的多限制器,它包含了一個限制器的切片。在這個限制器中,所有的請求都必須通過每個子限制器的檢查,且按照速率從慢到快的順序進行檢查。

package main

import (
	"context"
	"golang.org/x/time/rate"
	"log"
	"os"
	"sort"
	"sync"
	"time"
)

func main() {
	defer log.Printf("Done.")
	log.SetOutput(os.Stdout)
	log.SetFlags(log.Ltime | log.LUTC)

	apiConnection := Open()
	var wg sync.WaitGroup
	wg.Add(20)

	for i := 0; i < 10; i++ {
		go func() {
			defer wg.Done()
			err := apiConnection.ReadFile(context.Background())
			if err != nil {
				log.Printf("cannot ReadFile: %v", err)
			}
			log.Printf("ReadFile")
		}()
	}

	for i := 0; i < 10; i++ {
		go func() {
			defer wg.Done()
			err := apiConnection.ResolveAddress(context.Background())
			if err != nil {
				log.Printf("cannot ResolveAddress: %v", err)
			}
			log.Printf("ResolveAddress")
		}()
	}

	wg.Wait()
}
func Per(eventCount int, duration time.Duration) rate.Limit {
	return rate.Every(duration / time.Duration(eventCount))
}
func Open() *APIConnection {
	secondLimit := rate.NewLimiter(Per(2, time.Second), 1)   // <1>
	minuteLimit := rate.NewLimiter(Per(10, time.Minute), 10) // <2>
	return &APIConnection{
		rateLimiter: MultiLimiter(secondLimit, minuteLimit), // <3>
	}
}

type APIConnection struct {
	rateLimiter RateLimiter
}

func (a *APIConnection) ReadFile(ctx context.Context) error {
	if err := a.rateLimiter.Wait(ctx); err != nil {
		return err
	}
	// Pretend we do work here
	return nil
}

func (a *APIConnection) ResolveAddress(ctx context.Context) error {
	if err := a.rateLimiter.Wait(ctx); err != nil {
		return err
	}
	// Pretend we do work here
	return nil
}

type RateLimiter interface { // <1>
	Wait(context.Context) error
	Limit() rate.Limit
}

func MultiLimiter(limiters ...RateLimiter) *multiLimiter {
	byLimit := func(i, j int) bool {
		return limiters[i].Limit() < limiters[j].Limit()
	}
	sort.Slice(limiters, byLimit) // <2>
	return &multiLimiter{limiters: limiters}
}

type multiLimiter struct {
	limiters []RateLimiter
}

func (l *multiLimiter) Wait(ctx context.Context) error {
	for _, l := range l.limiters {
		if err := l.Wait(ctx); err != nil {
			return err
		}
	}
	return nil
}

func (l *multiLimiter) Limit() rate.Limit {
	return l.limiters[0].Limit() // <3>
}


上一篇
27.Rate limiting-1
下一篇
29.Healing unhealthy goroutines
系列文
Concurrency in go 讀書心得30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言