GitHub如何在Redis中使用分片的複製速率限制器擴充套件API

banq發表於2021-04-06


大約一年前,我們GitHub遷移了一箇舊的速率限制器,以提供更多的流量並適應更具彈性的平臺體系結構。我們採用了帶有客戶端分片的複製Redis後端。最終,效果很好,但是我們在此過程中吸取了一些教訓。
 

Memcached問題
我們以前有一個很簡單的舊速率限制器:

  • 對於每個請求,確定當前速率限制的“key”
  • 在Memcached中,遞增該key的值,如果沒有任何當前值,則將其設定為1
  • 另外,如果還沒有,請使用相關的key(例如“ #{key}:reset_at”)在Memcached中設定“reset at”值
  • 遞增時,如果“ reset at”值是過去的值,則忽略現有值並設定一個新的“ reset at”
  • 在每個請求的開始,如果key的值大於限制,並且“reset at”是未來的值,則拒絕該請求

(可能會有更多細微差別,但這是主要思想。)
但是,此限制器有兩個問題:
  • 我們的Memcached架構是由於更改而來的。由於它主要用作快取層,因此我們將從一個共享Memcached切換到每個資料中心的單個Memcached。儘管對於應用程式快取來說這很好用,但是如果將客戶端請求路由到不同的資料中心,它將使我們的速率限制器的行為非常奇怪。
  • Memcached的“永續性”不適用於我們。Memcached後端由速率限制器和其他應用程式快取共享,這意味著,當記憶體被填滿時,即使它仍處於活動狀態,有時也會刪除速率限制器資料。

 

Redis
經過一番討論,我們決定為速率限制器設計一種新的設計:

  • 使用Redis,因為它具有更合適的永續性系統以及簡單的分片和複製設定
  • 在應用程式內部分片:應用程式將為每個鍵選擇要從中讀取和寫入的Redis叢集
  • 為了減輕Redis的CPU約束性,請在每個群集中放置一個主資料庫(用於寫入)和幾個副本(用於讀取)
  • 與其在資料庫中編寫“ reset at”(重置為),不如使用Redis過期功能使值在不再適用時消失
  • 在Lua中實現儲存邏輯,以確保操作的原子性(這是對先前設計的改進)

我們從兩個出色的現有資源中汲取靈感:

解決了各種問題之後,新的速率限制器效果很好。它提高了可靠性,為客戶解決了固定問題,並減少了我們的支援負載。
詳細原文點選標題。


 

相關文章