Mari bayangkan senario bahawa seseorang mempunyai aplikasi teragih yang berinteraksi dengan API pihak ketiga. Biasanya, API pihak ketiga mempunyai mekanisme kawalan had kadar untuk mengelakkan pelanggan mereka daripada meletus permintaan dan menyebabkan masa berhenti pada perkhidmatan mereka. Dalam senario sedemikian, bagaimanakah pemanggil boleh mengawal kadar permintaan keluar kepada API pihak ketiga dalam persekitaran yang diedarkan? Siaran ini membincangkan strategi yang mungkin untuk masalah ini.
Terdapat berbilang algoritma untuk mengawal kadar permintaan, tetapi di sini kita akan menumpukan pada algoritma baldi token, kerana ia agak mudah difahami dan dilaksanakan. Algoritma ini menyatakan bahawa: baldi boleh memuatkan maksimum T token, dan apabila aplikasi ingin membuat permintaan kepada API pihak ketiga, ia perlu mengambil 1 token dari baldi. Sekiranya baldi kosong, ia perlu menunggu sehingga terdapat sekurang-kurangnya 1 token dalam baldi. Selain itu, baldi diisi semula dengan 1 token pada kadar tetap R token/milisaat.
Algoritma baldi token sangat mudah untuk difahami, tetapi bagaimanakah seseorang boleh menggunakannya dalam persekitaran yang diedarkan untuk mengawal permintaan keluar kepada API pihak ketiga?
Jika seseorang ingin mengawal had kadar keluar dalam persekitaran yang diedarkan, sumber kebenaran terpusat untuk had kadar semasa adalah perlu. Terdapat pelbagai cara untuk melaksanakan sumber kebenaran dan saya telah mengidamkan rajah berikut dengan kemungkinan pelaksanaan:
Dalam rajah di atas, kami mempunyai aplikasi yang diedarkan dalam berbilang pod, dan setiap pod boleh membuat permintaan kepada API pihak ketiga. Dalam infrastruktur aplikasi, terdapat pelayan TCP yang mengawal had kadar dengan menggunakan algoritma baldi token. Sebelum membuat permintaan kepada API pihak ketiga, pod meminta pelayan TCP untuk token baharu dan pod menunggu respons daripada pelayan TCP sehingga terdapat sekurang-kurangnya satu token yang tersedia. Selepas token tersedia, pod membuat permintaan kepada API pihak ketiga.
Pelaksanaan pelayan TCP boleh didapati dalam repositori ini https://github.com/rafaquelhodev/rlimit/ dan dalam bahagian seterusnya saya akan membincangkan secara ringkas pelaksanaan baldi token dalam golang.
Di bawah, saya menunjukkan idea utama di sebalik pelaksanaan baldi token. Sila lihat di https://github.com/rafaquelhodev/rlimit/ repositori untuk memahami pelaksanaan terperinci.
Kawalan had kadar dipusatkan dalam struct TokenBucket:
type TokenBucket struct { id string mu sync.Mutex tokens int64 maxTokens int64 refillPeriod int64 cron chan bool subs []chan bool }
Anda dapat melihat bahawa terdapat sifat subs dalam struct TokenBucket. Pada asasnya, ini ialah tatasusunan pelanggan untuk baldi token tertentu: setiap kali token diminta daripada pelanggan, pelanggan ditambahkan pada tatasusunan subs dan pelanggan dimaklumkan apabila token baharu ditambahkan pada baldi.
Apabila memulakan baldi, kami perlu menyediakan bilangan maksimum token baldi boleh menyokong (Token maks) dan jumlah masa token ditambahkan pada baldi (refillPeriod):
func newTokenBucket(id string, maxTokens int64, refillPeriod int64) *TokenBucket { bucket := &TokenBucket{ id: id, tokens: 0, maxTokens: maxTokens, refillPeriod: refillPeriod, cron: make(chan bool), subs: make([]chan bool, 0), } fmt.Printf("refill period = %d\n", refillPeriod) bucket.startCron() return bucket }
Kini, anda mungkin tertanya-tanya, "bagaimana token ditambahkan pada baldi?". Untuk itu, apabila baldi dibuat, kerja cron dimulakan dan pada setiap milisaat Tempoh pengisian semula, token baharu ditambahkan pada baldi:
func (tb *TokenBucket) startCron() { ticker := time.NewTicker(time.Duration(tb.refillPeriod) * time.Millisecond) go func() { for { select { case <-tb.cron: ticker.Stop() return case <-ticker.C: if tb.tokens < tb.maxTokens { tb.tokens += 1 fmt.Printf("[TOKEN REFIL] | currTokens = %d\n", tb.tokens) if len(tb.subs) > 0 { sub := tb.subs[0] tb.subs = tb.subs[1:] sub <- true } } } } }() }
Akhir sekali, apabila pelanggan mahukan token daripada baldi, fungsi waitAvailable mesti dipanggil:
func (tb *TokenBucket) waitAvailable() bool { tb.mu.Lock() if tb.tokens > 0 { fmt.Printf("[CONSUMING TOKEN] - id = %s\n", tb.id) tb.tokens -= 1 tb.mu.Unlock() return true } fmt.Printf("[WAITING TOKEN] - id %s\n", tb.id) ch := tb.tokenSubscribe() tb.mu.Unlock() <-ch fmt.Printf("[NEW TOKEN AVAILABLED] - id %s\n", tb.id) tb.tokens -= 1 return true }
Diinspirasikan oleh https://github.com/Mohamed-khattab/Token-bucket-rate-limiter
Atas ialah kandungan terperinci Mengawal had kadar keluar. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!