經驗教訓帖:探尋Reddit廣告服務系統的構建!
Reddit如何使用Go構建其廣告服務系統,以及從流程中汲取經驗教訓。
概要
Reddit工程團隊近段時間將Go引入其堆疊,以編寫新的廣告服務系統來取代第三方系統。 Deval Shah向我們介紹了這個新服務的架構、Reddit團隊第一次使用Go的經驗,以及他們使用Go構建這個廣告伺服器所學到的所有課程。
Reddit簡介
Reddit是網際網路的首頁,它是一個擁有數萬個興趣社群的社交網路,人們可以在那裡討論對他們來說重要的事情。
Reddit的數字排名:
-
第5/18(美國/世界)Alexa排名
-
330M + MAU
-
138K活躍社群
-
每月1200w篇文章
-
每月2B投票
而Reddit構建的任何系統都必須能夠處理此級別的流量。
廣告架構概述
廣告伺服器需要處理整個廣告流程。廣告伺服器處理從廣告顯示到廣告之後的任何後處理的所有內容。
廣告服務 @ Reddit
Reddit廣告伺服器有幾個要求:
-
擴充套件 :Reddit上的每個請求都會進入廣告系統,所以它必須應對大規模的需求。
-
速度 :廣告伺服器必須快速。他們不希望廣告成為降低使用者體驗的效能瓶頸。他們要求在30毫秒內回覆廣告。
-
拍賣 :確保伺服器能夠根據出價選擇最佳廣告。
-
步調 :伺服器必須能夠以最佳方式分配廣告。
之前的廣告服務@ Reddit:
之前,每當使用者訪問reddit.com時,reddit 的monolith後端都會向第三方廣告伺服器傳送請求。第三方伺服器將使用其選擇的一個或多個廣告進行響應,並將其返回給使用者。
過了一會兒,他們意識到繼續使用第三方廣告伺服器對他們來說不會有用,因為它:
-
慢
-
可定製性較低:第三方不支援他們想要進行的許多更改。
-
操作上不透明:他們無法知道某些事情是如何實施的,無法控制廣告的質量等。
我們決定建立一個廣告伺服器,建立一個由3人組成的團隊。從infra開始,編寫服務,然後將其推廣到正在生產的系統。
廣告服務基礎設施:
廣告伺服器基礎架構中使用的一些值得注意的工具:
-
適用於所有RPC的Apache Thrift。 Thrift自2007年以來一直存在,Reddit從一開始就一直在使用它。
-
RocksDB用於資料儲存。它是由Facebook構建的OSS鍵值儲存。它是一個可嵌入的資料儲存,它避免了網路跳變,並針對高讀取和寫入進行了優化。
-
他們還決定使用Go作為主要的後端語言。這是Reddit第一次將Go用於生產。在此之前,Reddit主要使用Python和Java。該團隊希望確保Go成為Reddit使用的語言集中的一等公民,並支援Reddit所需的一切。
廣告伺服器架構:
這是新廣告伺服器的架構:
簡要概述它的工作原理:
-
Reddit.com呼叫了一個名為廣告選擇器的服務。這是廣告投放基礎架構中的第一項服務。這是一種Thrift服務,並接收來自reddit.com的請求。然後,它呼叫一個名為getAds的函式,該函式處理獲取和返回要向使用者顯示的廣告。然後廣告選擇器呼叫充實服務。
-
充實服務負責獲取有關查詢和選擇最相關廣告所需的請求、使用者和其他資訊的更多資料和資訊。它會收集所有這些資訊並將其返回給廣告選擇器。
-
收到充實服務的響應後,廣告選擇器會選擇新增,然後將廣告返回給reddit.com以顯示給使用者。它還將回復傳送給Kafka。
-
向使用者展示廣告後,需要進行一些後期處理。客戶端向事件跟蹤器服務傳送事件HTTP請求。此活動可確認廣告已投放。此事件通知也被帶到Kafka。
-
Kafka為兩個Apache Spark作業提供資料:
-
事件統計流作業始終在執行,它會寫入增強服務以提供用於學習選擇更好的廣告資訊。
-
還有Pacing迴圈,它涉及Pacing Spark工作。這涉及一個流媒體工作,計算每個廣告客戶展示的廣告數量,以及另一個確保廣告最佳展示的工作。
在這種架構中,Go服務是:
-
廣告選擇器:
-
有30ms的P99要求
-
涉及用於定位和選擇的複雜業務規則
-
進行競價:所有的廣告,業務邏輯規則,是在競爭得到廣告顯示,而廣告選擇器會處理此問題。
-
事件追蹤:
-
1ms P99要求
-
確認日誌和事件
-
需要高度可靠
-
充實服務:
-
節儉服務
-
將資料返回到廣告選擇器
-
有一個嵌入的RocksDB資料庫
-
4毫米P99
-
對於每個請求,它在Go中進行字首掃描,並獲取一堆資料並進行計算和聚合。我們的想法是避免網路跳變獲取資訊,以確保我們快速提供響應。
Reddit的其他一些Go工具和服務則不會深入探討:
-
報告服務
-
Vault管理工具
-
廣告事件生成服務
我們的Go經驗
這是Reddit與Go的第一次體驗。德瓦爾表示,到目前為止,這段經驗很棒。這項工作始於使用Go的兩到三名工程師,現在已經發展到大約十幾名在Go方面工作的工程師。
他們在Go看到的主要優勢是:
-
提高開發人員的速度:新工程師可以加入並快速熟悉程式碼。 Go強調簡單性、快速部署和編譯時間意味著緊密的反饋迴圈,這有很大幫助。
-
開箱即用的出色表現:除了遵循最佳實踐外,沒有太多的工具或優化來快速執行。與他過去調整JVM和處理垃圾收集的經驗相比,這對Deval來說是一次不錯的體驗。
-
易於專注於業務邏輯:業務邏輯是困難的部分,Go的簡單性和開箱即用的效能有助於團隊專注於它。
-
最後,廣告服務會延遲大幅下降:響應時間從90毫秒降至10毫秒以下。
得到教訓
這是一系列面臨的問題,Reddit如何處理這些問題,以及從這些挑戰中學到的知識。
問題1:如何構建生產就緒的微服務?
Reddit以前有過為Python做過的經驗,但不是Go。
最初的原型通過大量的StackOverflow讀取和谷歌搜尋工作,但顯然不會與開發人員一起擴充套件。
他們看到的一些問題是:
-
記錄、指標等都到處都是
-
改變傳輸層很難
-
我們需要可重複的模式
他們意識到Go社群已經解決了這些問題,因此他們研究瞭解決這些問題的現有框架。他們遇到的一些選擇:
他們認為Go-Kit最有意義。 Reddit選擇Go-Kit的主要原因是:
-
支援Thrift
-
是靈活的,不是非常有描述性。如果Reddit想要轉移到gRPC,他們希望能夠輕鬆遷移。
-
具有用於記錄、度量、速率限制、跟蹤、斷路等工具,這些是在生產中執行微服務時的標準要求。
-
Go-Kit @ Reddit。這是使用Go-Kit的圖:
這個架構有一些值得注意的事情。中心服務有2個實現:記憶體實現(這很好並可以用於原型),以及用於生產實現的RocksDB實現。本地開發仍然存在記憶體中實現。
有幾個中介軟體層:跟蹤、日誌記錄和度量。最後,Thrift運輸處於頂層。這種結構使得更改變得容易。例如,如果他們想要將傳輸層從Thrift更改為gRPC,他們只需要更改頂層。
使用Go-Kit是有益的,因為它為團隊提供瞭如何構建Go程式碼的良好例證。他們以前沒有這方面的經驗,因此使用Go-Kit有助於理解Go服務的典型結構。
教訓1:使用框架/工具包。 對於您使用Go的所有內容而言,並不是必需的,但對於需要度量、日誌記錄等的生產服務,請使用已解決問題的庫而不是嘗試自己完成。
問題2:如何安全快速地推出新系統?
最終目標是推出新的廣告伺服器,對Reddit使用者、支付廣告客戶、依賴廣告團隊的其他內部團隊影響最小。第三方廣告伺服器是一個黑盒子,Reddit需要一種快速迭代、學習和改進的方法。
這就像在飛行途中改變飛機。他們慢慢地在他們的第三方服務周圍新增了新的基礎設施,當它準備就緒時,他們會把它撕掉:
-
他們首先將廣告選擇器注入請求路徑,將其純粹作為代理。系統執行的操作與以前相同,但廣告選擇器就位。這使他們可以通過廣告選擇器擴充套件請求,而無需實際執行任何操作。
-
然後,他們不僅僅是代理,而是在廣告選擇器服務中實施並推出了原生廣告選擇。現在,廣告選擇器將在內部處理請求,但仍充當代理並將請求傳遞給第三方,系統仍將使用第三方響應。
-
然後,他們新增了Event Logger來實現本機響應的日誌記錄,並設定Kafka。
-
他們繼續構建其餘的服務,從存根服務開始,並在此過程中新增邏輯。
-
最終,一旦一切就緒,他們就會切斷第三方廣告伺服器。
在這些Go特性的幫助下,Go允許他們安全輕鬆地遷移到新的廣告伺服器:
-
Go編譯器很快
-
支援跨平臺編譯
-
自包含二進位制檔案
-
強併發原語
教訓2:Go使快速迭代變得簡單而安全。
問題3:如何除錯延遲問題?
部署新廣告伺服器後,他們確實看到了一些緩慢,網路故障,部署不良等問題。
如果你確切知道哪個服務有問題,pprof就很棒。另一方面,分散式跟蹤使您可以檢視服務。他們沒有支援廣告方面的分散式跟蹤,但他們確實在Reddit的堆疊上的其他地方支援它。
為什麼跟蹤有用?
-
識別導致高總體延遲的熱點
-
幫助發現其他錯誤/意外行為
跟蹤通常很容易,你有一個客戶端和伺服器。在客戶端,您提取跟蹤識別符號,並將它們注入您傳送的伺服器的請求中。在伺服器端,當您獲得請求和識別符號時,將它們放入上下文物件並傳遞它們。使用HTTP和gRPC非常簡單,沒有理由不這樣做。
但是,reddit正在處理Thrift,所以他們遇到了一些問題。
他們看了一下Thrift替代品,Facebook Thrift和Apache Thrift。他們正在尋找的兩個關鍵功能是對標題和上下文物件的支援:
他們嘗試使用FB thrift,但是存在一些問題,主要是缺少上下文物件,導致程式碼混亂和複雜化。在Apache thrift中,支援上下文物件,但它不支援標頭檔案。因此,解決方案是:向Apache Thrift新增標頭。這已經針對其他語言完成,但不適用於Go。因此,他們將THeader新增到Apache Thrift。這意味著現在支援上下文物件,並且標頭檔案可以儲存跟蹤識別符號。
如果您想檢視這些更改,可以檢視https://github.com/devalshah88/thrift。 Deval希望通過貢獻流程獲得更改並將其合併到上游。
這是一個跟蹤程式碼。客戶端包裝器只從上下文物件中提取跟蹤資訊,並將其新增到headers:
伺服器包裝器從頭部獲取資訊並將其注入上下文物件,以便它可以傳遞:
此程式碼來自https://github.com/devalshah88/thrift-tracing。
完成所有這些工作後,分散式跟蹤被證明在除錯延遲問題方面非常有用。然而,我們得到的結論是第三課:使用節儉和Go進行分散式跟蹤是很困難的。
問題4:如何處理緩慢/超時?
在Reddit,他們希望系統能夠優雅地處理緩慢。他們從不希望使用者受到影響,因此如果速度緩慢,Reddit寧願不展示廣告也不願降低使用者體驗。
他們的兩個目標是:
-
不要讓使用者等待太久
-
不要浪費資源做不必要的工作
使用上下文物件來強制服務中的超時:這是來自充實服務的程式碼,用於向上下文物件新增截止日期,傳遞它,並在截止日期到期時提前退出。
這樣的結果是好的,但還不夠:
第一張圖顯示了從充實服務獲得響應所需的時間。這個特定的時間框架有一些緩慢,但它沒有讓使用者等待超過25毫秒。
第二個圖表顯示,在伺服器端,增強服務正在處理最長70毫秒的請求,因此伺服器在客戶端已經超時並且在不再需要響應之後,有些浪費資源。
通常要做的是使用HTTP傳播截止日期。此程式碼新增了一個超時,它通過上下文物件傳遞給伺服器:
Thrift使這很難。這裡沒有使用上下文物件。如果客戶端超時,goroutine不知道並且不退出:
這個方法不是特別好,但有辦法來解決這個問題:
一種選擇是為請求有效負載新增截止時間。客戶需要在請求中包含截止日期。伺服器會將截止日期注入上下文物件,並使用它。這並不是很好,因為必須在所有端點進行此更改。
相反,他們通過截止日期作為節儉標題。這與它們傳遞跟蹤識別符號的方式類似。在此更改之後,在伺服器端,他們看到類似於客戶端的延遲:
教訓4:在服務內部和服務之間使用截止日期。
問題5:如何確保新功能不會降低效能?
快速迭代和複雜的業務邏輯可能導致效能問題。廣告服務團隊需要流程和工具,以確保他們能夠快速移動而不會違反延遲SLA。為此,他們使用了負載測試和基準測試。
使用彎曲器進行負載測試:
這就是你從Bender那裡得到的回應:
負載測試對於在重負載下測試更改非常有用,並且允許開發人員再推送到生產之前優化新功能以實現高負載。
他們還利用所有關鍵系統的基準測試。此基準測試程式碼:
獲取此輸出:
基準測試有助於:
-
通過改變減慢速度來防止降級
-
讓您瞭解事情隨時間的變化情況
-
告知開發人員有關不同實現存在的權衡
教訓5:基準測試和負載測試很容易。做吧!
回顧:
-
使用框架/工具包
-
Go使快速迭代變得簡單而安全
-
使用Thrift和Go進行分散式跟蹤很難
-
在服務內部和跨服務使用截止日期
-
使用負載測試和基準測試
結論:
-
Go幫助reddit構建和擴充套件新的廣告服務平臺 - 易於構建和快速
-
我們分享了我們在此過程中學到的5個重要經驗教訓
-
嘗試在下一個Go專案中至少使用其中一個
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31509949/viewspace-2214207/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 從Debezium到Snowflake在生產中構建資料複製的經驗教訓 - Shippeo
- 使用Kafka Streams構建事件源系統的經驗Kafka事件
- 使用 go micro 搭建微服務介面的經驗教訓Go微服務
- 摩杜雲:探尋雲原生遊戲通路,構建更好的邊緣雲服務遊戲
- [譯] Data Binding 庫使用的經驗教訓
- Heap使用Postgres SQL後的經驗教訓SQL
- 廣告系統架構架構
- Avionos報告:網路品牌和傳統零售商的經驗和教訓
- 安裝pytorch-gpu的經驗與教訓PyTorchGPU
- 我的軟體開發中經驗教訓
- 經驗分享:HelloFresh在生產中執行Istio的經驗教訓 - Craig HuberAI
- 來自10位 IT 大牛的23條經驗教訓
- 「譯文」Google SRE 二十年的經驗教訓Go
- Go 併發程式設計中的經驗教訓Go程式設計
- 20+條軟體開發的經驗教訓
- 北斗系統:構建天地一體化的高精度定位服務
- 關於/福建應該如何開廣告發稅票-開票服務大廳-百度經驗
- Supercell成立10週年的10條經驗和教訓
- 《神鬼寓言》的開發中有些什麼經驗教訓?
- 大規模執行 Apache Airflow 的經驗教訓 - shopifyApacheAI
- 教務管理系統
- Northridge:2020年客戶服務經驗報告
- 致廣大、盡精微,曙光問道算力服務“神經系統”
- Wayfair的廣告競價系統架構 - quastorAI架構AST
- 乾貨 | 廣告系統架構解密架構解密
- 經驗教訓:Instacart 的實時機器學習之旅 - shu機器學習
- 來說說成功的雲遷移的10個經驗教訓
- 每天會生成巨大的資料庫,請教系統設計方法?- Reddit資料庫
- 網易基於 Iceberg 的實時湖倉一體系統構建經驗
- 基於雲服務MRS構建DolphinScheduler2排程系統
- 「服務端」node服務的監控預警系統架構服務端架構
- 避免使用服務網格的原因? - Reddit
- 阿里巴巴的 Kubernetes 應用管理實踐經驗與教訓阿里
- 使用Go兩年學到的五大經驗教訓 - hashnodeGo
- Salesforce使用Spring Data Redis記憶體洩漏的經驗教訓SalesforceSpringRedis記憶體
- swoole 服務的建構函式函式
- [轉貼]:軟體過程改進:經驗和教訓
- 如何構建多快好省的“知識圖譜即服務”?這裡有獨家的經驗分享