得物增長兌換商城的架構演進
一、簡介
二、誕生背景
三、業務架構
1. 業務架構圖
2. 業務架構概述
四、玩法迭代
1. 整體玩法總覽
2. RPC調用迭代
3. 庫存設計迭代
4. 獎品橫向迭代
5. 兌換防刷迭代
6. 項目穩定性建設
五、更多拓展
1. 抽獎組件搭建器
2. 引入增長的任務中臺
3. 打造簡易的積分系統
六、總結
一、簡介
在移動互聯網蓬勃發展的今天,用戶的選擇日益多元化,App市場的競爭也愈發白熱化。為了在激烈的市場競爭中脫穎而出,提升用戶獲取效率并增強用戶粘性,越來越多的應用開始采用積分兌換、抽獎等互動玩法。這些精心設計的運營策略不僅能夠滿足用戶的參與感和成就感需求,更能有效促進社交傳播,提升品牌忠誠度。通過持續優化和迭代,開發者能夠在競爭激烈的市場中占據優勢地位,實現用戶的可持續增長和長期留存。
基于提升系統復用性和穩定性的考量,我們構建了一個統一的兌換商城中臺系統。該中臺旨在為各上游業務線提供標準化的積分兌換和獎勵機制,使各類應用和服務能夠快速接入并享受便捷的服務支持。通過這一中臺架構,我們不僅實現了核心業務邏輯的集中化管理與維護,更為后續的功能擴展(如抽獎、彩票等多樣化玩法)奠定了堅實基礎,從而顯著提升了開發效率和用戶體驗。
二、誕生背景
在用戶增長和留存策略的探索中,增長團隊先后推出了多種互動類玩法,如每日簽到、星愿森林、心愿海洋以及早期的點鞋成金等。這些玩法通過積分或虛擬貨幣的積累,不僅增強了用戶的參與感,也為用戶提供了豐富的獎勵機制。然而,隨著玩法的多樣化,如何高效管理這些積分或貨幣的消耗途徑,成為提升用戶體驗和趣味性的關鍵。
每日簽到
星愿森林
心愿海洋
點鞋成金
兌換專區
兌換專區
兌換專區
兌換專區
面對這一挑戰,團隊意識到為每個互動玩法單獨開發兌換功能不僅需要前后端的全面支持,還需重復搭建復雜的監控和預警系統以防范潛在的資損風險。這種重復開發的模式不僅成本高昂,而且難以保證系統的一致性和穩定性。因此,構建一個統一的積分商城,為各互動玩法提供通用化的兌換系統,成為了提升開發效率和確保系統安全性的必然選擇。這樣不僅優化了資源配置,也為后續的玩法擴展和用戶體驗升級奠定了堅實的基礎。
在既有兌換業務的基礎上,我們也成功沉淀并提煉了多個核心領域模型。后續基于這些通用化的領域模型,我們逐步探索并實現了多種新型玩法的快速落地,包括老虎機、盲盒以及抽獎券等多樣化互動形式。
三、業務架構業務
架構圖
圖片
業務架構概述
業務架構主要分4級:
- 上層業務? 主要是有使用兌換商城功能的上游應用,包括星愿森林、每日簽到、心愿海洋、增長新客、社區抽獎、學生答題等應用。
- 玩法沉淀? 主要包括三類玩法,包括積分兌換、老虎機/盲盒,以及抽獎券等。玩法核心所依賴的領域模型基本相似,各個系統都能得到了很好的復用。而在玩法層面,我們主要使用了設計模式中的模板模式,優先搭建好每個玩法的骨架,然后將一些關鍵步驟交由上游應用自定義實現。
- 領域模型? 活動:對應每個新接入的應用都需要新建一個活動模型,而各類自定義的業務層面的邏輯都是在活動維度上實現的。? 模板:模板作為上下關系的重要承接,一個活動能夠創建多個模板,而每個模板下又可以關聯多個獎品。之所以存在模板這個中間層,是有多個原因的。 ? 原因1:模板和活動之間的關系,我們可以以星愿森林為例,一個活動他是存在多個兌換專區的,包含了合種專區、超值專區等,這些是需要通過模板維度來劃分的。 ? 原因2:模板和獎品之間的關系,獎品需要關聯到不同的資產,創建一個獎品的成本較高。而將獎品設計成可以被綁定到多個模板上,獎品這個資產就可以得到一個很好的復用。同時每個模板中該獎品有其獨立的庫存模塊,那么模板之間就不會互相影響了。 ? ...? 獎品:后臺不同類型的獎品統一都需要在獎品庫內創建。這樣我們就能在不同的活動和模板中在獎品庫內選出該獎品。每次新增新的類型獎品,需要實現該新類型獎品的發獎能力,如優惠券、津貼、現金等都依賴不同的下游應用進行發放。? 庫存:目前支持分時間段庫存、每日庫存以及總庫存邏輯頻控,包含用戶及獎品的發放頻控、包含每日頻次、周期循環頻次以及總頻次。
- 下游依賴? 主要是兌換商城所依賴的下游應用,包括營銷中臺、商品聚合中心、提現中臺、口令服務、增長配置中心等。
四、玩法迭代
整體玩法總覽
本次玩法迭代的介紹主要以積分兌換玩法為主,參考樣式如下:
圖片
要想兌換具體的商品需要經過一系列流程,下圖為兌換的主流程介紹,包含了用戶從發起兌換到最終兌換成功或失敗的流程。主要為用戶選擇某個獎品兌換需要處理的一系列業務邏輯。
如上文所說,在玩法層面,主要使用了設計模式中的模板模式,利用自己領域模型的能力,優先搭建好兌換玩法的骨架,比如有效性校驗、庫存扣減、獎品發放等邏輯,然后將一些關鍵步驟如前置自定義檢查、扣減貨幣、回滾操作交由上游應用自定義實現。
玩法內置邏輯是由商城內部的領域模型實現,主要包含通用邏輯如下:
- 各個模型的有效性校驗:確保兌換請求的合法性。
- 庫存及頻控模型的前置校驗:檢查庫存和頻控模型,確保兌換請求的可行性。
- 庫存扣減邏輯:扣減獎品庫存,確保庫存數據的實時性和準確性。
- 獎勵模型的發放邏輯:發放用戶所兌換的獎品。
- 兌換成功后置處理:進行兌換成功的后置處理,如記錄日志、更新用戶狀態等。
即下圖灰色模塊是由商城自己的玩法模板實現。
需業務方對接邏輯主要包含:
- 前置自定義校驗:實現特定業務規則的前置校驗。
- 貨幣扣減:扣除用戶相應的積分或虛擬貨幣。
- 兌換失敗后的貨幣回滾:在兌換失敗時,將扣除的積分或虛擬貨幣返還給用戶。
即黃色箭頭及模塊是由商城側發起,調用上游接入應用,由上游應用處理相關邏輯,然后通知商城成功與否。
圖片
RPC調用迭代
1.0版本
在最初的版本中,黃色模塊(如前置自定義檢查、扣減貨幣、回滾操作)的實現方式較為繁瑣。具體流程如下:
- 上游業務方提供protobuf文件:每個新接入的業務方需要提供自己的protobuf文件。
- 生成go-grpc的server及client端樁代碼:根據protobuf文件生成go-grpc的server和client端樁代碼。
- 商城服務編寫代碼調用client端樁代碼:在商城服務中編寫代碼,調用client端樁代碼發起RPC調用。
這種實現方式存在以下問題:
- 開發成本高:每次新接入一個業務,都需要在商城服務和接入方同時編寫代碼。
- 維護復雜:隨著業務接入數量的增加,代碼維護和管理的復雜度顯著提升。
圖片
2.0版本
為了減少商城側的開發成本,我們借鑒了Java的SPI(Service Provider Interface)概念,對RPC調用進行了優化。具體實現如下:
- 商城統一生成protobuf的server端代碼:商城統一生成protobuf的server端代碼,作為服務提供者接口。
- 活動模塊統一配置調用路由:在商城服務的活動模塊中,統一配置調用路由,實現服務的動態路由和調用。
- 接入方引用protobuf生成的server端樁代碼:每次有新業務接入,接入方只需引用對應protobuf生成的server端樁代碼,并通過反向注冊機制實現服務的注冊和調用。
這種實現方式帶來了以下優勢:
- 開發成本降低 :商城層無需再為每個新接入的業務編寫代碼,只需進行應用配置。
- 維護簡化 :通過統一的服務提供者接口和調用路由,代碼維護和管理的復雜度顯著降低。
- 擴展性強 :新業務的接入更加便捷,系統的擴展性和靈活性得到提升。
通過這種優化,我們不僅實現了RPC調用的高效復用,還為未來的業務擴展和系統優化奠定了堅實的基礎。這種模塊化、可配置的設計理念,不僅提升了開發效率,還增強了系統的靈活性和可擴展性。
圖片
庫存設計迭代
一開始庫存只有每日及總庫存的概念,實現也比較簡單。為了保障庫存扣減的并發安全,使用Redis作為庫存的存儲。具體的庫存扣減則通過提前設置好Redis值并不斷扣減即可。由于Redis的單線程特性,也不用擔心并發重復扣減的風險。Redis一直扣減直至數值小于0就表示庫存已被耗盡。
rest, err := redis.decr(key)
if err {
...
return err
}
if rest < 0 {
return limitErr
}
而后需求迭代中出現了分時間段庫存,每天需要分成多個時段來分配庫存,每個時間段的庫存如果未被耗盡會累積到下一個時段。這個時候通過簡單的Decr命令就不能滿足需求了。
- 方案一 :為每個時段單獨設置一個Redis的key,但這樣會導致某個時段的庫存被浪費,不符合功能需求。要實現庫存累積效果,需要在進入下一個時段時將上一個時段的庫存加到下一個時段,增加了實現復雜度。
- 方案二 :繼續使用同一個庫存key,但需要拆分成兩次Redis命令調用,首先判斷當前時段的庫存是否足夠,然后進行扣減庫存。這種非事務性的執行方式可能導致并發問題,難以確保庫存被準確扣減。
考慮到實現成本,我們選擇了方案二。具體邏輯如下:
圖片
- 當前剩余未釋放庫存 = 總庫存 - 當前已釋放庫存(即timeStock)? 當前已釋放庫存是當前已過時段各個庫存的累加。
- 如果當前庫存 < 當前剩余未釋放庫存,表示當前時段已無可使用庫存,返回-1。
- 否則,返回當前剩余未釋放庫存-1。
圖片
為了解決并發問題,我們引入了Lua腳本,確保庫存扣減的原子性。
LUA腳本偽代碼:
# KEYS[1] 為 redis的key
# ARGV[1] 為 提前算出的剩余未釋放庫存
# return的值 < 0 代表庫存扣減失敗
local _args1 = ARGV[1]
local stock = redis.call('GET', KEYS[1])
if stock < _args1 then
return -1
else
local rest = redis.call('DECR', KEYS[1])
return rest
end
return -1
獎品橫向迭代
圖片
商城內部的獎品都統一維護在獎品庫里,每個類型的獎品都有通用屬性和擴展屬性,同時不同的獎品背后對應著不同的資產,需要有獨立的發獎邏輯(資產發放)。因此每增加一個類型的獎品,不僅需要增加獎品類型的枚舉,也需要對接下游資產的發放功能。
商城從一開始便不斷的在新增不同類型的獎品,從優惠券、代金券、津貼,到后面拓展出現金、虛擬獎品、道具等獎品。
而一些特殊的獎品,比如抽獎券,單獨實現一個發獎邏輯就不能實現一個完整功能,為此我們還特地拓展出抽獎券玩法。具體玩法是:用戶需要在開獎前獲得抽獎券,這時會發放一個抽獎碼給用戶,我們會在指定時間根據規則進行開獎并通知獲獎的用戶。這里除了發放抽獎券的邏輯之外,我們還需要一個獨立的開獎功能。
開獎流程如下圖:
圖片
抽獎券的玩法迭代:
抽獎券玩法的核心在于開獎定時任務的邏輯,這類抽獎券活動不同于獎品兌換,單次活動可能有百萬人參與,一萬人中獎的情況。那么這樣開獎Job的實時計算量就過于龐大了,測試環境按照這個量級進行開獎的話,耗時就已達到10多分鐘之久。這個時長對于開獎腳本來說是不可接受的了,不僅用戶體驗達不到預期,而且時長太長,這段時間如果服務一旦出現其他情況(如新功能部署、容器節點被調度等)中斷開獎流程更是會阻塞流程。
為此我們做了以下迭代優化:
- 開獎Job的實時計算前置:在用戶兌換抽獎券時,系統提前為用戶打好標簽,并隨機計算出一個分數。將用戶的分數加入到Redis的有序隊列中。這樣,在開獎時只需從有序隊列中取數,大大減少了實時計算量。
- 開獎Job引入并發操作:允許多場次和多獎品同時進行匹配,顯著提高開獎效率。當然,為了避免并發操作引起的數據錯亂,在關鍵流程中加入了Mutex鎖,確保數據的一致性和完整性。
- 異常通知與一鍵重開功能:在應用出現各類意外情況導致開獎流程被阻塞時,我們會有異常通知告知負責人,并提供一鍵重開的功能。
最終效果:經過以上優化,正式版上線時,100萬人的開獎時長從10多分鐘縮短了30倍,僅需20秒即可完成開獎。這不僅大幅提升了用戶體驗,還增強了系統的穩定性和可靠性。
兌換防刷迭代
針對于防刷策略,項目從最開始只是通過接口驗簽及H5參數加密進行防控,但這種方式的破解成本比較低,也很容易被黑灰產刷接口。到后面我們便系統梳理了從業務層、接口層及黑灰產防控三方面對代碼進行加固。
- 業務層防刷? 首先是獎品維度的頻控,這是基于3.2-業務架構概述中模板模型實現的,包含每日頻次、周期循環頻次以及總頻次。? 其次是活動維度的頻控,這個是在兌換流程中,商城中臺給業務方提供了開放能力。在兌換前置環節可以自定義校驗邏輯,比如N天可兌換N次。
圖片
圖片
- 接口層防刷? 這個主要是依賴于得物統一的流控中心,可以針對于單個接口配置限流和熔斷策略,這里就不過多贅述了。
- 黑灰產防控? 針對于這部分流量,我們首先是通過接口驗簽和時間校驗先過濾一部分。? 其次是針對IP及設備的黑名單過濾。? 還有就是針對于高價值獎品,我們會在前置環節預埋token,只有通過正常流程的用戶才能進行兌換。
項目穩定性建設
商城作為一個潛在的資損高危區,在穩定性建設上投入大量資源是必要的。本文我們首先聚焦于問題發現及應急止血方面的工作,包括通過監控和告警快速發現問題,以及通過一鍵止損開關及時對發生異常的業務進行熔斷。
監控告警
在監控告警方面,我們采用了多種技術手段來確保系統的穩定性和及時發現潛在問題:
業務監控組件:對于一些明細數據的同環比對比,我們使用了得物通用的業務監控組件。這種組件能夠實時監控業務數據的變化,幫助我們快速發現異常情況。
Prometheus SDK埋點:對于一些需要分位數統計的數據,我們使用了Golang的Prometheus SDK進行埋點,并統一收集上報。
最終這些數據統一接入到增長的監控大盤中,能夠清晰地看到各個接入方實時的訪問數據、兌換數量及面額等。
告警配置:根據采集到的數據,我們根據規則指標制作了各類告警通知,確保在異常情況發生時能夠及時響應:
- 事前預警 :獎品庫存的實時通知與查看,確保庫存充足。
- 事中告警 :獎品數量及金額的同環比變化,及時發現異常波動。
- 事后統計 :獎品的核銷率及T+1天金額對賬,確保數據準確無誤。
圖片
圖片
圖片
|
|
一鍵止損開關
為了在發生異常時能夠快速響應,我們引入了一鍵止損開關。這個開關能夠在檢測到異常情況時,立即對相關業務進行熔斷,防止資損進一步擴大。通過這種機制,我們能夠在最短時間內控制問題,減少損失。
五、更多拓展
抽獎組件搭建器
為了進一步提升抽獎玩法的易用性和靈活性,我們與前端團隊聯合打造了可視化的H5抽獎組件。該組件基于商城中臺,提供了完整的抽獎解決方案,運營人員只需按照SOP文檔進行配置,即可快速接入抽獎玩法并投入使用,真正實現了抽獎玩法的“開箱即用”。
產品特性:
- 開箱即用: 提供完整的前后端組件, 支持九宮格/翻拍/老虎機等樣式;
- 中獎控制: 中獎次數及中獎頻次控制;
- 庫存控制: 獎品每日/總庫存控制, 分時段釋放;
- 多種獎品: 優惠券/代金券/津貼/虛擬獎品等;
部分組件樣式如下,也支持在可視化的H5搭建器內自定義組件樣式。
圖片
在抽獎組件的設計中,抽獎次數的獲取是一個關鍵環節。最初,這一功能完全依賴于業務方自行實現,這導致只有已經接入過商城玩法的業務方才能使用H5的抽獎組件。為了提升組件的通用性和易用性,我們進行了以下優化:
引入增長的任務中臺
為了簡化抽獎次數的獲取流程,我們引入了增長的任務中臺。通過這一中臺,用戶可以完成配置的指定任務來獲得積分,這些積分可以直接用于參與抽獎。
圖片
打造簡易的積分系統
我們打造了一個完整的積分系統,實現了從獲得積分、消耗積分到領取獎勵的閉環流程。具體實現如下:
- 獲得積分:用戶通過完成指定任務(任務中臺提供了非常豐富的任務類型)獲得積分。
- 消耗積分:用戶使用積分參與抽獎,系統自動扣除相應積分。
- 領取獎勵:用戶中獎后,系統自動發放獎品,完成整個抽獎流程。
這種閉環流程的設計帶來了多重優勢:
- 簡化接入流程 :業務方無需自行實現抽獎次數的獲取邏輯,降低了接入成本。
- 增強通用性 :所有業務方都可以通過積分系統使用H5抽獎組件,提升了組件的適用范圍。
六、總結
本文講述了增長兌換商城整體的業務框架及部分功能的實現細節。兌換商城作為一個中臺,承接了不同上下游提出的需求,很多功能的實現都需要考慮到通用性及拓展性,而一些復雜需求或功能的實現,是否會加重配置的難度,影響后續業務方的接入成本,都是需要在項目迭代中不斷思考的問題。不僅如此,在迭代過程中的穩定性保障也會是商城自始至終的基本要求。
對于兌換商城之后的規劃:
- 拓展玩法:引入更潮流、更具吸引力的玩法,以保持用戶的持續參與和新鮮感。通過不斷創新,確保商城始終處于用戶增長的前沿。
- 降本提效:輸出簡版玩法及后臺配置,維護每個版本的Changelog及SOP文檔,確保迭代過程透明且可追溯。精簡業務接入流程,減少開發成本和維護成本,提升整體效率。
- 交流進步:本文作為大綱綜述,簡要的講解了兌換商城整體的架構,后續將輸出更深入的博客文章,詳細探討兌換商城的核心技術實現、優化策略。通過不斷的技術交流,促進項目的成熟和優化,確保商城系統的高效穩定運行。