在日常運營中,公眾號會通過給用戶下發(fā)營銷或者科普類的消息來通知客戶。 根據(jù)經(jīng)驗,微信消息下發(fā)后10分鐘后流量會逐步上升,30分鐘左右到達峰值,1個小時后會顯著下降。在這個時間段內(nèi),系統(tǒng)的壓力會很大。
在系統(tǒng)設(shè)計和改進中,系統(tǒng)的很多場景使用異步進行實現(xiàn),一方面能縮短主流程的時間處理,另一方面能夠通過異步隊列進行一定程度的削峰。今天重點介紹單個JVM內(nèi)的異步優(yōu)化實踐,不涉及分布式時的異步優(yōu)化實踐。在異步執(zhí)行時,可以調(diào)用遠程的服務(wù)集群來實現(xiàn)一定的任務(wù)分解。
部署示意圖
整個系統(tǒng)都部署在公有云上,虛擬機上有部署1個Nginx,4個Tomcat,Nginx使用隨機的方式負載均衡到Tomcat上面。虛機之間通過LB將客戶請求轉(zhuǎn)發(fā)到Nginx上面負載均衡,Nginx再將請求分配到tomcat應(yīng)用服務(wù)器上。
由多臺應(yīng)用服務(wù)器,對外服務(wù)提供Rest服務(wù),在每個Tomcat內(nèi)部使用異步隊列。同時由一臺控制服務(wù)器,進行異步任務(wù)的補償任務(wù)和管理功能。Tomcat和Redis使用多級緩存來降低對Redis的壓力,并減少依賴。
為什么要在虛機上部署Nginx將請求轉(zhuǎn)發(fā)到Tomcat,而不是由LB直接轉(zhuǎn)發(fā)到Tomcat。這是因為LB能夠支持的IP個數(shù)是有限的。
典型的用戶場景
在公眾號的運營過程中,典型的事件包括:
發(fā)送短信驗證碼
購買成功或者抽獎成功短信通知
卡券或優(yōu)惠券發(fā)放
發(fā)放微信紅包
微信消息通知
訂單流程處理
定時批處理(比如數(shù)據(jù)同步)
工作流性質(zhì)的異步任務(wù)(未完成異步任務(wù)補償)
面詳細說明不同場景能夠異步的原因:
不同場景(用戶注冊,用戶購買產(chǎn)品等)下的短信驗證碼發(fā)送,可以使用異步方式發(fā)送: 一方面是因為客戶這個時效性要求沒有那樣高,另一方面在特定時間范圍內(nèi)用戶沒有收到驗證碼,用戶可以點擊再次發(fā)送驗證碼。
購買成功或者抽獎成功后短信或者郵件通知,可以通過異步的方式進行。 因為涉及用戶的利益,要謹慎對待。一方面一定要把數(shù)據(jù)先存到數(shù)據(jù)庫或者日志里面(注意信息安全^-^,別存敏感明文信息或者加密存儲),然后再放入到異步隊列中執(zhí)行。
另一個方面,要考慮到應(yīng)用服務(wù)意外停止時,沒有發(fā)送成功數(shù)據(jù)的補償機制。 這種情況不常見,并且為了減少耦合和當前異步程序的復(fù)雜度。我們使用單獨的服務(wù)上部署異步任務(wù)補償程序,來掃描未完成的任務(wù),并且進行重放(一定要注意嚴謹性)。
優(yōu)惠券和卡券的發(fā)放,跟購買成功或抽獎成功的方式類似。\u000b可以在當前活動高峰后延時發(fā)放,并且使用異步的方式進行。
微信紅包,因為需要跟微信進行交互,并且微信會通知客戶紅包的情況,可以使用異步的方式進行。 當涉及資金或者禮品時,一定要謹慎對待設(shè)計,并且需要有方便進行異步任務(wù)停止和啟動的功能。
微信消息通知,因為跟微信進行交互,成功后微信進行通知,可以使用異步。 這個跟短信驗證碼類似。
訂單流程處理,可以使用異步,因為涉及到后續(xù)步驟可以使用簡單工作流來完成。有幾個開源的框架可以參考。
數(shù)據(jù)同步或者異步任務(wù)補償,因為是延時處理,可以使用異步進行處理。在使用時,可以配合定時任務(wù),比如cron4j來周期性的進行補償。適合后面總-分-總的任務(wù)處理模式。
針對這些“無處不在的異步”,后面詳細分析其內(nèi)在模型。
無處不在的異步
下圖包含了4種典型的異步隊列模型(圖片來源于網(wǎng)絡(luò)):
一個生產(chǎn)者生產(chǎn)數(shù)據(jù),一個消費者消費數(shù)據(jù),一般用在后臺處理的業(yè)務(wù)邏輯中。
一個生產(chǎn)者生產(chǎn)數(shù)據(jù),多個消費者消費數(shù)據(jù)(這里面有兩種情況:同一個消息,可以被多個消費者分別消費?;蛘叨鄠€消費者組成一個組,一個消費者消費一個數(shù)據(jù))。
多個生產(chǎn)者生產(chǎn)數(shù)據(jù),單個消費者消費數(shù)據(jù),可以用在限流或者排隊等候單一資源處理的場景中。
多個生產(chǎn)者分別生產(chǎn)數(shù)據(jù),多個消費者消費數(shù)據(jù)(這里面有兩種情況:同一個消息,可以被多個消費者分別消費?;蛘叨鄠€消費者組成一個組,一個消費者消費一個數(shù)據(jù))。
總分總?cè)蝿?wù)模型:特別適第一個線程取出一批數(shù)據(jù)放到隊列中(比如select);由多個線程分別執(zhí)行業(yè)務(wù)邏輯;執(zhí)行后的結(jié)果由一個線程來執(zhí)行(比如update操作,這樣能夠防止數(shù)據(jù)庫鎖)
這是從技術(shù)上分析的幾種常見模型,在實踐中涉及怎樣選擇框架。
使用堵塞隊列的線程池
使用固定步長或固定時間的隊列
使用Disruptor
使用MQ或Kafka
使用線程池實現(xiàn)異步 (支持多生產(chǎn)者,多消費者)
特點:可以使用JDK自帶的線程池實現(xiàn)異步,編程簡單,資料多。建議在并發(fā)量小的場景下優(yōu)先選擇。
使用Guava Queues (支持多生產(chǎn)者單消費者)
特點:異步批量隊列,在隊列到達指定長度,或者到達指定時間后,批量進行數(shù)據(jù)處理。適合于對響應(yīng)時間要求低,能夠容忍一定的數(shù)據(jù)丟失的場景。比如短小文本數(shù)據(jù)的批量保存。
在經(jīng)過一段時間調(diào)研后,我們發(fā)現(xiàn)Disruptor更能滿足我們需要。
首先介紹一下Disruptor的強悍的性能。
這張圖包含我列舉的上述的異步隊列模型景,因此很有代表意義。Disruptor因為使用無鎖的隊列方式,具有很高的性能。具體的原理不詳述,大家可以搜索看到。Disruptor支持上面典型場景,并且靈活使用Disruptor的工作流機制,能簡化編程。
再貼一下官方的測試結(jié)果。
使用異步后的煩惱
煩惱一: 數(shù)據(jù)丟失的風險
解決方式:先寫日志或數(shù)據(jù)庫,后放入異步隊列。
煩惱二:對其他系統(tǒng)的壓力變大
解決方式:使用一定的限流和熔斷,對其他系統(tǒng)進行保護。
煩惱三:數(shù)據(jù)保存后異步任務(wù)未執(zhí)行
解決方式:使用異步任務(wù)補償?shù)姆绞?,定期從?shù)據(jù)庫中獲取數(shù)據(jù),放到隊列中進行執(zhí)行,執(zhí)行后更新數(shù)據(jù)狀態(tài)位。
煩惱四:怎樣隊列長設(shè)置和消費者數(shù)量
解決方式:使用實際的壓力測試來獲得隊列長度?;蛘呤褂门抨犝摰臄?shù)學(xué)公式得到初步的值,然后進行實際壓測。
最后介紹一下項目中的經(jīng)驗:
量力而行:根據(jù)業(yè)務(wù)特點進行技術(shù)選型,業(yè)務(wù)量小盡量避免使用異步。有所為,有所不為
數(shù)據(jù)說話:異步時一定要進行必要的壓力測試
先找出系統(tǒng)的關(guān)鍵點:優(yōu)化單體系統(tǒng)內(nèi)的性能,再通過整體系統(tǒng)分解來全局優(yōu)化
根據(jù)團隊和項目的特點選擇框架。
-
JAVA
+關(guān)注
關(guān)注
20文章
2989瀏覽量
109595 -
異步
+關(guān)注
關(guān)注
0文章
62瀏覽量
18306
發(fā)布評論請先 登錄
HarmonyOS如何使用異步并發(fā)能力進行開發(fā)
從服務(wù)端視角看高并發(fā)難題
java異步回調(diào)和同步回調(diào)
詳細介紹了Java泛型、注解、并發(fā)編程
如何去實現(xiàn)一種基于SpringMVC的電商高并發(fā)秒殺系統(tǒng)設(shè)計
HarmonyOS如何使用異步并發(fā)能力進行開發(fā)
java之volatile并發(fā)

評論