Twitter 如何保證廣告平臺的可靠性?

作者 | Twitter 博客

譯者 | 王者

1 背景簡述

在深入研究超支是如何發生前,先來了解一下我們的廣告系統是如何提供廣告服務的。下面是我們廣告服務管道的高級架構圖:

當用戶在 Twitter 上瀏覽廣告時,我們會向廣告回調管道發送一個事件。一旦活動支出計數器收到這個事件,它將計算活動的總支出,並在支出緩存中更新活動的支出。對於每個傳入的請求,廣告服務器管道都會查詢支出緩存,以便獲得活動的當前支出,並根據剩餘的預算確定是否繼續提供服務。

2 超支

因爲我們處理的廣告活動的規模比較大 (數據中心每秒有數以百萬計的廣告瀏覽事件),所以延遲或硬件故障隨時都可能在我們的系統中發生。如果支出緩存沒有更新最新的活動支出,廣告服務器就會獲取到陳舊的信息,並繼續爲已經達到預算上限的活動提供廣告服務。我們將永遠無法收取超出廣告預算的那部分費用,導致 Twitter 的收入損失。

例如,假設有一個每天預算爲 100 美元的廣告活動,每一次點擊的價格爲 0.01 美元。在沒有超支的情況下,這將爲活動創造每天 10000 次點擊的機會。

假設廣告回調管道或 LSC 出現故障,導致支出緩存沒有更新,丟失了價值 10 美元的事件,支出緩存只會報告支出爲 90 美元,而實際上活動已經支出了 100 美元,那麼該活動將獲得額外的 1000 次免費點擊機會。

3 跨數據中心一致性

Twitter 有多個數據中心,每個數據中心都部署了整個廣告服務管道的副本,包括廣告回調管道、實時支出計數器和支出緩存。當用戶點擊廣告時,回調事件被路由到其中的一個數據中心,這個數據中心裏的回調管道將負責處理這個事件。

那麼,問題就來了:每個數據中心計算的總支出只計算該數據中心接收到的事件,不包括其他數據中心的數據。由於廣告客戶的預算是跨數據中心的,這意味着每個數據中心的支出信息是不完整的,可能會少算了廣告客戶的實際支出。

儘管複製事件爲我們帶來了更好的一致性和更準確的支出信息,但系統的容錯能力仍然不是很強。例如,每隔幾周,跨數據中心複製失敗就會導致支出緩存由於事件丟失或滯後而失效。通常,廣告回調管道會出現系統問題,例如垃圾收集停頓或數據中心的不可靠網絡連接導致的事件處理延遲。由於這些問題發生在數據中心本地,該數據中心中的 LSC 接收到的事件與延遲成正比,因此支出緩存的更新也將延遲,從而導致超支。

在過去,如果一個數據中心發生這些故障,我們會禁用這個數據中心的 LSC,並讓其他數據中心的 LSC 同時更新本地緩存和發生故障的數據中心的 LSC,直到出現滯後的廣告調管道和 LSC 重新追上來。

5 跨數據中心寫入方案

由於這種架構存在很多問題,我們重新設計了管道,讓它能更有彈性地應對故障,並減少運維人員的干預。這個解決方案有兩個主要組成部分:

在正常情況下,新解決方案的工作原理與之前的設計完全一致。但是,如果本地支出緩存落後了,廣告服務器能夠檢測到,並自動切換到包含來自遠程寫入數據的數據集。當本地的問題解決之後,廣告服務器將自動切換回本地數據集。

我們怎麼知道哪個數據集更健康?

我們通過常見的故障場景來決定數據集的健康情況:

爲了構建一個包含這兩個因素的健康檢查機制,我們引入了支出直方圖的概念。

支出直方圖

假設我們有一個滾動窗口,顯示每個數據中心的 LSC 在任意給定時刻正在處理的事件計數。滾動窗口包含最近 60 秒內每毫秒處理了多少事件的計數。當到達窗口的末尾,我們刪除頭部的計數,並計算後面 1 毫秒的計數。我們可以看到 LSC 在 60 秒內處理的 “事件計數” 的直方圖。直方圖如下圖所示:

爲了能夠選擇最佳的數據集 (在廣告服務端),我們利用了這個直方圖和最近的事件時間戳。LSC 將這些元數據與支出數據一起寫入支出緩存。

LSC 在寫入時不會序列化 / 反序列化整個直方圖。在寫入之前,它會彙總窗口中所有計數器的計數,並寫入一個聚合值。這裏使用事件的近似值就足夠了,近似值可以作爲這個數據中心的 LSC 總體健康狀況的信號。這是由故障的本質決定的——如果故障足夠嚴重,我們將立即看到故障的影響,計數會顯著下降。如果不是很嚴重的話,數量幾乎是一樣的。

包含元數據的結構體是這樣的:

1struct SpendHistogram {
2    i64 approximateCount;
3    i64 timestampMilliSecs;
4}
5

在處理請求時,廣告服務器同時讀取本地和遠程的數據集。它使用 SpendHistogram 根據下面描述的數據中心選擇邏輯來決定使用哪個數據集作爲事實數據來源。

數據中心的選擇

選擇數據集的邏輯如下:

這可以總結成以下的真值表:

x = LocalTimeStamp - RemoteTimeStamp

y = LocalApproxCount - RemoteApproxCount

ts = ThresholdTimeStamp

tc = ThresholdApproxCountPercent

在切換到使用來自遠程數據中心的數據集之前,我們使用 ts 和 tc 來確定容忍度閾值。如果差值在閾值內,我們會更傾向於使用本地數據集。我們嘗試找到閾值,以便在不需要進行數據中心切換的情況下儘早檢測故障。廣告服務器在處理每個請求時都會發生這個選擇過程,因此我們會在本地進行緩存,每隔幾秒刷新一次,以防止頻繁的網絡訪問影響整體性能。

下面是切換使用數據中心數據的可視化表示。當 DC1 的 LSC 發生故障時,會導致 DC1 的廣告服務器自動選擇使用 DC2 的數據。

6 擴展到多個數據中心

到目前爲止,我們討論的方法只涉及到兩個數據中心。通過引入跨數據中心複製因子的概念,我們可以將設計擴展到 “N” 個數據中心。複製因子控制每個 LSC 服務寫入的遠程數據中心的數量。在讀取數據時,我們使用了相同的邏輯,並做了一些優化,比如一次讀取 (批讀取) 所有必要數據,而不是分多次讀取。

例如,假設 ReplicationFactor 設置爲 2,DC1 中的 LSC 將寫入到 DC1、DC2 和 DC3 的支出緩存,DC2 中的 LSC 將寫入到 DC2、DC3 和 DC4 的支出緩存,DC3 中的 LSC 將寫入到 DC3、DC4 和 DC1 的支出緩存。下圖顯示了三個數據中心的複製原理圖。在每個數據中心中,廣告服務器將讀取三個支出直方圖,並從所有這些數據中心選擇首選的數據集。根據我們的網絡和存儲約束,我們選擇 2 作爲複製因子。

7 結論

在推出這些變更之後,我們注意到團隊的運維成本發生了重大變化。之前每個季度由於系統問題會導致多次超支事件,而在過去的一年,都沒有發生此類事件。這節省了大量的工程時間,並避免了由於基礎設施問題而向廣告商發放補償。

通過識別系統健康關鍵指標,設計出最簡單的工程解決方案,並根據這些指標自動採取行動,我們解決了一個影響服務管道正確性的關鍵性問題。我們不僅構建了一個具有容錯能力和彈性的系統,而且釋放了工程資源,把它們用在更有價值的地方。

原文鏈接:

https://blog.twitter.com/engineering/en_us/topics/infrastructure/2020/how_we_fortified_twitters_real_time_ad_spend_architecture.html

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/RJnCV2-1JEqj8S6eYh2IWA