java開(kāi)發(fā)線上事故理解RocketMQ異步精髓
引言
在高并發(fā)的場(chǎng)景下,異步是一個(gè)極其重要的優(yōu)化方向。
前段時(shí)間,生產(chǎn)環(huán)境發(fā)生一次事故,筆者認(rèn)為事故的場(chǎng)景非常具備典型性 。
寫(xiě)這篇文章,筆者想和大家深入探討該場(chǎng)景的架構(gòu)優(yōu)化方案。希望大家讀完之后,可以對(duì)異步有更深刻的理解。
1 業(yè)務(wù)場(chǎng)景
老師登錄教研平臺(tái),會(huì)看到課程列表,點(diǎn)擊課程后,課程會(huì)以視頻的形式展現(xiàn)出來(lái)。
訪問(wèn)課程詳情頁(yè)面,包含兩個(gè)核心動(dòng)作:
- 讀取課程視頻信息 :
從緩存服務(wù)器 Redis 獲取課程的視頻信息 ,返回給前端,前端通過(guò)視頻組件渲染。
- 寫(xiě)入課程觀看行為記錄 :
當(dāng)教師觀看視頻的過(guò)程中,瀏覽器每隔3秒發(fā)起請(qǐng)求,教研服務(wù)將觀看行為記錄插入到數(shù)據(jù)庫(kù)表中。而且隨著用戶在線人數(shù)越多,寫(xiě)操作的頻率也會(huì)指數(shù)級(jí)增長(zhǎng)。
上線初期,這種設(shè)計(jì)運(yùn)行還算良好,但隨著在線用戶的增多,系統(tǒng)響應(yīng)越來(lái)越慢,大量線程阻塞在寫(xiě)入視頻觀看進(jìn)度表上的 Dao 方法。上。
首先我們會(huì)想到一個(gè)非常直觀的方案,提升寫(xiě)入數(shù)據(jù)庫(kù)的能力。
- 優(yōu)化 SQL 語(yǔ)句;
- 提升 MySQL 數(shù)據(jù)庫(kù)硬件配置 ;
- 分庫(kù)分表。
這種方案其實(shí)也可以滿足我們的需求,但是通過(guò)擴(kuò)容硬件并不便宜,另外寫(xiě)操作可以允許適當(dāng)延遲和丟失少量數(shù)據(jù),那這種方案更顯得性價(jià)比不足。
那么架構(gòu)優(yōu)化的方向應(yīng)該是: “減少寫(xiě)動(dòng)作的耗時(shí),提升寫(xiě)動(dòng)作的并發(fā)度” , 只有這樣才能讓系統(tǒng)更順暢的運(yùn)行。
于是,我們想到了第二種方案:寫(xiě)請(qǐng)求異步化。
- 線程池模式
- 本地內(nèi)存 + 定時(shí)任務(wù)
- MQ 模式
- Agent 服務(wù) + MQ 模式
2 線程池模式
2014年,筆者在藝龍旅行網(wǎng)負(fù)責(zé)紅包系統(tǒng)相關(guān)工作。運(yùn)營(yíng)系統(tǒng)會(huì)調(diào)用紅包系統(tǒng)給特定用戶發(fā)送紅包,當(dāng)這些用戶登錄 app 后,app 端會(huì)調(diào)用紅包系統(tǒng)的激活紅包接口 。
激活紅包接口是一個(gè)寫(xiě)操作,速度也比較快(20毫秒左右),接口的日請(qǐng)求量在2000萬(wàn)左右。
應(yīng)用訪問(wèn)高峰期,紅包系統(tǒng)會(huì)變得不穩(wěn)定,激活接口經(jīng)常超時(shí),筆者為了快速解決問(wèn)題,采取了一個(gè)非常粗糙的方案:
"控制器收到請(qǐng)求后,將寫(xiě)操作放入到獨(dú)立的線程池中后,立即返回給前端,而線程池會(huì)異步執(zhí)行激活紅包方法"。
坦率的講,這是一個(gè)非常有效的方案,優(yōu)化后,紅包系統(tǒng)非常穩(wěn)定。
回到教研的場(chǎng)景,見(jiàn)下圖,我們也可以設(shè)計(jì)類似線程池模型的方案:
使用線程池模式,需要注意如下幾點(diǎn):
- 線程數(shù)不宜過(guò)高,避免占用過(guò)多的數(shù)據(jù)庫(kù)連接池 ;
- 需要考慮評(píng)估線程池隊(duì)列的大小,以免出現(xiàn)內(nèi)存溢出的問(wèn)題。
3 本地內(nèi)存 + 定時(shí)任務(wù)
開(kāi)源中國(guó)統(tǒng)計(jì)瀏覽數(shù)的方案非常經(jīng)典。
用戶訪問(wèn)過(guò)一次文章、新聞、代碼詳情頁(yè)面,訪問(wèn)次數(shù)字段加 1 , 在 oschina 上這個(gè)操作是異步的,訪問(wèn)的時(shí)候只是將數(shù)據(jù)在內(nèi)存中保存,每隔固定時(shí)間將這些數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)。
示例代碼如下:
我們可以借鑒開(kāi)源中國(guó)的方案 :
- 控制器接收請(qǐng)求后,觀看進(jìn)度信息存儲(chǔ)到本地內(nèi)存 LinkedBlockingQueue 對(duì)象里;
- 異步線程每隔1分鐘從隊(duì)列里獲取數(shù)據(jù) ,組裝成 List 對(duì)象,最后調(diào)用 Jdbc batchUpdate 方法批量寫(xiě)入數(shù)據(jù)庫(kù);
- 批量寫(xiě)入主要是為了提升系統(tǒng)的整體吞吐量,每次批量寫(xiě)入的 List 大小也不宜過(guò)大 。
這種方案優(yōu)點(diǎn)是:不改動(dòng)原有業(yè)務(wù)架構(gòu),簡(jiǎn)單易用,性能也高。該方案同樣需要考慮內(nèi)存溢出的風(fēng)險(xiǎn)。
4 MQ 模式
很多同學(xué)們會(huì)想到 MQ 模式 ,消息隊(duì)列最核心的功能是異步和解耦,MQ 模式架構(gòu)清晰,易于擴(kuò)展。
核心流程如下:
- 控制器接收寫(xiě)請(qǐng)求,將觀看視頻行為記錄轉(zhuǎn)換成消息 ;
- 教研服務(wù)發(fā)送消息到 MQ ,將寫(xiě)操作成功信息返回給前端 ;
- 消費(fèi)者服務(wù)從 MQ 中獲取消息 ,批量操作數(shù)據(jù)庫(kù) 。
這種方案優(yōu)點(diǎn)是:
- MQ 本身支持高可用和異步,發(fā)送消息效率高 , 也支持批量消費(fèi);
- 消息在 MQ 服務(wù)端會(huì)持久化,可靠性要比保存在本地內(nèi)存高;
不過(guò) MQ 模式需要引入新的組件,增加額外的復(fù)雜度。
5 Agent 服務(wù) + MQ 模式
互聯(lián)網(wǎng)大廠還有一種常見(jiàn)的異步的方案:Agent 服務(wù) + MQ 模式。
教研服務(wù)器上部署 Agent 服務(wù)(獨(dú)立的進(jìn)程) , 教研服務(wù)接收寫(xiě)請(qǐng)求后,將請(qǐng)求按照固定的格式(比如 JSON )寫(xiě)入到本次磁盤(pán)中,然后給前端返回成功信息。
Agent 服務(wù)會(huì)監(jiān)聽(tīng)文件變動(dòng),將文件內(nèi)容發(fā)送到消息隊(duì)列 , 消費(fèi)者服務(wù)獲取觀看行為記錄,將其存儲(chǔ)到 MySQL 數(shù)據(jù)庫(kù)中。
還有一種演進(jìn),假設(shè)我們不想在應(yīng)用中依賴消息隊(duì)列,不生成本地文件,可以采用如下的方式:
這種方案最大的優(yōu)點(diǎn)是:架構(gòu)分層清晰,業(yè)務(wù)服務(wù)不需要引入 MQ 組件。
筆者原來(lái)接觸過(guò)的性能監(jiān)控平臺(tái),或者日志分析平臺(tái)都使用這種模式。
6 總結(jié)
學(xué)習(xí)需要一層一層遞進(jìn)的思考。
第一層:什么場(chǎng)景下需要異步
- 大量寫(xiě)操作占用了過(guò)多的資源,影響了系統(tǒng)的正常運(yùn)行;
- 寫(xiě)操作異步后,不影響主流程,允許適當(dāng)延遲;
第二層:異步的外功心法
本文提到了四種異步方式:
- 線程池模式
- 本地內(nèi)存 + 定時(shí)任務(wù)
- MQ 模式
- Agent 服務(wù) + MQ 模式
它們的共同特點(diǎn)是:將寫(xiě)操作命令存儲(chǔ)在一個(gè)池子后,立刻響應(yīng)給前端,減少寫(xiě)動(dòng)作的耗時(shí)。任務(wù)服務(wù)異步從池子里獲取任務(wù)后執(zhí)行。
第三層:異步的本質(zhì)
在筆者看來(lái),異步是更細(xì)粒度的使用系統(tǒng)資源的一種方式。
在教研課程詳情場(chǎng)景里,數(shù)據(jù)庫(kù)的資源是固定的,但寫(xiě)操作占據(jù)大量數(shù)據(jù)庫(kù)資源,導(dǎo)致整個(gè)系統(tǒng)的阻塞,但寫(xiě)操作并不是最核心的業(yè)務(wù)流程,它不應(yīng)該占用那么多的系統(tǒng)資源。
我們使用異步的解決方案時(shí),無(wú)論是使用線程池,還是本地內(nèi)存 + 定時(shí)任務(wù) ,亦或是 MQ ,對(duì)數(shù)據(jù)庫(kù)資源的使用都需要在合理的范圍內(nèi),只有這樣系統(tǒng)才能順暢的運(yùn)行。
以上就是java開(kāi)發(fā)線上事故理解RocketMQ異步精髓的詳細(xì)內(nèi)容,更多關(guān)于java開(kāi)發(fā)RocketMQ異步的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Mybatis Plus條件構(gòu)造器ConditionConstructor用法實(shí)例解析
這篇文章主要介紹了Mybatis Plus條件構(gòu)造器ConditionConstructor用法實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08java數(shù)據(jù)類型轉(zhuǎn)換陷阱包括列表陷阱
這篇文章主要介紹了java數(shù)據(jù)類型轉(zhuǎn)換的一些陷阱,包括基本數(shù)據(jù)類型轉(zhuǎn)換列表陷阱,基本上這一篇就把常見(jiàn)的問(wèn)題就給大家分享一下2020-10-1030分鐘入門(mén)Java8之lambda表達(dá)式學(xué)習(xí)
本篇文章主要介紹了30分鐘入門(mén)Java8之lambda表達(dá)式學(xué)習(xí),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04Java 根據(jù)網(wǎng)絡(luò)URL獲取該網(wǎng)頁(yè)上面所有的img標(biāo)簽并下載圖片
這篇文章主要介紹了Java 根據(jù)網(wǎng)絡(luò)URL獲取該網(wǎng)頁(yè)上面所有的img標(biāo)簽并下載圖片,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-11-11java開(kāi)發(fā)分布式服務(wù)框架Dubbo原理機(jī)制詳解
這篇文章主要為大家介紹了java開(kāi)發(fā)分布式服務(wù)框架Dubbo的原理機(jī)制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的長(zhǎng)輪詢的示例代碼
長(zhǎng)輪詢是與服務(wù)器保持即時(shí)通信的最簡(jiǎn)單的方式,它不使用任何特定的協(xié)議,例如 WebSocket ,所以也不依賴于瀏覽器版本等外部條件的兼容性。本文將用Java實(shí)現(xiàn)一個(gè)簡(jiǎn)單的長(zhǎng)輪詢,需要的可以參考一下2022-08-08JDBC對(duì)MySQL數(shù)據(jù)庫(kù)布爾字段的操作方法
這篇文章主要介紹了JDBC對(duì)MySQL數(shù)據(jù)庫(kù)布爾字段的操作方法,實(shí)例分析了JDBC操作mysql布爾字段的原理與實(shí)現(xiàn)技巧,需要的朋友可以參考下2015-02-02SpringBoot對(duì)接小程序微信支付的實(shí)現(xiàn)
本文主要介紹了SpringBoot對(duì)接小程序微信支付的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧<BR>2023-09-09