我們如何使用Go打造了Uber QPS最高的服務

2021-08-19 12:15:57 字數 1997 閱讀 3634

原文:how we built uber engineering』s highest query per second service using go

譯者:孫薇

2023年初,我們建立了乙個微服務來負責這項任務:地理圍欄查詢(geofence lookups),結果完成很出色。如今已過一年,這項技術在uber數以百計的生產應用中脫穎而出,成為了每秒查詢量最高(qps)的服務。本文講述了我們建立這個服務的原因,還有近來go語言對構建和擴充套件該服務速度的貢獻。

在uber,地理圍欄指的是地面上由人為定義的地理區域(或幾何術語中的多邊形),廣泛用於地理位置的配置中。向使用者展示在指定位置上有哪些產品可用,根據特定需求(比如機場)定義區域,在同時有多人請求搭車的周邊區域執行動態定價,這些都非常重要。下圖是位於科羅拉多州的乙個地理圍欄樣例:

第一步是檢索地理位置的配置,根據使用者的手機定位,查詢經緯度之類的資訊,以確定該位置處於哪個地理圍欄中。這個功能曾經在多個服務/模組中都有實現,不過隨著從單體架構遷移到面向(微)服務架構,我們選擇將這個功能整合在新的單體微服務中。

根據我們的評估,那時最適合市場團隊的語言是node.js,因為我們在這種語言上有更多的內部知識和經驗。但是,出於下面這些原因,go更符合我們的需求:

我們如何根據經緯度指定的位置,在成千上萬個地理圍欄中查詢它屬於其中的哪乙個?使用簡單匹配演算法(brute-force)非常簡單:只要一一檢視所有地理圍欄,並使用演算法(比如光線投射演算法)進行點是否在多邊形內的比對。不過這個辦法速度太慢。那麼,如何有效地縮小搜尋範圍呢?

我們沒有使用r-tree或複雜的s2演算法,而是選擇了更簡單的辦法來找出地理圍欄:uber的商業模型是以城市為中心的,其商業規則還有定義商業規則的地理圍欄一般都與城市密切相關。這樣我們就可以將地理圍欄分為兩種層級,第一層是城市地理圍欄(定義城市邊界的地理圍欄),第二層是城市間的地理圍欄。

每次查詢,我們首先會通過線性掃瞄,查詢所有的城市地理圍欄,定位所在城市;然後再次通過線性掃瞄,找出其中包含的地理圍欄。根據該解決方案的複雜程度,執行時長為o(n),n被大幅縮減到100s到10000s的數量級。

我們希望這項服務是無狀態的,以便適用於所有請求;同時在所有的服務例項中,每個請求的結果相同。這意味著每個服務例項都必須有全世界的資訊,而不是某個分割槽的。我們使用確定性輪詢排程,確保來自不同服務例項的地理圍欄資料保持同步。這樣一來,該服務的架構就非常簡單了。後台任務定期對不同的資料庫的地理圍欄資料進行輪詢,並將這些資料儲存在主記憶體中,為查詢提供服務;同時序列化到本地檔案系統中,在服務重啟時快速引導載入:

上圖是我們的地理圍欄查詢服務架構。

我們的架構需要讀取/寫入併發訪問記憶體中的geo索引,特別是:在前台查詢引擎從索引讀取時,後台輪詢任務會對索引執行寫入。對於習慣node.js單執行緒的使用者來說,go的記憶體模型可能會構成挑戰。在go中,常用的方式是通過goroutines與channels同步併發讀取/寫入任務,出於對效能負面影響的擔心,我們嘗試使用sync/atomic資料報的storepointer/loadpointer基元自行管理記憶體屏障,卻導致**脆弱且難以維護。

最後我們進行了妥協,使用讀寫鎖來同步到geo索引的訪問。為了將鎖定等待的時間減到最短,在轉到主索引之前,我們另外構建了新的索引區段為查詢提供服務。使用鎖定導致查詢的延遲相對於storepointer/loadpointer的辦法來說有稍許增加,不過在我們看來利大於弊:**簡單化和可維護性的好處值得用稍許效能來換。

回顧之前的工作,我們非常慶幸選擇了go這種新語言來編寫服務。

優勢:

儘管之前uber的服務大多使用node.js和python,但go語言逐漸成為許多uber工程服務的新選擇。

Uber是如何基於Go語言構建高QPS服務的?

在2015年初,我們構建了乙個只做一件事 也的確做的非常好 的微服務 查詢地理圍欄 geofence lookup 一年後,這項服務已經成為uber數百個正在執行的服務中每秒查詢次數 qps 最高的服務。接下來,本文將談論我們構建這項服務的原因以及我們是如何使用go語言快速構建和擴充套件這項服務的。...

Uber是如何基於Go語言構建高QPS服務的?

在2015年初,我們構建了乙個只做一件事 也的確做的非常好 的微服務 查詢地理圍欄 geofence lookup 一年後,這項服務已經成為uber數百個正在執行的服務中每秒查詢次數 qps 最高的服務。接下來,本文將談論我們構建這項服務的原因以及我們是如何使用go語言快速構建和擴充套件這項服務的。...

Uber是如何基於Go語言構建高QPS服務的?

在2015年初,我們構建了乙個只做一件事 也的確做的非常好 的微服務 查詢地理圍欄 geofence lookup 一年後,這項服務已經成為uber數百個正在執行的服務中每秒查詢次數 qps 最高的服務。接下來,本文將談論我們構建這項服務的原因以及我們是如何使用go語言快速構建和擴充套件這項服務的。...