唐煜舟
摘要:該文分析了現(xiàn)有微信公眾號(hào)平臺(tái)存在的問(wèn)題,對(duì)如何優(yōu)化現(xiàn)有微信公眾號(hào)平臺(tái)以便支持千萬(wàn)級(jí)用戶(hù)訪問(wèn)量的總體需求進(jìn)行了分析,介紹了系統(tǒng)架構(gòu)調(diào)整的原因以及如何調(diào)整,具體的調(diào)整內(nèi)容以及如何實(shí)現(xiàn)。
關(guān)鍵詞:微信公眾平臺(tái);異步;隊(duì)列;緩存
中圖分類(lèi)號(hào):TP311 ? ? ? ?文獻(xiàn)標(biāo)識(shí)碼:A
文章編號(hào):1009-3044(2019)15-0096-04
1 概述
J銀行信用卡中心微信服務(wù)號(hào)自2014年1月正式上線以來(lái),累計(jì)關(guān)注用戶(hù)數(shù)已突破2000萬(wàn),綁卡用戶(hù)數(shù)已突破1700萬(wàn)。微信服務(wù)號(hào)已經(jīng)成為卡中心電子渠道中最為活躍、最為重要的一個(gè)部分,同時(shí)也成為卡中心市場(chǎng)推廣、營(yíng)銷(xiāo)的最重要的手段之一。
預(yù)計(jì)2019年底J銀行信用卡中心微信服務(wù)號(hào)關(guān)注用戶(hù)數(shù)將突破3000萬(wàn),對(duì)微信公眾號(hào)平臺(tái)自身的容量及可用性提出了更高的要求??ㄖ行奈⑿欧?wù)號(hào)定時(shí)全量客戶(hù)群發(fā)也會(huì)對(duì)卡中心各個(gè)系統(tǒng)造成沖擊。
本文分析了為達(dá)到業(yè)務(wù)目標(biāo)現(xiàn)有微信公眾號(hào)平臺(tái)存在的問(wèn)題,對(duì)如何支撐業(yè)務(wù)目標(biāo)改造現(xiàn)有微信公眾平臺(tái)的總體需求進(jìn)行了分析,探討了如何優(yōu)化J銀行卡中心現(xiàn)有微信公眾號(hào)平臺(tái)的系統(tǒng)架構(gòu)并重構(gòu)部分功能以支持千萬(wàn)級(jí)用戶(hù)量的微信公眾號(hào)平臺(tái)。
2 總體需求分析
為滿(mǎn)足2019年底微信服務(wù)號(hào)關(guān)注用戶(hù)數(shù)達(dá)到3000萬(wàn)的業(yè)務(wù)目標(biāo),微信公眾平臺(tái)亟須解決問(wèn)題如下:
1) 騰訊與微信公眾號(hào)平臺(tái)對(duì)接的網(wǎng)絡(luò)不穩(wěn)定
騰訊通過(guò)J銀行總行payment域名與卡中心微信公眾號(hào)平臺(tái)對(duì)接,網(wǎng)絡(luò)鏈路長(zhǎng)、故障點(diǎn)多,已多次發(fā)生由于線路故障導(dǎo)致交易中斷的情況。
2) 業(yè)務(wù)高峰期系統(tǒng)響應(yīng)慢
在用戶(hù)賬單日或者J銀行信用卡開(kāi)展大型活動(dòng)期間,瞬間用戶(hù)訪問(wèn)量超過(guò)微信公眾號(hào)平臺(tái)、后端業(yè)務(wù)系統(tǒng)容量上限時(shí),會(huì)造成響應(yīng)緩慢甚至無(wú)響應(yīng)情況。同時(shí),騰訊在大量請(qǐng)求無(wú)回復(fù)后會(huì)重復(fù)發(fā)起請(qǐng)求,用戶(hù)也會(huì)在系統(tǒng)無(wú)響應(yīng)后重復(fù)點(diǎn)擊微信公眾號(hào)菜單,導(dǎo)致情況進(jìn)一步惡化。
3) 下發(fā)模板消息無(wú)流量控制
后端業(yè)務(wù)系統(tǒng)對(duì)接微信公眾號(hào)平臺(tái)發(fā)送模板消息,如訪問(wèn)騰訊服務(wù)器的線路故障,將導(dǎo)致請(qǐng)求積壓,影響微信公眾號(hào)平臺(tái)、后端業(yè)務(wù)系統(tǒng)的并行處理能力,甚至導(dǎo)致系統(tǒng)宕機(jī)。大量的下發(fā)模板消息也容易引導(dǎo)用戶(hù)訪問(wèn)J銀行卡中心諸如官網(wǎng)、App、商城等其他對(duì)外系統(tǒng),造成對(duì)其他系統(tǒng)沖擊,引發(fā)其他系統(tǒng)宕機(jī)。
為解決上述問(wèn)題,微信公眾號(hào)平臺(tái)亟須優(yōu)化現(xiàn)有架構(gòu),以便系統(tǒng)容量能夠滿(mǎn)足日益增長(zhǎng)的業(yè)務(wù)目標(biāo)。
3 系統(tǒng)架構(gòu)設(shè)計(jì)
用戶(hù)關(guān)注J銀行信用卡中心微信公眾號(hào),在3*5菜單或者對(duì)話框中輸入問(wèn)題,點(diǎn)擊發(fā)送后至騰訊,騰訊將用戶(hù)的請(qǐng)求發(fā)送至J銀行總行payment域名。J銀行總行payment域名將消息轉(zhuǎn)發(fā)至微信公眾號(hào)平臺(tái),Proxy軟負(fù)載服務(wù)器收到請(qǐng)求后,將請(qǐng)求隨機(jī)分發(fā)至多臺(tái)Robot服務(wù)器中的某一臺(tái),Robot服務(wù)器調(diào)用平臺(tái)的AI引擎獲取后續(xù)操作指令后調(diào)用后端業(yè)務(wù)系統(tǒng)返回處理結(jié)果。
后端業(yè)務(wù)系統(tǒng)也可以通過(guò)調(diào)用微信公眾號(hào)平臺(tái)的下發(fā)/群發(fā)服務(wù)器,將營(yíng)銷(xiāo)內(nèi)容、交易提醒等實(shí)時(shí)模板消息發(fā)送至騰訊,騰訊將實(shí)時(shí)模板消息推送至用戶(hù)的微信號(hào)上。
現(xiàn)有系統(tǒng)架構(gòu)如圖1。
上述架構(gòu)存在消息接入鏈路長(zhǎng)、每筆消息同步回復(fù)容易對(duì)后端系統(tǒng)造成沖擊、系統(tǒng)響應(yīng)慢導(dǎo)致用戶(hù)重復(fù)請(qǐng)求以及實(shí)時(shí)模板下發(fā)無(wú)法實(shí)現(xiàn)流控等等弊端?,F(xiàn)有微信公眾號(hào)平臺(tái)架構(gòu)已無(wú)法滿(mǎn)足2019年底微信服務(wù)號(hào)用戶(hù)數(shù)達(dá)到3000萬(wàn)的業(yè)務(wù)目標(biāo),更無(wú)法適應(yīng)互聯(lián)網(wǎng)用戶(hù)無(wú)規(guī)律、大并發(fā)、脈沖式的訪問(wèn)。故本文引入專(zhuān)線接入、異步回復(fù)、隊(duì)列機(jī)制、緩存機(jī)制、重復(fù)請(qǐng)求甄別機(jī)制、實(shí)時(shí)模板下發(fā)流控等機(jī)制,對(duì)系統(tǒng)架構(gòu)進(jìn)行調(diào)整。
調(diào)整后系統(tǒng)架構(gòu)如圖2。
4 具體調(diào)整內(nèi)容
4.1 專(zhuān)線接入
目前騰訊平臺(tái)發(fā)送微信公眾號(hào)平臺(tái)的報(bào)文需通過(guò)J銀行總行payment域名及總行DMZ、卡中心辦公網(wǎng)轉(zhuǎn)發(fā)??ㄖ行闹鲃?dòng)訪問(wèn)騰訊服務(wù)器有2條線路,一條是通過(guò)卡中心DMZ的外網(wǎng)出口(備用線路),一條是通過(guò)電信的專(zhuān)線出口(主用線路)。
現(xiàn)有接入方式如圖3。
現(xiàn)有平臺(tái)與騰訊服務(wù)器的接入方式存在線路環(huán)節(jié)較多、延時(shí)較大、出故障概率高、問(wèn)題排查較復(fù)雜等問(wèn)題。為解決上述問(wèn)題,改進(jìn)機(jī)制如下:
1) 通過(guò)電信、鐵通專(zhuān)線直連騰訊機(jī)房,跳過(guò)中間環(huán)節(jié),減少故障概率。
2) 平臺(tái)入口由電信、鐵通專(zhuān)線在物理層面形成主備,同時(shí)保留卡中心DMZ出口,將騰訊服務(wù)器的地址作為基礎(chǔ)參數(shù),快速實(shí)現(xiàn)出口網(wǎng)絡(luò)(專(zhuān)線、互聯(lián)網(wǎng))環(huán)境切換。
調(diào)整后采用專(zhuān)線接入方式如圖4。
4.2 異步回復(fù)
目前微信公眾號(hào)平臺(tái)所接收到的服務(wù)請(qǐng)求會(huì)通過(guò)平臺(tái)自有的Proxy軟負(fù)載服務(wù)器平均分配至3臺(tái)Robot服務(wù)器,如果后端業(yè)務(wù)系統(tǒng)響應(yīng)超過(guò)4s則回復(fù)空串。
未采用異步回復(fù)方式如圖5。
4S等待時(shí)間并無(wú)太大意義,一旦超時(shí),對(duì)于用戶(hù)請(qǐng)求來(lái)說(shuō)相當(dāng)于浪費(fèi)了4S。同時(shí),由于延遲響應(yīng),將堆積后續(xù)服務(wù)請(qǐng)求,對(duì)系統(tǒng)造成更大的壓力。為解決上述問(wèn)題,改進(jìn)機(jī)制如下:
1) Proxy軟負(fù)載服務(wù)器將請(qǐng)求放入隊(duì)列服務(wù)器后即返回空串,具體實(shí)現(xiàn)如下:
//返回騰訊空字符串內(nèi)容
String con = "success";
//直接在IO中返回將空字符串內(nèi)容返回騰訊
out.write(con);
// 將請(qǐng)求放入隊(duì)列服務(wù)器
executor.execute(new Runnable() {
public void run() {
KafkaUntils.push(topic_req, openid, reqxml);
}
});
2) 由后端Robot服務(wù)器從隊(duì)列服務(wù)器中獲取請(qǐng)求并處理用戶(hù)的請(qǐng)求后異步返回結(jié)果,具體實(shí)現(xiàn)如下:
channel.send(return.get(resp));
調(diào)整后采用異步回復(fù)方式如圖6。
4.3 隊(duì)列機(jī)制
微信公眾號(hào)平臺(tái)在接收到用戶(hù)同時(shí)大量并發(fā)請(qǐng)求后直接發(fā)送至后端,在超出后端處理能力的情況下,將導(dǎo)致對(duì)后端資源的爭(zhēng)搶?zhuān)蠖颂幚眄憫?yīng)將變慢,交易耗時(shí)增加。
未采用隊(duì)列的方式如圖7。
采用隊(duì)列后,微信公眾號(hào)平臺(tái)將以后端能夠承受的速度發(fā)送請(qǐng)求。假設(shè)1筆交易平均耗時(shí)0.25s(平均5個(gè)接口*50ms=0.25s),在后端系統(tǒng)不擴(kuò)容、響應(yīng)不變的前提下,如果交易量增加1倍,則原有用戶(hù)交易不受影響,新增一半用戶(hù)交易時(shí)長(zhǎng)增加0.25s,但預(yù)計(jì)可抵消由于交易量激增導(dǎo)致的后端響應(yīng)緩慢問(wèn)題。
采用隊(duì)列后的效果如圖8。
采用消息隊(duì)列技術(shù),所有的用戶(hù)請(qǐng)求都先進(jìn)入到隊(duì)列服務(wù)器中,程序保持先進(jìn)先出的原則順序讀取隊(duì)列并對(duì)所有用戶(hù)的請(qǐng)求進(jìn)行處理。Proxy服務(wù)器根據(jù)算法將不同用戶(hù)的消息放置入不同隊(duì)列中,不同Robot服務(wù)器從不同隊(duì)列中獲取消息。Proxy服務(wù)器監(jiān)控隊(duì)列深度,一旦發(fā)現(xiàn)某隊(duì)列深度超過(guò)最大值,則判為Robot服務(wù)器故障,改變分發(fā)策略。同時(shí),隊(duì)列深度超過(guò)一定閾值還會(huì)觸發(fā)告警,以便運(yùn)維人員及時(shí)了解Robot服務(wù)器、后端業(yè)務(wù)系統(tǒng)是否存在故障。
Robot服務(wù)器以指定的速度(考慮后端業(yè)務(wù)系統(tǒng)處理能力)從隊(duì)列服務(wù)器中獲取消息后送后端業(yè)務(wù)系統(tǒng)處理。Robot服務(wù)器將返回消息放入隊(duì)列服務(wù)器,由Proxy服務(wù)器以最快速度發(fā)送騰訊。
隊(duì)列機(jī)制具體實(shí)現(xiàn)如下:
Map
List
//從隊(duì)列中獲取數(shù)據(jù)以指定頻率發(fā)送后端業(yè)務(wù)系統(tǒng)處理
if(topic.equals(PropertiesUtil.getValueByKey("topic"))){
ExecutorService executor = Executors.newFixedThreadPool(Integer.parseInt(a_numThreads));
for (final KafkaStream stream : streams) {
executor.submit(new KafkaMasssendThread(stream));
}
}
隊(duì)列方式的技術(shù)實(shí)現(xiàn)如圖9。
4.4 緩存機(jī)制
在業(yè)務(wù)處理的過(guò)程中,引入Redis緩存讀取機(jī)制,即在處理請(qǐng)求前查看是否存在可用的緩存內(nèi)容并加以使用,以及在完成業(yè)務(wù)處理后將結(jié)果保存到緩存中以供重復(fù)使用。
緩存設(shè)計(jì)在以下幾個(gè)場(chǎng)景中使用:
1) 對(duì)騰訊端內(nèi)容的緩存:緩存請(qǐng)求響應(yīng)報(bào)文中的Token供系統(tǒng)生成報(bào)文時(shí)使用,具體實(shí)現(xiàn)如下:
URLConnection conn = new URL(tokenUrl).openConnection();
ByteArrayOutputStream ?bytesOut =
new ByteArrayOutputStream();
accessToken = new String(bytesOut.toByteArray(), "UTF-8");
2) 對(duì)綁定關(guān)系的緩存:緩存用戶(hù)微信openid與用戶(hù)賬戶(hù)usrid的綁定關(guān)系、用戶(hù)下掛卡關(guān)系,具體實(shí)現(xiàn)如下:
Object[] bindinfo = bindService.isBind(openid, nickname);
3) 對(duì)每個(gè)用戶(hù)操作行為緩存:供對(duì)重復(fù)請(qǐng)求判斷時(shí)使用,具體實(shí)現(xiàn)如下:
boolean isRepeat = RedisService.isRepeat(openid, action);
緩存機(jī)制技術(shù)實(shí)現(xiàn)如圖10。
4.5 重復(fù)請(qǐng)求甄別機(jī)制
Proxy服務(wù)器每次接收請(qǐng)求后從Redis緩存服務(wù)器中獲取用戶(hù)操作行為,對(duì)用戶(hù)在短時(shí)間內(nèi)重復(fù)請(qǐng)求的甄別,按照一定規(guī)則(例如10s內(nèi)同類(lèi)型重復(fù)請(qǐng)求)排除重復(fù)請(qǐng)求防止對(duì)系統(tǒng)資源的浪費(fèi):
1) 如果為重復(fù)請(qǐng)求,將重復(fù)請(qǐng)求放入重復(fù)請(qǐng)求的隊(duì)列中,由存儲(chǔ)服務(wù)器記錄供相關(guān)維護(hù)人員查看。
2) 如果不為重復(fù)請(qǐng)求,則緩存用戶(hù)請(qǐng)求后將用戶(hù)請(qǐng)求放入隊(duì)列中供Robot服務(wù)器處理。
3) 對(duì)于短時(shí)間內(nèi)來(lái)源于同一用戶(hù)的大量重復(fù)請(qǐng)求做出預(yù)警,有相關(guān)人員來(lái)判斷是否屬于惡意攻擊。
重復(fù)請(qǐng)求甄別機(jī)制具體實(shí)現(xiàn)如下:
boolean isRepeat = RedisService.isRepeat(openid, action);
if(isRepeat){
//重復(fù)請(qǐng)求放入相應(yīng)隊(duì)列供業(yè)務(wù)人員、運(yùn)維人員分析
KafkaUntils.push(topic_req, openid, reqxml);
}else{
//正常請(qǐng)求由Robot服務(wù)器處理
int result = this.proxyClient.executeMethod(post);
}
重復(fù)請(qǐng)求甄別機(jī)制技術(shù)實(shí)現(xiàn)如圖11。
4.6 實(shí)時(shí)模板下發(fā)流控
現(xiàn)有微信公眾號(hào)平臺(tái)的下發(fā)/群發(fā)模塊在收到后端系統(tǒng)的下發(fā)請(qǐng)求后直接將下發(fā)請(qǐng)求發(fā)送至騰訊服務(wù)器。如騰訊服務(wù)器響應(yīng)超時(shí),將影響使用下發(fā)接口的后端業(yè)務(wù)系統(tǒng)。同時(shí),平臺(tái)直接接收后端系統(tǒng)的下發(fā)請(qǐng)求并實(shí)時(shí)發(fā)送,無(wú)法對(duì)下發(fā)的模板實(shí)現(xiàn)并發(fā)數(shù)等流量控制。
調(diào)整前實(shí)時(shí)模板下發(fā)方式如圖12。
改造后,微信公眾號(hào)平臺(tái)下發(fā)/群發(fā)服務(wù)器在收到下發(fā)請(qǐng)求后將消息寫(xiě)入隊(duì)列服務(wù)器并立刻返回后端系統(tǒng),Proxy服務(wù)器從隊(duì)列服務(wù)器中獲取下發(fā)請(qǐng)求后發(fā)送騰訊。
實(shí)時(shí)模板下發(fā)流控具體實(shí)現(xiàn)如下:
//流控隊(duì)列線程池處理
ExecutorService executor = Executors.newFixedThreadPool(Integer.parseInt(b_numThreads));
for (final KafkaStream stream : streams) {
executor.submit(new KafkaMasssendThread(stream));
}
}
調(diào)整后實(shí)時(shí)模板下發(fā)方式如圖13。
4.7 實(shí)時(shí)模板消息回執(zhí)處理
微信公眾號(hào)平臺(tái)在實(shí)時(shí)模板消息發(fā)送后對(duì)消息的發(fā)送回執(zhí)進(jìn)行判斷:對(duì)于未發(fā)送成功的消息回執(zhí)進(jìn)行存儲(chǔ),并生成相應(yīng)報(bào)表,后期可通過(guò)人工干預(yù)的方式進(jìn)行處理。
消息回執(zhí)中包含以下有用內(nèi)容:用戶(hù)openID、所發(fā)消息messageID、發(fā)送是否成功標(biāo)示、發(fā)送失敗原因標(biāo)示(用戶(hù)屏蔽、其他原因)。
具體實(shí)現(xiàn)如下:
//模板消息回執(zhí)放置入隊(duì)列稍后處理
if event.equals("TEMPLATESENDJOBFINISH") {
executor.execute(new Runnable() {
public void run() {
KafkaUntils.push(topic_templatesend, openid, reqxml);
}
});
}
實(shí)時(shí)模板消息回執(zhí)處理實(shí)現(xiàn)方式如圖14。
5 結(jié)束語(yǔ)
通過(guò)引入專(zhuān)線接入、異步回復(fù)機(jī)制、隊(duì)列機(jī)制、緩存機(jī)制、重復(fù)請(qǐng)求甄別機(jī)制、實(shí)時(shí)模板下發(fā)流控等機(jī)制后,J銀行卡中心微信公眾號(hào)平臺(tái)系統(tǒng)容量、可靠性、穩(wěn)定性獲得了極大的提升。
平臺(tái)架構(gòu)調(diào)整后,微信公眾號(hào)平臺(tái)性能、容量可滿(mǎn)足2019年底J銀行卡中心微信服務(wù)號(hào)關(guān)注用戶(hù)數(shù)達(dá)到3000萬(wàn)的業(yè)務(wù)目標(biāo),J銀行卡中心微信公眾號(hào)更是獲得了信用卡行業(yè)關(guān)注用戶(hù)數(shù)、品牌影響力、美譽(yù)度等綜合排名僅次于行業(yè)標(biāo)桿招行信用卡的微信公眾號(hào)。
【通聯(lián)編輯:代影】