轉(zhuǎn)轉(zhuǎn)一體化推送平臺(tái)的實(shí)踐
1 背景
轉(zhuǎn)轉(zhuǎn)不同業(yè)務(wù)會(huì)根據(jù)各自業(yè)務(wù)特點(diǎn),經(jīng)常性地向不同用戶(hù)推送各種消息。例如:
- 每天向部分圖書(shū)新用戶(hù)發(fā)放紅包,并推送站內(nèi)卡片消息,以刺激新客增長(zhǎng);
- 每周向部分奢侈品用戶(hù)發(fā)送站內(nèi)系統(tǒng)消息,以召回老用戶(hù);
- 在世界圖書(shū)日推送站內(nèi)消息,同時(shí)發(fā)布圖書(shū)微信公眾號(hào)消息;
- 在指定日期,向游戲線索商家推送push,并發(fā)送短信提醒等等。
圖片
隨著各業(yè)務(wù)的不斷成熟,“消息推送”類(lèi)型的需求逐漸累積,我們從中總結(jié)出了如下特點(diǎn):
- 不同業(yè)務(wù)的推送主體不同,例如:圖書(shū)、游戲、奢品等。
- 每次推送面向的用戶(hù)群不同,例如:瀏覽過(guò)某些頁(yè)面的用戶(hù)、提交過(guò)某種訂單的用戶(hù)、新注冊(cè)的用戶(hù)等。
- 用戶(hù)數(shù)據(jù)來(lái)源不同,有些是通過(guò)執(zhí)行HiveSQL生成的,有些是通過(guò)用戶(hù)畫(huà)像平臺(tái)圈定的,有些是產(chǎn)品同學(xué)人工指定的Excel表格。
- 推送渠道多種多樣,包含紅包、push&IM消息、短信、公眾號(hào)消息等。此外,還包含組合發(fā)送的情況,例如:先發(fā)紅包再發(fā)push,站內(nèi)消息和短信同時(shí)推送等。
- 每個(gè)推送對(duì)應(yīng)各自的推送內(nèi)容,包含:標(biāo)題文案、落地頁(yè)、圖片等。同時(shí),可能會(huì)伴隨文案的動(dòng)態(tài)替換。例如:點(diǎn)擊消息,跳轉(zhuǎn)至訂單詳情頁(yè),需要根據(jù)不同用戶(hù)替換不同落地頁(yè)鏈接。
- 不同推送有各自的推送頻次,有些是定時(shí)周期性推送,有些是臨時(shí)一次性推送。此外,周期維度各不相同,有些是每天發(fā)送,有些是指定一周中的某幾天推送。
- 推送需求的業(yè)務(wù)目的不同,有些是為了拉新,有些是為了召回,還有些是為了提醒用戶(hù)。
痛點(diǎn)在于,每次面對(duì)新的推送,我們都需要針對(duì)上述特點(diǎn),編碼一個(gè)新的推送任務(wù),而每個(gè)任務(wù)的實(shí)現(xiàn)過(guò)程,都伴隨著重復(fù)搬磚:
- 都需要增加一套推送配置,進(jìn)而生成一套消息上下文;
- 為了保證可靠性,都需要做防重頻率校驗(yàn);
- 為了提升效率,都需要通過(guò)異步方式提升速度;
- 為了保證高數(shù)據(jù)量級(jí)下的系統(tǒng)能力,還需要同時(shí)進(jìn)行速度控制。
- 都需要統(tǒng)計(jì)對(duì)成功率、觸達(dá)率、打開(kāi)率做統(tǒng)計(jì)。
上述的重復(fù)能力的實(shí)現(xiàn)會(huì)造成冗余代碼和人力成本。此外,每個(gè)推送配置分散在各個(gè)角落,也不方便代碼維護(hù)。因此,需要一個(gè)較為通用的推送平臺(tái),將這一類(lèi)需求進(jìn)行統(tǒng)一管理。
2 設(shè)計(jì)思路
整體設(shè)計(jì)思路為:將“可變的”配置化,將“不變的”系統(tǒng)化。
2.1 將"可變"的配置化
維護(hù)一張推送配置表,為每一個(gè)推送需求創(chuàng)建一條配置記錄。上述背景中提到的“特點(diǎn)”即為“可變的”,將其抽象為配置表中的一個(gè)字段。例如:
- “業(yè)務(wù)方”字段:將推送主體抽象成一個(gè)枚舉類(lèi),每個(gè)業(yè)務(wù)方對(duì)應(yīng)枚舉類(lèi)中的一個(gè)值。
- “推送類(lèi)型”字段:將不同推送渠道抽象成不同推送類(lèi)型,該字段存儲(chǔ)的是渠道列表,允許同一推送需求選擇多個(gè)推送渠道。
- 各推送內(nèi)容字段:標(biāo)題、文案、落地頁(yè)、圖片、紅包計(jì)劃id等。此外,針對(duì)上述類(lèi)似“訂單詳情頁(yè)”的推送,需要在配置項(xiàng)中加入占位符,例如:不同用戶(hù)根據(jù)訂單id區(qū)分落地頁(yè),則需要在鏈接的相應(yīng)位置中加入${訂單id}占位符。
- 用戶(hù)來(lái)源相關(guān)字段:若為HiveSQL生成的,則需要填寫(xiě)“文件id”和“API key”,以觸發(fā)sql任務(wù)執(zhí)行;若來(lái)自用戶(hù)畫(huà)像平臺(tái),則需要填寫(xiě)“人群id”,以從拉取圈定數(shù)據(jù);若為人工指定,則需要上傳excel文件,由系統(tǒng)解析。
2.2 將"不變"的系統(tǒng)化
將上述“重復(fù)編程”的工作內(nèi)容,抽取成一套代碼,沉淀為通用的系統(tǒng)能力。例如:
- 維護(hù)一張推送用戶(hù)表,記錄用戶(hù)id、對(duì)應(yīng)的配置id、推送結(jié)果、失敗原因等。
- 將配置表中的配置項(xiàng)和用戶(hù)表中的用戶(hù)信息,根據(jù)不同推送渠道,映射成推送上下文實(shí)體。
- 執(zhí)行推送前,進(jìn)行用戶(hù)維度的防重和頻率校驗(yàn),若系統(tǒng)判斷重復(fù)推送,則直接將失敗結(jié)果寫(xiě)回。
- 執(zhí)行推送時(shí),開(kāi)啟線程池異步,并限制遠(yuǎn)程服務(wù)接口調(diào)用QPS。
- 推送后,記錄推送結(jié)果,并分批將結(jié)果回寫(xiě)至用戶(hù)表。
整體設(shè)計(jì)
按照上述思路,將開(kāi)發(fā)后的推送工具可視化之后,當(dāng)新增一個(gè)推送需求時(shí),產(chǎn)品同學(xué)只需在后臺(tái)添加一個(gè)配置,將推送需求填充至各配置項(xiàng)即可。若用戶(hù)來(lái)源為HiveSQL生成,研發(fā)同學(xué)只需編寫(xiě)SQL語(yǔ)句圈定用戶(hù)群,添加至用戶(hù)表。接下來(lái)的工作全部交給系統(tǒng),即可滿足新的推送需求。一方面,將研發(fā)同學(xué)從重復(fù)搬磚中解放出來(lái),省時(shí)省力;另一方面,所有推送配置集中交給后臺(tái)管理,方便維護(hù)。
3 實(shí)現(xiàn)細(xì)節(jié)
3.1 速度控制
- 異步化:由一個(gè)主線程順序執(zhí)行推送,顯然效率低下。為保證推送速度,我們使用線程池開(kāi)啟異步推送:主線程只負(fù)責(zé)從庫(kù)表讀取推送數(shù)據(jù),分批將推送任務(wù)提交至線程池任務(wù)隊(duì)列。
異步推送過(guò)程
- 分片:當(dāng)面對(duì)百萬(wàn)甚至千萬(wàn)級(jí)數(shù)據(jù)量時(shí),推送任務(wù)集中在單節(jié)點(diǎn)上,一方面容易造成單機(jī)過(guò)載,另一方面容易達(dá)到性能上限。為進(jìn)一步提升系統(tǒng)效率,并實(shí)現(xiàn)負(fù)載均衡,我們使用分片廣播調(diào)度:將推送任務(wù)分布至集群中各機(jī)器上,分片執(zhí)行。分片策略:各機(jī)器分批從用戶(hù)表中讀取相同數(shù)據(jù)量的用戶(hù),表主鍵id%機(jī)器數(shù),若命中當(dāng)前機(jī)器號(hào),則執(zhí)行推送。
分片調(diào)度
- 斷點(diǎn)重試:用lastId記錄上一批次推送數(shù)據(jù)的結(jié)束位置,每臺(tái)機(jī)器將各自的lastId緩存至redis,用來(lái)標(biāo)識(shí)執(zhí)行進(jìn)度。若任務(wù)異常中斷,將根據(jù)lastId進(jìn)行斷點(diǎn)重試,避免全表掃描。
- 接口限速:消息推送依賴(lài)平臺(tái)能力,因此在提升效率的同時(shí),會(huì)在RPC接口調(diào)用處通過(guò)架構(gòu)組件設(shè)置全局QPS限制。
3.2 資源分配策略
無(wú)論線程池、機(jī)器數(shù)還是配置的QPS限速為多少,單時(shí)段的推送資源都有上限。因此,在面對(duì)同一時(shí)段多個(gè)配置執(zhí)行推送時(shí),必然要解決資源分配問(wèn)題。主要考慮如下幾種策略:
- 按優(yōu)先級(jí)順序
按優(yōu)先級(jí)順序
這樣分配的好處在于,策略比較簡(jiǎn)單,只需配置優(yōu)先級(jí)即可解決資源分配問(wèn)題。但同時(shí)會(huì)引入新的問(wèn)題,例如:設(shè)置優(yōu)先級(jí)時(shí)應(yīng)該考慮哪些因素?當(dāng)優(yōu)先級(jí)相同時(shí)該如何處理等。此外,優(yōu)先級(jí)低的配置可能始終沒(méi)機(jī)會(huì)被執(zhí)行。
- 按數(shù)據(jù)量大小
每個(gè)時(shí)段執(zhí)行推送前,先計(jì)算當(dāng)前時(shí)段各命中配置的數(shù)據(jù)量大小,按比例分配可執(zhí)行的推送量。
例如:共有3個(gè)配置,總數(shù)據(jù)量分別為:40、20、20,每個(gè)時(shí)段最大推送量為:30。
時(shí)段1:命中配置為前2個(gè),則按比例劃分的數(shù)據(jù)量分別為:20、10。推送結(jié)束后,3個(gè)配置的余量分別為:20、10、20。
時(shí)段2:待推配置為3個(gè),將余量按比例劃分后的數(shù)據(jù)量為:12、6、12。推送結(jié)束后剩余:8,4,8。
時(shí)段3:將剩余數(shù)據(jù)推送完畢。
按數(shù)據(jù)量大小
按數(shù)據(jù)量分配的好處在于,可以根據(jù)推送進(jìn)度,實(shí)時(shí)動(dòng)態(tài)分配占比,保證每個(gè)配置都有機(jī)會(huì)推送數(shù)據(jù),更加公平。但是策略相對(duì)復(fù)雜,每次推送前需要重新掃表計(jì)算數(shù)據(jù)量,一定程度上影響推送性能。
- 均勻分配
每個(gè)時(shí)段推送前,根據(jù)當(dāng)前命中的配置數(shù),均勻分配推送量。例如:共有3個(gè)配置,總數(shù)據(jù)量分別為:40、20、20,每個(gè)時(shí)段最大推送量為:30。
時(shí)段1:待推配置為前2個(gè),則均勻分配的數(shù)據(jù)量分別為:15、15。推送結(jié)束后,3個(gè)配置的余量分別為:25、5、20。
時(shí)段2:待推配置為3個(gè),將余量均分30后的推送量為:10、5、10。推送結(jié)束后,第2個(gè)配置的5條數(shù)據(jù)全部推送完畢,其他配置的余量分別為:20、10。
時(shí)段3:剩余配置均分30后的推送量:15,10。剩余配置1的5條數(shù)據(jù)。
時(shí)段4:將配置1的數(shù)據(jù)全部推送。
均勻分配
綜上,對(duì)比優(yōu)先級(jí)策略,均勻分配可以保證公平性,每個(gè)配置都有機(jī)會(huì)執(zhí)行一定數(shù)據(jù)量的推送,此外,均勻分配有一個(gè)默認(rèn)優(yōu)先級(jí):數(shù)據(jù)量越小越先推送完成,符合業(yè)務(wù)場(chǎng)景要求;相比按數(shù)據(jù)量策略推送,均勻分配不需要二次讀表計(jì)算數(shù)據(jù)量,效率更高。因此,我們最終選擇均勻分配策略。
3.3 問(wèn)題解決
- 現(xiàn)象
系統(tǒng)上線后,面對(duì)千萬(wàn)級(jí)的推送量,持續(xù)Full GC。
異常監(jiān)控
- 原因
主線程不斷讀取庫(kù)表,獲取用戶(hù)信息,分批提交至線程池執(zhí)行推送。由于執(zhí)行推送的耗時(shí)大于數(shù)據(jù)讀取,導(dǎo)致線程池任務(wù)隊(duì)列累積,進(jìn)而造成內(nèi)存積壓。
- 改進(jìn)
改用生產(chǎn)者-消費(fèi)者模式
圖片
維護(hù)一個(gè)固定長(zhǎng)度的阻塞隊(duì)列,隊(duì)列不空時(shí),由消費(fèi)者從隊(duì)列中取數(shù)并執(zhí)行推送;隊(duì)列不滿時(shí),由生產(chǎn)者掃表取數(shù)并放入隊(duì)列。當(dāng)生產(chǎn)者速度過(guò)快,即隊(duì)滿時(shí),生產(chǎn)者線程阻塞,等待消費(fèi)者推送數(shù)據(jù)完成后,隊(duì)列不滿時(shí),再次喚醒。
- 效果
- Full GC問(wèn)題不復(fù)存在。
- 老年代使用明顯下降。
4 上線效果
目前,已將上述功能可視化至業(yè)務(wù)后臺(tái),開(kāi)放給多業(yè)務(wù)方。
圖片
至此,每產(chǎn)生一個(gè)推送需求,產(chǎn)品同學(xué)只需在后臺(tái)新建一條推送配置,并根據(jù)需要填寫(xiě)各配置項(xiàng),即可完成推送。
圖片
上線至今,已支持轉(zhuǎn)轉(zhuǎn)多業(yè)務(wù)方日常及活動(dòng)推送,日均達(dá)百萬(wàn)級(jí)推送量,最高可達(dá)兩千萬(wàn)推送量。
5 總結(jié)
本文介紹了轉(zhuǎn)轉(zhuǎn)一體化推送平臺(tái)的實(shí)現(xiàn),現(xiàn)已成為多業(yè)務(wù)日常推送的通用工具。未來(lái)會(huì)在諸如推送目標(biāo)轉(zhuǎn)化率、推送結(jié)果定向通知等方面繼續(xù)完善和豐富系統(tǒng)能力。
關(guān)于作者
陳曦,轉(zhuǎn)轉(zhuǎn)訂單業(yè)務(wù)研發(fā)工程師。