websocket實(shí)現(xiàn)進(jìn)度條

背景

一鍵導(dǎo)出功能,一次可以導(dǎo)出大量數(shù)據(jù),最終導(dǎo)出形式為壓縮包。后端需要把生成壓縮包的進(jìn)度實(shí)時(shí)發(fā)給前端

痛點(diǎn)

1.如果用戶導(dǎo)出的數(shù)據(jù)量過(guò)大,后端生成壓縮包時(shí)間過(guò)長(zhǎng),超出http請(qǐng)求的時(shí)間限制,前端會(huì)斷開(kāi)連接
2.用戶需要等待較長(zhǎng)時(shí)間,體驗(yàn)較差

技術(shù)方案

1.前端生成uuid,使用uuid作為websocket的userId,開(kāi)啟websocket
2.后端使用ConcurrentHashMap保存userId與對(duì)應(yīng)的session,key為userId,value為session
3.在導(dǎo)出文件的http請(qǐng)求的參數(shù)中加入uuid,后端根據(jù)uuid找到session,并把文件導(dǎo)出進(jìn)度通過(guò)websocket實(shí)時(shí)發(fā)送到前端

問(wèn)題

以上方案,在單機(jī)系統(tǒng)沒(méi)有問(wèn)題,但如果是分布式系統(tǒng),會(huì)有問(wèn)題。
后端服務(wù)通過(guò)容器部署,假設(shè)是雙實(shí)例,前端websocket連接請(qǐng)求發(fā)到實(shí)例A,但是http請(qǐng)求發(fā)到了實(shí)例B,實(shí)例B上找不到userId對(duì)應(yīng)的session,因此發(fā)送消息給前端失敗。
系統(tǒng)已經(jīng)做了會(huì)話保持,但是是通過(guò)cookie(JSESSIONID)實(shí)現(xiàn)的,而不是nginx的iphash,因此只能控制http請(qǐng)求發(fā)到同一個(gè)實(shí)例,無(wú)法控制websocket連接請(qǐng)求和http請(qǐng)求發(fā)到同一個(gè)實(shí)例

解決方案一

整個(gè)導(dǎo)出功能使用websocket進(jìn)行通信,不使用http請(qǐng)求,所有請(qǐng)求參數(shù)通過(guò)websocket傳輸。
注意事項(xiàng)
websocket消息有長(zhǎng)度限制,需要修改org.apache.tomcat.websocket.textBufferSize

@Configuration
public class WebAppRootContext implements ServletContextInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        servletContext.addListener(WebAppRootListener.class);
        // websocket消息有長(zhǎng)度限制,默認(rèn)限制是8k個(gè)字符,這里改成1024k
        servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize", String.valueOf(1024 * 1024));
    }
}

解決方案二

使用nginx配置iphash實(shí)現(xiàn)會(huì)話保持

解決方案三

使用kafka解決websocket多實(shí)例問(wèn)題(實(shí)例指部署后端服務(wù)的容器實(shí)例)
1.每個(gè)實(shí)例維護(hù)一個(gè)ConcurrentHashMap,保存連接到該實(shí)例的userId和session
2.每個(gè)實(shí)例把要發(fā)給客戶端的消息(導(dǎo)出進(jìn)度),通過(guò)kafka廣播模式發(fā)給所有kafka消費(fèi)者
3.消費(fèi)者收到消息后,從消息中提取出userId,判斷該userId對(duì)應(yīng)的session是否存在于本實(shí)例,若存在,則把消息通過(guò)websocket發(fā)到前端,若不存在,直接舍棄消息(因?yàn)閟ession在其他實(shí)例)


使用nginx代理websocket的注意事項(xiàng)

1.客戶端與服務(wù)端之間有nginx時(shí),nginx是七層負(fù)載均衡,即在應(yīng)用層代理真實(shí)客戶端轉(zhuǎn)發(fā)http請(qǐng)求,并代理真實(shí)服務(wù)端把響應(yīng)結(jié)果返回給客戶端,nginx與真實(shí)服務(wù)端的讀超時(shí)時(shí)間限制默認(rèn)是60s,也就是超過(guò)60s會(huì)斷開(kāi)websocket長(zhǎng)連接,需要把該時(shí)間限制改大一點(diǎn)

#改成10分鐘
proxy_read_timeout 600s

2.需要配置把http協(xié)議升級(jí)為ws協(xié)議
Error during WebSocket handshake: Unexpected response code: 404


websocket進(jìn)度條準(zhǔn)確性控制

技術(shù)方案

在執(zhí)行具體業(yè)務(wù)邏輯之前,就計(jì)算好任務(wù)量,假設(shè)一次導(dǎo)出請(qǐng)求,包含ABCDE五個(gè)步驟,那就把任務(wù)量定為5,每執(zhí)行完一個(gè)步驟,就把進(jìn)度增加20%,全都執(zhí)行完畢后,進(jìn)度為100%。

問(wèn)題1

假如五個(gè)步驟的耗時(shí)不同怎么辦,比如ABCDE的耗時(shí)比例為6:1:1:1:1

解決方案

根據(jù)耗時(shí)比例,加權(quán)計(jì)算任務(wù)量,比如把任務(wù)量定為10,其中A占6個(gè)任務(wù)量,完成A后進(jìn)度增加60%

問(wèn)題2

假設(shè)A步驟耗時(shí)1分鐘,那么用戶看到進(jìn)度條1分鐘不動(dòng),然后突然升到60%,體驗(yàn)會(huì)非常差。

解決方案

需要把A步驟繼續(xù)拆分,比如,根據(jù)時(shí)間區(qū)間等入?yún)ⅲ琧ount查詢可以確定A步驟需要查1w條數(shù)據(jù),需要分頁(yè)查詢10次,那么可以把A步驟分成10個(gè)子任務(wù),每個(gè)任務(wù)執(zhí)行完成后,進(jìn)度增加60% / 10 = 6%

問(wèn)題3

問(wèn)題2的解決方案中,A步驟具體需要怎么拆分,很難在一開(kāi)始確定,需要執(zhí)行到具體的代碼段(比如count查詢)才能確定

解決方案(分治算法)

一開(kāi)始計(jì)算任務(wù)量的時(shí)候,還是分配6個(gè)任務(wù)量給A,至于A怎么分配這6個(gè)任務(wù)量,由A執(zhí)行到具體代碼段的時(shí)候再?zèng)Q定。比如執(zhí)行count查詢后發(fā)現(xiàn)需要分頁(yè)查10次,那么每一次分頁(yè)查詢后進(jìn)度增量為60% / 10 = 6%

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 最近由于項(xiàng)目需求,項(xiàng)目中需要實(shí)現(xiàn)一個(gè)WebSSH連接終端的功能,由于自己第一次做這類型功能,所以首先上了Gi...
    ObjectSpace閱讀 1,104評(píng)論 0 3
  • 前言 之前一個(gè)項(xiàng)目中九風(fēng)開(kāi)發(fā)app的用戶的消息部分,由于項(xiàng)目比較緊,而且之前沒(méi)有接觸過(guò)WebSocket開(kāi)發(fā),所以...
    九風(fēng)萍舟閱讀 164,119評(píng)論 24 97
  • 通過(guò)fastdfs-java-client的api按塊下載文件,下載成功后寫入到輸出流并將進(jìn)度按用戶通過(guò)webso...
    Ericxujun閱讀 1,849評(píng)論 0 0
  • 前言 之前一個(gè)項(xiàng)目中九風(fēng)開(kāi)發(fā)app的用戶的消息部分,由于項(xiàng)目比較緊,而且之前沒(méi)有接觸過(guò)WebSocket開(kāi)發(fā),所以...
    kobe_lp閱讀 758評(píng)論 0 1
  • 基于Pomelo開(kāi)發(fā)的聊天室應(yīng)用天生是多進(jìn)程的,因此可以非常容易地?cái)U(kuò)展服務(wù)器類型和數(shù)量。對(duì)于多頻道聊天室,在Pom...
    JunChow520閱讀 1,444評(píng)論 0 1

友情鏈接更多精彩內(nèi)容