Gubernator高性能分布式限速微服務(wù)
Gubernator 作為微服務(wù)的主要特性是,它為進(jìn)入系統(tǒng)的許多請(qǐng)求創(chuàng)建了一個(gè)同步點(diǎn)。在幾微秒內(nèi)接收到的請(qǐng)求可以被優(yōu)化并協(xié)調(diào)成批,從而減少服務(wù)在重載下使用的總帶寬和往返延遲。多個(gè)服務(wù)都運(yùn)行在單個(gè)主機(jī)上,并且所有服務(wù)都在各自的進(jìn)程中運(yùn)行相同的庫,但它們沒有此功能。
Gubernator 的特性
- Gubernator 在整個(gè)集群中均勻地分布速率限制請(qǐng)求,這樣用戶就可以添加更多的節(jié)點(diǎn)來擴(kuò)展系統(tǒng)。
- Gubernator 不依賴于 Memcache 或 Redis 等外部緩存,因此部署時(shí)不存在服務(wù)依賴。這使得在諸如 kubernetes 或 nomad 的編排系統(tǒng)中能動(dòng)態(tài)增長或縮小集群。
- Gubernator 在磁盤上不保存狀態(tài),它的配置是由客戶機(jī)根據(jù)每個(gè)請(qǐng)求傳遞給它的。
- Gubernator 提供了對(duì)其 API 的 GRPC 和 HTTP 訪問。可以根據(jù)需要限制速率的陪伴服務(wù)運(yùn)行,也可以作為獨(dú)立的服務(wù)運(yùn)行。
- 可以用作庫來實(shí)現(xiàn)特定領(lǐng)域的限速服務(wù)。
- 支持對(duì)高吞吐量環(huán)境進(jìn)行定制化的一致速率限制服務(wù)。
- Gubernator 是 俄語中 governor 的英文發(fā)音 ,聽起來也很酷。
示例配置:
rate_limits:
# Scopes the request to a specific rate limit
- name: requests_per_sec
# A unique_key that identifies this instance of a rate limit request
unique_key: account_id=123|source_ip=172.0.0.1
# The number of hits we are requesting
hits: 1
# The total number of requests allowed for this rate limit
limit: 100
# The duration of the rate limit in milliseconds
duration: 1000
# The algorithm used to calculate the rate limit
# 0 = Token Bucket
# 1 = Leaky Bucket
algorithm: 0
# The behavior of the rate limit in gubernator.
# 0 = BATCHING (Enables batching of requests to peers)
# 1 = NO_BATCHING (Disables batching)
# 2 = GLOBAL (Enable global caching for this rate limit)
Gubernator 是無狀態(tài)的,因?yàn)樗恍枰疟P空間來操作。不需要任何配置或緩存數(shù)據(jù)同步到磁盤,這是因?yàn)閷?duì) Gubernator 的每個(gè)請(qǐng)求都包含速率限制的配置。
首先,你可能認(rèn)為這對(duì)每個(gè)請(qǐng)求都是不必要的開銷。然而,實(shí)際上,速率限制配置僅由 4 個(gè) 64 位整數(shù)組成。配置由限制、持續(xù)時(shí)間、算法和行為組成 (有關(guān)工作原理的詳細(xì)信息,請(qǐng)參閱下面)。正是由于這種簡(jiǎn)單的配置,Gubernator 可以用來提供客戶端可以使用的各種速率限制用例。其中一些用例如下:
- 入口限制:典型的基于 HTTP 的 402 多請(qǐng)求類型限制。
- 流量減少:當(dāng) API 處于不佳狀態(tài)時(shí),只拒絕新的或未經(jīng)身份驗(yàn)證的請(qǐng)求。
- 出口限制:用數(shù)百萬條消息轟炸外部 SMTP 服務(wù)器并非易事。
- 隊(duì)列處理:知道何時(shí)可以立即處理請(qǐng)求,或者應(yīng)該按照接收請(qǐng)求的順序排隊(duì)和處理請(qǐng)求。
- API 容量管理:對(duì)一個(gè)集合 API 系統(tǒng)能夠處理的請(qǐng)求總數(shù)設(shè)置全局限制。拒絕或?qū)`反系統(tǒng)正常操作能力的請(qǐng)求進(jìn)行排隊(duì)。
除了上面提到的用例,無配置設(shè)計(jì)對(duì)微服務(wù)的設(shè)計(jì)和部署有重要的影響:
- 部署時(shí)不用配置同步。當(dāng)使用 Gubernator 的服務(wù)被部署時(shí),不用預(yù)先部署到 Gubernator 的速率限制配置。
- 使用 Gubernator 的服務(wù)擁有其問題空間的速率極限域模型。這使得 Gubernator 無法獲得領(lǐng)域特定的知識(shí),因此 Gubernator 可以專注于它最擅長的事情——速率限制!
在這些問題之外,下面就從 Gubernator 的工作原理開始,討論更多關(guān)于 Gubernator 的內(nèi)容。
Gubernator 的工作原理
Gubernator 被設(shè)計(jì)成一個(gè)分布式的對(duì)等點(diǎn)集群,它利用了內(nèi)存中所有當(dāng)前活動(dòng)速率限制的緩存,因?yàn)椴挥脤?shù)據(jù)同步到磁盤。由于大多數(shù)基于網(wǎng)絡(luò)的速率限制持續(xù)時(shí)間只有幾秒鐘,因此在重啟或計(jì)劃停機(jī)期間丟失內(nèi)存緩存并不是什么大問題。對(duì)于 Gubernator,我們選擇性能而不是精度,因?yàn)樵诰彺鎭G失的情況下,一小部分流量在短時(shí)間內(nèi) (通常是幾秒鐘) 超過請(qǐng)求是可以接受的。
當(dāng)向 Gubernator 發(fā)出速率限制請(qǐng)求時(shí),將鍵入該請(qǐng)求并應(yīng)用一致的哈希算法來確定哪個(gè)對(duì)等點(diǎn)將是速率限制請(qǐng)求的所有者。為速率限制選擇單個(gè)所有者可以使計(jì)數(shù)的原子增量非常快,并且避免了在對(duì)等集群中一致地分布計(jì)數(shù)所涉及的復(fù)雜性和延遲。
盡管簡(jiǎn)單且性能良好,但是這種設(shè)計(jì)可能會(huì)受到一大堆請(qǐng)求的影響,因?yàn)橐粋€(gè)協(xié)調(diào)器可能要處理成千上萬個(gè)請(qǐng)求,而且速度有限。
為了解決這個(gè)問題,客戶機(jī)可以請(qǐng)求 Behaviour=BATCHING,它允許對(duì)等點(diǎn)在指定的窗口內(nèi)接受多個(gè)請(qǐng)求 (缺省值為 500 微秒),并將請(qǐng)求批處理為單個(gè)對(duì)等點(diǎn)請(qǐng)求,從而極大地減少了通過網(wǎng)絡(luò)向單個(gè) Gubernator 對(duì)等點(diǎn)發(fā)送請(qǐng)求的總數(shù)。
為了確保集群中的每個(gè)對(duì)等點(diǎn)準(zhǔn)確地計(jì)算速率限制鍵的正確散列,必須以及時(shí)和一致的方式將集群中的對(duì)等點(diǎn)列表分發(fā)給集群中的每個(gè)對(duì)等點(diǎn)。目前,Gubernator 支持使用 etcd 或 kubernetes 端點(diǎn) API 來發(fā)現(xiàn) Gubernator 對(duì)等點(diǎn)。
Gubernator 操作
當(dāng)客戶機(jī)或服務(wù)向 Gubernator 發(fā)出請(qǐng)求時(shí),客戶機(jī)將為每個(gè)請(qǐng)求提供速率限制配置。然后,速率限制配置與當(dāng)前速率限制狀態(tài)一起存儲(chǔ)在速率限制所有者的本地緩存中。存儲(chǔ)在本地緩存中的速率限制及其配置僅在速率限制配置的指定持續(xù)時(shí)間內(nèi)存在。
在持續(xù)時(shí)間過期之后,如果在此期間沒有再次請(qǐng)求速率限制,則從緩存中刪除它。對(duì)相同名稱和 unique_key 對(duì)的后續(xù)請(qǐng)求將在緩存中重新創(chuàng)建配置和速率限制,這個(gè)循環(huán)將重復(fù)。另一方面,具有不同配置的后續(xù)請(qǐng)求將覆蓋以前的配置并立即應(yīng)用新配置。
由于 Gubernator 速率限制是由集群中的單個(gè)對(duì)等點(diǎn)哈希和處理的,所以適用于數(shù)據(jù)中心中的每個(gè)請(qǐng)求的速率限制將導(dǎo)致單個(gè)對(duì)等點(diǎn)處理整個(gè)數(shù)據(jù)中心的速率限制請(qǐng)求。
例如,考慮 name=requests_per_datacenter 和 unique_id=us-east-1 的速率限制。現(xiàn)在,假設(shè)對(duì)每個(gè)進(jìn)入 us-east-1 數(shù)據(jù)中心的 HTTP 請(qǐng)求都使用這個(gè)速率限制向 Gubernator 發(fā)出請(qǐng)求。這可能是每秒數(shù)十萬個(gè)請(qǐng)求,甚至可能是數(shù)百萬個(gè)請(qǐng)求,這些請(qǐng)求都由集群中的一個(gè)對(duì)等點(diǎn)哈希并處理。由于這個(gè)潛在的可伸縮性問題,Gubernator 引入了一個(gè)名為 GLOBAL 的可配置 behavior。
當(dāng)速率限制配置為 behavior=GLOBAL 時(shí),從客戶機(jī)接收到的速率限制請(qǐng)求將不會(huì)轉(zhuǎn)發(fā)給擁有它的對(duì)等方。相反,它將從接收請(qǐng)求的對(duì)等方處理的內(nèi)部緩存中得到響應(yīng)。Hits 速率限制的點(diǎn)擊率將由接收對(duì)等點(diǎn)批量處理,并異步發(fā)送到擁有該點(diǎn)擊率的對(duì)等點(diǎn),在該對(duì)等點(diǎn)上,點(diǎn)擊率將被總計(jì)并得出 OVER_LIMIT。然后,擁有節(jié)點(diǎn)的節(jié)點(diǎn)有責(zé)任用速率限制的當(dāng)前狀態(tài)更新集群中的每個(gè)節(jié)點(diǎn),這樣,節(jié)點(diǎn)內(nèi)部緩存就會(huì)定期從所有者那里獲得最新速率限制狀態(tài)的更新。
Global Behavior 的其他影響
由于 Hits 是批量處理并異步轉(zhuǎn)發(fā)給擁有它的對(duì)等點(diǎn)的,所以對(duì)客戶機(jī)的即時(shí)響應(yīng)將不包括最精確的 remaining 計(jì)數(shù)。只有在對(duì)所有者對(duì)等點(diǎn)的異步調(diào)用完成并且擁有對(duì)等點(diǎn)有時(shí)間更新集群中的所有對(duì)等點(diǎn)之后,該計(jì)數(shù)才會(huì)得到更新。因此,使用 GLOBAL 允許更大的集群規(guī)模,但要以一致性為代價(jià)。如果集群足夠大,使用 GLOBAL 可以增加每速率限制請(qǐng)求的通信量。 GLOBAL 應(yīng)該只用于與傳統(tǒng)的非 GLOBAL 行為不兼容的高容量速率限制。
Gubernator 性能
在我們的生產(chǎn)環(huán)境中,每向我們的 API 發(fā)送一個(gè)請(qǐng)求,我們就向 Gubernator 發(fā)送兩個(gè)速率限制請(qǐng)求來評(píng)估速率限制;一個(gè)用于對(duì) HTTP 請(qǐng)求進(jìn)行評(píng)級(jí),另一個(gè)用于對(duì)用戶在特定時(shí)間內(nèi)也可以發(fā)送電子郵件的收件人數(shù)量進(jìn)行評(píng)級(jí)。在這種設(shè)置下,一個(gè) Gubernator 節(jié)點(diǎn)每秒處理超過 2000 個(gè)請(qǐng)求,大多數(shù)批量響應(yīng)在 1 毫秒內(nèi)返回。
轉(zhuǎn)發(fā)給擁有節(jié)點(diǎn)的對(duì)等請(qǐng)求通常在 30 微秒內(nèi)響應(yīng)。
- NOTE
- The
- above
- graphs
- only report
- the slowest
- request
- within the
- 1 second
- sample
- time. So
- you are
- seeing the
- slowest
- requests
- that
- Gubernator
- fields to
- clients.
由于許多面向公眾的 API 都是用 python 編寫的,所以我們?cè)谝粋€(gè)節(jié)點(diǎn)上運(yùn)行許多 python 解釋器實(shí)例。這些 python 實(shí)例將本地請(qǐng)求轉(zhuǎn)發(fā)給 Gubernator 實(shí)例,然后 Gubernator 實(shí)例將請(qǐng)求批處理并轉(zhuǎn)發(fā)給擁有節(jié)點(diǎn)的節(jié)點(diǎn)。
Gubernator 允許用戶選擇非批處理行為,這將進(jìn)一步減少客戶機(jī)速率限制請(qǐng)求的延遲。但是,由于吞吐量需求,我們的生產(chǎn)環(huán)境使用默認(rèn)的 500 微秒窗口使用 Behaviour=BATCHING。在生產(chǎn)中,我們觀察到在 API 使用高峰期間,批處理大小為 1000。其他不具有相同高流量需求的用戶可以禁用批處理,并以吞吐量為代價(jià)降低延遲。
Gubernator 開發(fā)庫
如果使用 Golang,可以使用 Gubernator 作為開發(fā)庫。這對(duì)你希望在頂部實(shí)現(xiàn)一個(gè)公司特有模型的速率限制服務(wù)時(shí)非常有用。我們?cè)?Mailgun 內(nèi)部有一項(xiàng)名為“ratelimits”的服務(wù),專門跟蹤每個(gè)賬戶的限額。通過這種方式,你可以利用 Gubernator 的強(qiáng)大功能和速度,同時(shí)可以分層業(yè)務(wù)邏輯,并將特定領(lǐng)域的問題集成到速率限制服務(wù)中。
性能表現(xiàn):
下面是一個(gè)單節(jié)點(diǎn)每秒 2000 個(gè)請(qǐng)求的測(cè)試結(jié)果:
轉(zhuǎn)發(fā)給擁有節(jié)點(diǎn)的對(duì)等請(qǐng)求通常在30微秒內(nèi)響應(yīng)
