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