
在2021年底,Pinterest決心砍掉重練舊廣告推薦系統Mohawk,打造新廣告系統AdMixer。他們期待新系統上線後,能額外投入上百名工程師來參與廣告系統,開發和維運新產品和演算法,來支援飛速成長的廣告業務。不只如此,新系統設計下,不同工程團隊開發和維運時,不能互相干擾,且維持高服務穩定性及數據完整性。
新系統更要避免重蹈舊系統覆轍,否則隨著業務持續增長,系統肥大、難以改動、錯誤頻出等同樣問題,仍會再次浮現。
基於前述需求,他們奠定出4大設計原則,包括易於擴展、明確劃分系統邊界、安全設計(Safe by design),以及利於高效開發。
首先是易於擴展原則。系統框架和API需要具備足夠彈性,既能支持新功能擴展,也能方便地淘汰舊功能。尤其,記取上一代系統累積巨量技術債的教訓,Pinterest這次特別加入功能淘汰的設計,避免導致系統過於擁腫。
再來是明確劃分系統邊界,根據業務邏輯來定義高層次、抽象的系統模組。這樣一來,不同團隊負責的功能會各自形成一套模組,並相互隔離,以避免開發和維運過程中彼此干擾。
安全設計原則,強調從高層次的設計和邏輯來確保執行緒安全和數據完整性安全。他們希望用並行運算(Concurrent computing)來提升效率,但不互相競爭資源。同時,多執行緒並行時,希望能避免數據被任意改動,導致系統錯誤或Logging到錯誤數據。
利於提升開發效率原則,則是在設計系統時就將開發體驗納入考量,並提供開發者易於使用和重複利用的測試和分析工具。
為了實現這些原則,Pinterest工程團隊採用了DAG圖學架構,來組織程式碼模組之間的關係。同時制定了一套資料模型,作為DAG不同節點傳遞數據的規則,以保證程式執行時的執行緒和數據安全。
以Twitter為師,用DAG圖學架構來組織程式碼模組
Pinterest舊版廣告推薦系統不只有38萬行程式碼,更串聯超過70個外部資料來源,來執行請求處理器(Request handler)、特徵拓展器(Feature expander)、檢索(Retrieval)、排序(Ranking)、競價(Auction)等一系列功能,規模龐大、內外部系統關係複雜。
為了妥善劃分程式碼模組,以及管理模組間的耦合性,Pinterest採用Twitter開源的非同步性服務(Asynchronous service)運作框架Nodes,來設計新一代廣告推薦系統。
Pinterest還以Nodes為基礎,修改出自家程式碼組織框架Apex,並規定較Mohawk嚴謹許多的資料模型規則,來確保節點間,數據傳輸的規格與處理模式都如預期進行,以符合安全設計的設計原則。
Pinterest以Twitter開源的非同步性服務運作框架Nodes為基礎,發展出新一代廣告推薦系統的程式碼組織架構Apex,來管理負責模組間的耦合關係及資料傳輸流程。
此框架採用了一個特別的程式碼組織方式,將一個個程式碼模組視為節點,串聯成一個網狀DAG流程圖。Pinterest看好這套架構,可以組織和執行RPC(遠端程序呼叫)伺服器等非同步性服務,適合用來打造AdMixer模組間耦合性複雜的大系統。
工程師添加新模組時,就像是於這張DAG流程圖中新增一個節點,需要明確定義上下游依賴關係,也就是新節點與其他節點之間的箭頭。反之,淘汰功能時,能明確看出移除一個節點後,哪些上下游節點會受耦合性影響,避免意料之外的錯誤。
用更嚴謹的資料模型規則進一步強化系統安全性設計
Pinterest工程團隊為Apex框架定義出一套,適用於廣告推薦系統的資料模型,統一規範資料在節點間傳輸的結構和型別,以及資料存取或更動的限制。
他們維護資料模型規則的做法是,將一系列資料結構及資料處理規則檢查機制,寫在一個個Java物件中,用來連接節點,以確保數據傳輸時,節點處理數據的方式符合預期。
他們還將系統中的節點,組織為一個個節點子群組,每個子群組內的節點都有相同資料模型。這樣一來,當一個子群組要拓展新節點,能輕鬆套用同一套資料模型,符合AdMixer易於擴展的系統設計原則。
Apex框架需要搭配更加嚴謹的資料模型規則,是記取了前一代廣告系統Mohawk的教訓。舊系統允許程式碼在任意地方修改全域的資料,且沒有檢查機制來維護數據處理流程的最佳實踐方法,衝擊系統數據完整性。甚至,當不同並行執行的功能模組需要同時存取同一份資料,還有可能造成執行緒安全問題,導致其他節點存取到錯誤數據,彼此干擾,並影響到整體廣告投放流程。
Apex資料模型規則中,尤其重要的兩項是,嚴格數據輸入和輸出節點的數據型別,以及資料單次寫入原則。
資料模型關鍵規則一:強型別數據傳輸模式
Apex資料模型的一項重要規則是,節點間資料傳輸時,必須明確定義輸入與輸出的型別。
Twitter Nodes框架下的節點是弱型別(Weakly-typed),只會定義數據輸出型別。輸入時,則採取通用的「物件(Object)」類型處理,需要進一步由編譯器來自動轉換型別,或用程式碼來手動轉換型別。這種設定,容易出現人為錯誤,且不容易快速追蹤到錯誤根因。
為了避免此問題,Pinterest設計Apex框架時,將節點調整為強類型(Strongly-typed),統一於資料模型中定義數據輸入與輸出的型別。
開發者無需再手動撰寫類型轉換的程式碼,且早在執行階段之前的編譯階段(Compile time),編譯器即能自動檢查數據類型,準確抓出型別錯誤發生的程式碼,既節省除錯時間,還能避免對廣告業務的影響,是為了滿足安全設計和利於高效開發兩項原則的做法。
資料模型關鍵規則二:單次寫入原則
另一條重要的資料模型規則是,節點間傳輸的數據,必須符合單次寫入(Write-once)原則。
AdMixer預設的Thrift數據框架下,數據是可變(Mutable)的。不過,Pinterest為了在編譯階段就能維護單次寫入原則,特別寫出一套腳本,來產生只有取值器(Getter)而沒有設值器(Setter)的不可變(Immutable)Java資料類別。
接著,盡可能將資料、資料欄位、資料類別,都設定為不可變屬性。他們花了大把時間研究,哪些資料適合設為不可變屬性,如廣告請求相關的資料。當節點有需要動用這些不可變資料時,需要產生新版本資料,而不能更動原始版本資料。
這些做法,是為了確保廣告投放流程中,不同執行順序的節點都能存取正確版本的數據。同時,兩個節點同時存取相同的數據欄位時,不會意外修改該數據並影響其他節點。
常見避免這些問題的做法是資料鎖定(Data Lock),在資料庫端或其他資料儲存環境設置,每次只有單一執行緒能存取和修改資料。Pinterest則從資料模型規則下手,來統一規定整個系統的資料處理模式,進一步確保資料處理模式符合預期。
AdMixer的四大系統設計原則,以及因應而生的Apex程式碼組織框架和一系列嚴謹的資料模型規定,都是Pinterest為了避免重蹈舊系統Mohawk覆轍,而採取的做法。
他們表示,這些做法都是為了Pinterest廣告業務規模提升時,較不易累積技術債,同時讓系統錯誤能在更早的階段顯露,以提升開發和維運效率,進而降低對廣告業務的衝擊。
系統設計時期所採用的高度模組化做法及一系列資料模型規定,更有助於他們在測試和轉移階段,能降低成本、增加效率。
熱門新聞
2025-02-17
2025-02-17
2025-02-17
2025-02-18
2025-02-18
2025-02-14