作者 | 交易中臺(tái)團(tuán)隊(duì)
導(dǎo)讀
隨著公司內(nèi)容生態(tài)的蓬勃發(fā)展,內(nèi)容產(chǎn)出方和流量提供方最關(guān)注的“收益結(jié)算”的工作,也就成為重中之重。本文基于內(nèi)容分潤(rùn)結(jié)算業(yè)務(wù)為入口,介紹了實(shí)現(xiàn)過(guò)程中的重難點(diǎn),比如千萬(wàn)級(jí)和百萬(wàn)級(jí)數(shù)據(jù)量下的技術(shù)選型和最終實(shí)現(xiàn),滿足了業(yè)務(wù)需求的同時(shí),最終實(shí)現(xiàn)了高效,準(zhǔn)確的資金結(jié)算,文章旨在拋磚引玉,希望能給讀者帶來(lái)思考和幫助。
01 業(yè)務(wù)介紹全文5185字,預(yù)計(jì)閱讀時(shí)間13分鐘。
什么是內(nèi)容分潤(rùn)平臺(tái)呢?簡(jiǎn)單來(lái)說(shuō),百家號(hào)等平臺(tái)負(fù)責(zé)內(nèi)容的生產(chǎn)和引入,手百等渠道方負(fù)責(zé)內(nèi)容的分發(fā),鳳巢等廣告平臺(tái)負(fù)責(zé)在此流量上進(jìn)行變現(xiàn)。而分潤(rùn)平臺(tái),則是根據(jù)上述各方提供的數(shù)據(jù),通過(guò)核心策略模型,賦予作者、媒體、小程序主和用戶,合理的、差異化的、有競(jìng)爭(zhēng)力的分潤(rùn)收益,以吸引更加優(yōu)質(zhì)的內(nèi)容和流量的入駐和合作。通過(guò)這種多方相互協(xié)作模式,實(shí)現(xiàn)互惠共贏的目的。
1.1 三大功能點(diǎn)
針對(duì)上述的業(yè)務(wù)特點(diǎn),結(jié)算系統(tǒng)需要包含三大功能,用于支撐內(nèi)容分潤(rùn)業(yè)務(wù)的準(zhǔn)確性、合規(guī)性、及時(shí)性。
功能一:結(jié)算模型
這是我們最關(guān)鍵的功能,它負(fù)責(zé)將出色的文章轉(zhuǎn)化為作者的分潤(rùn)收益。該模型的輸入數(shù)據(jù)包括數(shù)據(jù)中臺(tái)生成的用戶維度的日分潤(rùn)明細(xì)和日補(bǔ)貼明細(xì),而輸出則是每月的結(jié)算賬單,這些賬單會(huì)被發(fā)送到統(tǒng)一業(yè)務(wù)平臺(tái)用于付款。在這個(gè)過(guò)程中,我們經(jīng)歷了一系列步驟,包括每日的計(jì)算、每月的總結(jié)、預(yù)提、計(jì)提和賬單生成等,所有這些步驟都是按照不同的維度逐層計(jì)算和聚合的,最終實(shí)現(xiàn)了賬單的付款。
功能二:C端內(nèi)容交易平臺(tái)
這個(gè)功能主要面向用戶,旨在幫助作者及時(shí)查看他們的收益,并進(jìn)一步激勵(lì)他們的創(chuàng)作動(dòng)力。作者只需登錄平臺(tái),即可查看每日的預(yù)估收益、文章的分發(fā)情況、瀏覽量等數(shù)據(jù),還可以查看每月實(shí)際的付款賬單,提供發(fā)票等相關(guān)數(shù)據(jù)。
功能三:O端管理端平臺(tái)
為了確保資金結(jié)算更加合規(guī)和準(zhǔn)確,整個(gè)結(jié)算體系引入了運(yùn)營(yíng)管理和反作弊等不同角色。這些角色在管理端負(fù)責(zé)資金管控、發(fā)票審核、黑名單管理等各種操作,以確保整個(gè)過(guò)程的合規(guī)性。
1.2 名詞解釋
PALO:百度數(shù)據(jù)倉(cāng)庫(kù),是基于開(kāi)源ApacheDoris構(gòu)建的企業(yè)級(jí)MPP云數(shù)據(jù)倉(cāng)庫(kù),可有效地支持在線實(shí)時(shí)數(shù)據(jù)分析。
BNS(BAIdu Naming Service):是指百度名字服務(wù)。BNS提供服務(wù)名稱或服務(wù)組名稱到服務(wù)所有運(yùn)行實(shí)例的映射,你可以根據(jù)一個(gè)名字(服務(wù)名或服務(wù)組) 獲取服務(wù)的信息,包括實(shí)例的主機(jī)名和IP、實(shí)例的運(yùn)行狀態(tài)、端口、負(fù)載、實(shí)例自定義配置標(biāo)簽以及其他實(shí)例自定義信息。用于滿足服務(wù)交互中常見(jiàn)的資源定位、IP白名單維護(hù)、查詢服務(wù)下的機(jī)器列表、負(fù)載均衡以及其他任何依賴于這些信息的開(kāi)發(fā)、測(cè)試和運(yùn)維需求。目前BNS已經(jīng)在全百度各業(yè)務(wù)線中廣泛使用,UB、RAL等框架的支持和各語(yǔ)言SDK也已經(jīng)發(fā)布。
02 業(yè)務(wù)架構(gòu)2.1 架構(gòu)分層介紹
圖1是整個(gè)內(nèi)容分潤(rùn)的業(yè)務(wù)架構(gòu)。內(nèi)容分潤(rùn)結(jié)算面向數(shù)據(jù)中臺(tái),業(yè)務(wù)方,用戶(作者)和運(yùn)營(yíng)管理提供服務(wù)。
△圖1.內(nèi)容分潤(rùn)結(jié)算平臺(tái)系統(tǒng)架構(gòu)
2.2 關(guān)鍵匯總文件
對(duì)于數(shù)據(jù)中臺(tái),我們是直接下游,同時(shí)在整個(gè)內(nèi)容分潤(rùn)流程的流程中,我們扮演的是最末端的角色。百家號(hào)、問(wèn)一問(wèn)、百度文庫(kù)等業(yè)務(wù)會(huì)將作者的內(nèi)容分發(fā)數(shù)據(jù)、廣告貢獻(xiàn)等給到數(shù)據(jù)中臺(tái),數(shù)據(jù)中臺(tái)按照各種分潤(rùn)計(jì)算模型歸一化數(shù)據(jù)結(jié)構(gòu),產(chǎn)出三份較為詳細(xì)的明細(xì)文件,包括日分潤(rùn)明細(xì),日內(nèi)容分發(fā)明細(xì),日補(bǔ)貼明細(xì)。
日分潤(rùn)明細(xì):作者內(nèi)容分發(fā)或流量貢獻(xiàn)所獲得的分潤(rùn)詳情,明細(xì)中包括分潤(rùn)金額,文章分發(fā)渠道,父子賬號(hào)等字段。
日補(bǔ)貼明細(xì):基于運(yùn)管管理的二次資金分配詳情。
日內(nèi)容分發(fā)明細(xì):作者的內(nèi)容分發(fā)貢獻(xiàn)報(bào)表。
數(shù)據(jù)中臺(tái)會(huì)將這些數(shù)據(jù)以離線文件的形式提供給我們,結(jié)算系統(tǒng)每日基于配置規(guī)則,進(jìn)行離線計(jì)算,最終將數(shù)據(jù)進(jìn)行降維匯總。后續(xù)每月月初,基于這些匯總數(shù)據(jù),做二次匯聚產(chǎn)出用戶收益賬單。
2.3 服務(wù)提供方式
結(jié)算系統(tǒng)根據(jù)外部需求,提供多種接入方式。面對(duì)業(yè)務(wù)方,結(jié)算系統(tǒng)提供API、網(wǎng)頁(yè)嵌入模式接入方式。若業(yè)務(wù)有其自建平臺(tái),可將結(jié)算系統(tǒng)提供的網(wǎng)頁(yè)嵌入其平臺(tái)內(nèi)部,用于展示用戶的收入信息或上傳發(fā)票等。若無(wú)自建平臺(tái),也可API形式接入。新用戶在業(yè)務(wù)側(cè)申請(qǐng)入駐作者后,業(yè)務(wù)調(diào)用結(jié)算系統(tǒng)API完成用戶注冊(cè),開(kāi)通計(jì)費(fèi)單元,維護(hù)財(cái)務(wù)信息等。后續(xù)作者在內(nèi)容分潤(rùn)平臺(tái)查看其收入,文章分發(fā)報(bào)表,重新維護(hù)財(cái)務(wù)信息等。若有重要變更或通知,系統(tǒng)通過(guò)站內(nèi)信方式通知作者。
系統(tǒng)整體支持三種賬號(hào)體系,面向作者提供兩類百度常用賬號(hào)登錄方式,面向管理端提供內(nèi)網(wǎng)賬號(hào)登錄方式,基于此賬戶體系做了靈活權(quán)限控制,不同用戶登錄管理端,看到的可操作菜單欄各不相同,避免出現(xiàn)越權(quán)操作。同時(shí)基于此賬號(hào)體系,能靈活獲取上下級(jí),構(gòu)建了自動(dòng)化的審批流程。
結(jié)算系統(tǒng)的平穩(wěn)、合規(guī)、高效運(yùn)行離不開(kāi)各類協(xié)同生態(tài)的合力支持。反作弊能力貫穿整個(gè)內(nèi)容分潤(rùn)的始終,著力于打擊黑產(chǎn),識(shí)別作弊用戶。OCR、發(fā)票平臺(tái)為發(fā)票識(shí)別,發(fā)票鑒定提供了通用服務(wù)。財(cái)務(wù)的各類審核,業(yè)務(wù)的多維度監(jiān)管則進(jìn)一步為資金結(jié)算的合規(guī)安全保駕護(hù)航。各類角色、各個(gè)系統(tǒng)協(xié)同合作,促成了目前內(nèi)容分潤(rùn)結(jié)算系統(tǒng)。
03 技術(shù)難點(diǎn)和細(xì)節(jié)
上文以整體的視角介紹了內(nèi)容分潤(rùn)結(jié)算系統(tǒng)的架構(gòu)設(shè)計(jì),下面我們將枚舉幾種業(yè)務(wù)場(chǎng)景構(gòu)建過(guò)程中的技術(shù)選型,來(lái)詳細(xì)介紹該系統(tǒng)的技術(shù)落地。
3.1 千萬(wàn)級(jí)數(shù)據(jù)日度任務(wù)的技術(shù)選型
場(chǎng)景:每日上游會(huì)給我們產(chǎn)出明細(xì)數(shù)據(jù),數(shù)據(jù)為細(xì)粒度,量級(jí)為大幾千萬(wàn)級(jí)別,格式為AFS文件(離線文件),需要基于某些過(guò)濾規(guī)則和計(jì)算規(guī)則做二次匯聚,后續(xù)支持多維度查詢,作者端展示報(bào)表。
3.1.1 DB批處理方案
最初任務(wù)是在物理機(jī)上通過(guò)sql批處理,任務(wù)串行執(zhí)行,簡(jiǎn)單明了,同時(shí)成功同時(shí)失敗。但隨著數(shù)據(jù)量持續(xù)遞增,串行執(zhí)行可能面臨著實(shí)效性問(wèn)題。基于原始的DB思路,我們構(gòu)建了基于DDBS(關(guān)系型分布式數(shù)據(jù)庫(kù)系統(tǒng))的解決方案,全部依賴于DB,因匯聚是基于用戶維度,所以基于子賬號(hào)uid計(jì)算shardingKey分表,過(guò)濾規(guī)則也落入庫(kù)中,后續(xù)使用表之間連接過(guò)濾,相同分表中的同子用戶數(shù)據(jù)匯聚。使用在線服務(wù),按照分表規(guī)則,啟動(dòng)多線程執(zhí)行任務(wù),實(shí)時(shí)寫(xiě)入日匯總數(shù)據(jù)表。具體方案如圖2。
△圖2.基于DDBS的解決方案
3.1.2 離線計(jì)算
利用SPARK天然的分布式計(jì)算能力,采用離線計(jì)算方案,匯聚時(shí)使用SPARK計(jì)算?;谏嫌翁峁┑碾x線文件,構(gòu)建RDD1文件,后續(xù)基于一些過(guò)濾規(guī)則過(guò)濾數(shù)據(jù)和然后基于集合規(guī)則,使用reduceBykey聚合,產(chǎn)出新的RDD2文件。這個(gè)RDD2文件就是我們后續(xù)使用的日表數(shù)據(jù)。因有各類在線查詢需求,需持久化到數(shù)據(jù)庫(kù)中,又因產(chǎn)出的日表需支持各角色多維度查詢,調(diào)研后采用PALO數(shù)據(jù)倉(cāng)庫(kù),具體方案如圖3所示。
△圖3.基于SPARK+PALO+DB解決方案
對(duì)比兩種方案后,我們最終選擇方案二實(shí)施。方案二的優(yōu)點(diǎn)比較突出:1.SPARK集群自帶分布式計(jì)算能力,無(wú)需我們按照方案一方式自行實(shí)現(xiàn)分布式計(jì)算;2.數(shù)據(jù)存儲(chǔ)于PALO,相比于傳統(tǒng)的MySQL,在大批量數(shù)據(jù)和多維度報(bào)表場(chǎng)景,PALO性能優(yōu)勢(shì)更加明顯。3.方案一有一個(gè)最大也是我們最踩坑的性能問(wèn)題,實(shí)時(shí)大批量寫(xiě)入DDBS數(shù)據(jù)庫(kù)導(dǎo)致較高的主從延遲,影響了其他業(yè)務(wù)場(chǎng)景。
3.2 百萬(wàn)級(jí)數(shù)據(jù)的月度任務(wù)
場(chǎng)景:基于上述場(chǎng)景會(huì)產(chǎn)出月表,數(shù)據(jù)量大約在百萬(wàn)級(jí)別,遵循月度出賬計(jì)算模型,產(chǎn)出最終的預(yù)提數(shù)據(jù)。日度任務(wù)和月度任務(wù)的最主要區(qū)別在于日度任務(wù)計(jì)算過(guò)程密集,月度任務(wù)過(guò)濾過(guò)程密集。
月度產(chǎn)出計(jì)提任務(wù)實(shí)際就是計(jì)算用戶本月收入以及本月可結(jié)算的收入,可結(jié)算收入=以前累積未結(jié)算金額+本月收入。目前該任務(wù)輸入的數(shù)據(jù)量相對(duì)較少,且以過(guò)濾為核心,因此此類任務(wù)未采用SPARK計(jì)算。而各類過(guò)濾規(guī)則與當(dāng)前用戶各種屬性息息相關(guān),因此任務(wù)圍繞用戶uid展開(kāi),采用以用戶uid為底表,先通過(guò)各類策略過(guò)濾uid,后置再計(jì)算的方案。數(shù)據(jù)量雖然相對(duì)日度任務(wù)較少,但畢竟在百萬(wàn)級(jí)別,如果使用單一線程處理所有用戶,速度會(huì)極其緩慢,所以必須拆分任務(wù),使用并行計(jì)算的方式提升效率,而如何拆分任務(wù),如何保障任務(wù)全部執(zhí)行是月度任務(wù)模型需要考慮的核心問(wèn)題。
3.2.1 冪等的分布式數(shù)據(jù)批處理框架master節(jié)點(diǎn)
我們?cè)O(shè)計(jì)了主從任務(wù)模型,用于支持上述任務(wù)拆分執(zhí)行,主結(jié)點(diǎn)先置啟動(dòng),用于數(shù)據(jù)備份、初始化出賬任務(wù),以及調(diào)度從節(jié)點(diǎn)。從結(jié)點(diǎn)則等待主結(jié)點(diǎn)啟動(dòng)子任務(wù)指令,啟動(dòng)后獲取子任務(wù)執(zhí)行。具體模型如下圖4,5所示。
△圖4.主節(jié)點(diǎn)生命周期
圖5描述了主節(jié)點(diǎn)的生命周期,主節(jié)點(diǎn)收到出賬指令后,優(yōu)先做的是賬戶余額類表的數(shù)據(jù)備份,這個(gè)動(dòng)作歸因于我們?cè)露热蝿?wù)的特殊性,月度任務(wù)產(chǎn)出的數(shù)據(jù)表在其他時(shí)間不會(huì)更新,即上個(gè)月出賬結(jié)束后,賬戶余額類的相關(guān)表會(huì)在下一次出賬完畢才更新。
備份表的環(huán)節(jié)非常重要:
1.是可以在月度任務(wù)結(jié)束后做數(shù)據(jù)總額驗(yàn)證工作;
2.是可以用于兜底,一旦月度任務(wù)產(chǎn)出數(shù)據(jù)異常,也可回退到備份數(shù)據(jù),重新啟動(dòng)任務(wù)。
主節(jié)點(diǎn)任務(wù)的第二步則是確認(rèn)出賬任務(wù)的用戶uid范圍,我們系統(tǒng)為了既支持C端用戶體系,也支持商家賬號(hào)體系,重新設(shè)計(jì)了一套內(nèi)部用戶id,不論是用戶賬號(hào)還是商家賬號(hào)的id均會(huì)唯一映射成一個(gè)內(nèi)部uid,后文提到的該任務(wù)的uid均為內(nèi)部uid。內(nèi)部uid為自增id,因此查詢數(shù)據(jù)庫(kù),即可獲取到最大uid和最小uid,也就確定了我們本次任務(wù)的uid范圍。在redis中設(shè)置兩個(gè)key代表uid的最值。至此,出賬任務(wù)的前置準(zhǔn)備工作就完成了。主節(jié)點(diǎn)獲取執(zhí)行子任務(wù)配置的BNS,基于BNS解析出所有實(shí)例,發(fā)送子出賬任務(wù)指令,子實(shí)例獲取到指令后,啟動(dòng)N個(gè)線程執(zhí)行任務(wù),即假設(shè)有M個(gè)子實(shí)例,那最終就是M*N個(gè)線程同時(shí)執(zhí)行任務(wù)。從主節(jié)點(diǎn)的任務(wù)可看出,該任務(wù)無(wú)其特殊性,即主節(jié)點(diǎn)實(shí)際和從結(jié)點(diǎn)是平等關(guān)系,任何實(shí)例都可成為主,也可成為從,這就為調(diào)度任務(wù)進(jìn)一步提高了靈活性。
3.2.2 woker節(jié)點(diǎn)的任務(wù)流程
△圖5.從節(jié)點(diǎn)生命周期
圖5以上述實(shí)例中的一個(gè)線程作為示例,詳細(xì)描述了線程啟動(dòng)后,執(zhí)行的子任務(wù)的過(guò)程。首先獲取目前的最大uid和最小uid,最大uid為主節(jié)點(diǎn)固定值,最小uid則是一個(gè)游標(biāo)。若最小uid已經(jīng)大于最大uid,則代表所有uid已經(jīng)處理完畢,線程結(jié)束。若不滿足上述條件,則繼續(xù)執(zhí)行任務(wù),利用redis的incryBy指令,將最小uid向前移動(dòng)N個(gè)數(shù)值,這N個(gè)uid就是本次子任務(wù)的執(zhí)行范圍。拿到uid后,先將uid變?yōu)镹條任務(wù)批量落入Job表,并設(shè)置初始化狀態(tài)。落庫(kù)失敗,引入報(bào)警機(jī)制。落庫(kù)成功后,按照出賬模型,啟動(dòng)過(guò)濾規(guī)則。所有被過(guò)濾的用戶uid均批量寫(xiě)入job表,設(shè)置任務(wù)結(jié)束狀態(tài),并且標(biāo)記過(guò)濾原因,便于后續(xù)運(yùn)營(yíng)查詢。過(guò)濾規(guī)則執(zhí)行完畢,剩余uid十不存一,此時(shí)我們利用sql計(jì)算本月用戶結(jié)算金額。計(jì)算完畢,寫(xiě)入jobDB的臨時(shí)產(chǎn)出表,設(shè)置job任務(wù)完結(jié)態(tài),此時(shí)一輪子任務(wù)就執(zhí)行完畢。線程繼續(xù)重復(fù)執(zhí)行上述過(guò)程,直至所有線程均結(jié)束,代表出賬任務(wù)執(zhí)行完畢。
3.2.3 出賬確認(rèn)任務(wù)
所有任務(wù)執(zhí)行完畢后,主節(jié)點(diǎn)會(huì)收到出賬任務(wù)確認(rèn)指令。
△圖6.出賬確認(rèn)任務(wù)
該任務(wù)的主要目的就是確認(rèn)所有uid均執(zhí)行完畢,無(wú)疏漏,具體如圖6所示。上文提到,子任務(wù)執(zhí)行時(shí),都是先置落庫(kù)job表的,確認(rèn)任務(wù)的第一步:掃描job表,看是否有非完結(jié)態(tài)的任務(wù),若有,則啟動(dòng)子任務(wù),重新執(zhí)行這批數(shù)據(jù)。確認(rèn)任務(wù)第二步:獲取job表中所有執(zhí)行的uid數(shù)量和需要執(zhí)行任務(wù)的uid數(shù)量,確認(rèn)數(shù)量是否一致,若不一致,重新執(zhí)行出賬任務(wù),任務(wù)基于uid和業(yè)務(wù)期間重入,已經(jīng)被執(zhí)行的任務(wù)會(huì)被跳過(guò)。多次兜底策略執(zhí)行完畢后,數(shù)據(jù)總量校驗(yàn)一致后,會(huì)將臨時(shí)月度產(chǎn)出數(shù)據(jù)寫(xiě)入正式DB,清理臨時(shí)數(shù)據(jù)。之所以設(shè)置臨時(shí)表:1.是為了數(shù)據(jù)校驗(yàn)工作,若數(shù)據(jù)校驗(yàn)異常,可快速清理該表,重新啟動(dòng)任務(wù);2.若直接寫(xiě)入正式線上庫(kù),大量數(shù)據(jù)的并發(fā)寫(xiě)入會(huì)導(dǎo)致數(shù)據(jù)庫(kù)的主從延遲,會(huì)影響其他線上實(shí)時(shí)業(yè)務(wù)場(chǎng)景。后置寫(xiě)入實(shí)現(xiàn)了另類的『讀寫(xiě)分離』,任務(wù)過(guò)程中僅讀正式表,任務(wù)完畢臨時(shí)表往正式表寫(xiě)入數(shù)據(jù)。
04 總結(jié)
本文主要介紹了在構(gòu)建結(jié)算系統(tǒng)過(guò)程中的幾個(gè)技術(shù)重點(diǎn)和難點(diǎn),而要維護(hù)整套系統(tǒng)的平穩(wěn)運(yùn)行,不僅有這些技術(shù)重點(diǎn),也有看似微不足道但卻環(huán)環(huán)相扣的細(xì)枝末節(jié),保障每個(gè)環(huán)節(jié)不掉鏈子是運(yùn)維工作的重要一環(huán),后續(xù)我們將著力于提升運(yùn)維效率,節(jié)省人力成本,向著運(yùn)維自動(dòng)化、智能化改造。另外目前的技術(shù)方案取決于我們的數(shù)據(jù)量級(jí),未來(lái)業(yè)務(wù)蓬勃發(fā)展,業(yè)務(wù)架構(gòu)也會(huì)持續(xù)迭代,期待我們向著更加完備的架構(gòu)前進(jìn)。
——END——