国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

動靜分離的數(shù)據(jù)并發(fā)加載策略

MrZONT / 3005人閱讀

摘要:關(guān)于動靜分離的描述,這里推薦一篇不錯的博文網(wǎng)站靜態(tài)化處理動靜分離策略。這里的解決辦法則是采用的屬性,將其應(yīng)用于數(shù)據(jù)請求相關(guān)的上,就可以達(dá)到腳本與數(shù)據(jù)并發(fā)加載的效果。

作者:莫冠釗

轉(zhuǎn)載請注明出處,保留原文鏈接和作者信息


前言

當(dāng)今許多大型網(wǎng)頁應(yīng)用尤其是SPA均采用了動靜分離的策略。關(guān)于動靜分離的描述,這里推薦一篇不錯的博文 網(wǎng)站靜態(tài)化處理—動靜分離策略。

本人是做前端的,之前有幸與一位對性能追求極致的后端同學(xué)一起開發(fā)這種動靜分離的web項(xiàng)目,以下將從傳統(tǒng)順序模式單路數(shù)據(jù)并發(fā)模式(以下簡稱單并發(fā)模式)多路數(shù)據(jù)并發(fā)模式(以下簡稱多路并發(fā)模式)來談?wù)勛约簩@類應(yīng)用關(guān)于前端加載方面的心得。本文中的例子均來自該項(xiàng)目中。

1. 傳統(tǒng)順序模式

一般情況下,瀏覽器首先會接收到一張靜態(tài)的頁面,這張頁面會包含樣式文件和腳本文件引用的標(biāo)簽(圖片什么的不在這里討論)。至于數(shù)據(jù)哪里來,下面介紹兩種方式:

腳本請求獲取
通常,在腳本加載完畢后,腳本會執(zhí)行一段向服務(wù)端發(fā)送請求數(shù)據(jù)的代碼,然后通過回調(diào)函數(shù)取出數(shù)據(jù)并做初始化工作。這一個過程為:請求頁面 => 渲染頁面 => 加載腳本 => 請求數(shù)據(jù) => 數(shù)據(jù)與腳本一起初始化 => 初始化完畢,也就是從加載應(yīng)用到啟動應(yīng)用是以順序任務(wù)的形式執(zhí)行。

直接填充于隱藏標(biāo)簽中
服務(wù)端也可以直接將數(shù)據(jù)填充到網(wǎng)頁中的一個隱藏標(biāo)簽中再傳回給客戶端,也就是上面順序中把獲取數(shù)據(jù)放在頁面請求之前。之后在腳本中直接去獲取相應(yīng)的DOM中的內(nèi)容也就是數(shù)據(jù),來進(jìn)行初始化工作。

這兩種方法各有優(yōu)劣,因?yàn)椴皇潜疚闹攸c(diǎn),在此就直接帶過。不過筆者更傾向于前者。

1.1 工作流圖

如果用工作流的思想去理解,大概可以為下圖(第一種方式):

1.2 結(jié)果分析

在這里我們只研究數(shù)據(jù)以及main.js的加載情況。

base64.css是用來存儲一些小圖片的base64字符串并且是允許延后加載,可以將其歸為圖片資源一類。

總體情況還是可以接受的,畢竟后端同學(xué)對緩存這一塊下了很大的功夫,用戶會在500ms左右看到頁面的內(nèi)容,到了600ms之后程序就可以正式啟動。

這種模式的優(yōu)點(diǎn)是顯而易見的,這種順序加載啟動模式易用性、可維護(hù)性都比較好,也能很好地發(fā)揮動靜分離的特長。
然而,我們認(rèn)為,如果將上圖中數(shù)據(jù)的請求放在前面和腳本一起并發(fā)請求,也許會減少整個頁面的加載和啟動所需時間,而且后端同學(xué)還覺得這樣的加載效果會更加直觀整齊……

于是便有了下面的研究。

2. 單并發(fā)模式

要實(shí)現(xiàn)數(shù)據(jù)與腳本并發(fā)加載,最核心的就是要讓數(shù)據(jù)不依賴于腳本進(jìn)行加載,筆者所能想到的有兩種:

在頭部添加一個script,插入一段發(fā)送ajax請求的代碼,向服務(wù)端發(fā)送數(shù)據(jù)請求。

同樣是添加一個script,將其src設(shè)為數(shù)據(jù)請求的url來引用外部數(shù)據(jù)資源。

單從執(zhí)行效率來說,1比2還多了一步,故本文中選擇2進(jìn)行討論。

2.1難點(diǎn)與解決方案 如何保證script標(biāo)簽進(jìn)行外部下載時不阻塞其他資源的下載?

把script在head標(biāo)簽內(nèi)。在下載script引入的外部腳本時,瀏覽器處于阻塞狀態(tài),網(wǎng)絡(luò)不好或者script文件過大時,頁面處于空白停頓狀態(tài),這樣的體驗(yàn)是很不好的。

我們一般會將腳本文件放在頁面底部來降低腳本下載與運(yùn)行所帶來的阻塞影響,而且這樣可以保證腳本中所引用的頁面元素已經(jīng)渲染完畢。

而數(shù)據(jù)請求是與頁面元素?zé)o關(guān),在這里我們希望它能放在頭部確保可以盡早地開始加載來達(dá)到與其它資源一起請求,但又不阻塞其他資源的下載。

瀏覽器對標(biāo)記有async屬性的scripts會立即加載并解析,該script相對于頁面的其余部分異步地執(zhí)行(當(dāng)頁面繼續(xù)進(jìn)行解析時,腳本將被執(zhí)行)。

這里的解決辦法則是采用HTML5async屬性,將其應(yīng)用于數(shù)據(jù)請求相關(guān)的script上,就可以達(dá)到腳本與數(shù)據(jù)并發(fā)加載的效果。如下代碼:

script(src="/Table/Data" type="text/javascript" async="async")
在數(shù)據(jù)與腳本加載的順序未知的情況下,如何保證正確的頁面啟動?

javascript是一門解析性語言,當(dāng)它加載完畢之后就會執(zhí)行。

此時的數(shù)據(jù)請求變成了一個script標(biāo)簽,也就是說,它可以變成一段與賦值相關(guān)的javascript代碼,直接把得到的結(jié)果放在公共環(huán)境中。如果不把它變成賦值代碼,基于上面的引言,可能得到的數(shù)據(jù)就會變成環(huán)境中的一個匿名對象而在之后無法再次被訪問。這樣一來,在腳本記載完畢就可以直接去引用這個結(jié)果進(jìn)行啟動頁面。那么問題來了……

基于上面async中闡述的方案,在實(shí)際中更多時候我們可能無法100%保證數(shù)據(jù)與腳本加載的先后順序。資源大小的確一定程度決定了加載時間,但是網(wǎng)絡(luò)傳輸也有著許多不穩(wěn)定的因素。

我們也不可能直接在任何一個script中直接引用對方的資源(如果未加載完畢,會返回undefined的錯誤)。

不到萬不得已,不應(yīng)該使用輪詢檢查的方法去解決并發(fā)問題,這樣的應(yīng)用性能太低,和我們的初衷相違背。

既然它們是相互依賴的關(guān)系,而且我們只需要其中一方引用另一方的資源即可完成我們所需要的啟動。在這里,我們只需要讓先加載完成前的把資源暴露到公共環(huán)境window中,讓后加載的那一方察覺到之后直接引用進(jìn)行啟動即可。

對于數(shù)據(jù)與腳本,我們把它們的資源分別定為:

名稱 資源 描述
數(shù)據(jù) allData(Object) 存儲所有的動態(tài)數(shù)據(jù)
腳本 mainInitByData(Function) 主引導(dǎo)函數(shù)

在數(shù)據(jù)請求里,代碼為:

var allData = window.allData = "{"name":"data"}";

//檢查腳本的資源是否存在
if (typeof window.mainInitByData !== "undefined") {
    mainInitByData(JSON.parse(allData));
};

腳本里相關(guān)的片段則為:

var mainInitByData = window.mainInitByData = function(data) {
    //TODO...
}

if (typeof window.allData !== "undefined") {
    mainInitByData(JSON.parse(allData));
}
2.2 工作流圖

2.3 結(jié)果分析

不難發(fā)現(xiàn),經(jīng)過并行化處理之后,加載頁面的效率相比于之前的順序模式大大增加了。且頁面程序也能順利啟動(這里大家可以自行嘗試)。

不料后端同學(xué)在一兩個月后,又提出了希望作多路數(shù)據(jù)并發(fā)請求,因?yàn)閯討B(tài)數(shù)據(jù)中也有部分?jǐn)?shù)據(jù)相對一段時間內(nèi)為靜態(tài)的,這部分?jǐn)?shù)據(jù)可以用緩存處理,其他數(shù)據(jù)則直接從其它服務(wù)器中獲取,可以進(jìn)一步提高并發(fā)效率。事情變得越來越有趣,也有了下面的研究。

3. 多路并發(fā)模式 3.1 “繼承”單并發(fā)

此時,假設(shè)我們所需請求的數(shù)據(jù)共有三條A、B、C,其中A為相對靜態(tài)數(shù)據(jù),可以做出以下定義:

名稱 資源 描述
子數(shù)據(jù)A AData(Object) 存儲A的相對靜態(tài)數(shù)據(jù)
子數(shù)據(jù)B BData(Object) 存儲B的動態(tài)數(shù)據(jù)
子數(shù)據(jù)C CData(Object) 存儲C的動態(tài)數(shù)據(jù)
腳本 mainInitByData(Function) 主引導(dǎo)函數(shù)

如果繼續(xù)沿用單并發(fā)中的策略,腳本的相關(guān)片段代碼則為:

var mainInitByData = window.mainInitByData = function(dataA, dataB, dataC) {
    //TODO...
}

if (typeof window.dataA !== "undefined" && window.dataB !== "undefined" && window.dataC !== "undefined") {
    var dataA = JSON.parse(dataA),
        dataB = JSON.parse(dataB),
        dataC = JSON.parse(dataC);
    mainInitByData(dataA, dataB, dataC);
}

以上數(shù)據(jù)只是一個例子,并不代表這樣就可以解決這類的問題。假如有一天后端突然要求一次并發(fā)加載10條數(shù)據(jù),代碼就會變得十分冗余。

既然要處理并發(fā),那么單并發(fā)的思想是可以沿用的,只是這里的方向不對。

不妨我們換個角度思考,腳本仍然和數(shù)據(jù)進(jìn)行互相檢查,但是這個數(shù)據(jù)包含了所有子數(shù)據(jù),在這里我直接將其稱為父數(shù)據(jù)。那子數(shù)據(jù)之間怎么辦?

3.2 以信號量的思想處理數(shù)據(jù)整合

之所以說是信號量的思想而不是信號量,因?yàn)樾盘柫勘旧硎?strong>多線程多任務(wù)同步,而對于帶有async標(biāo)簽里的javascript是單線程異步,但不代表javascript不能利用信號量的思想,信號量的思想就是在解決處理并發(fā)問題。具體的信號量定義,請讀者自行查閱。

為了更好的描述這個借用思想的過程,先做以下定義:

父數(shù)據(jù)與子數(shù)據(jù)之間共用一種信號量,子數(shù)據(jù)運(yùn)用這種信號量進(jìn)行數(shù)據(jù)的整合,而父數(shù)據(jù)應(yīng)用這種信號量進(jìn)行與腳本初始化啟動。

每次子數(shù)據(jù)加載完畢后,釋放信號量,并把自己的數(shù)據(jù)整合到父數(shù)據(jù)中。

假設(shè)子數(shù)據(jù)之間申請信號量的順序未知,但必定在父數(shù)據(jù)之前。

整合的數(shù)據(jù)以及信號量都放在一個js對象integrateData中,分別命名為datasem(其值為1-子數(shù)據(jù)數(shù)量),即integrateData = {data: {}, sem: -2}

這里可能需要對子數(shù)據(jù)的格式做一定的調(diào)整。變成以下類型,方便做整合

{"message":"success", "data": {....}}

那么對于所有子數(shù)據(jù)的處理代碼為:

var result = "JSON";
var integrateData = window.integrateData || (window.integrateData = { data: {}, sem: 1 - 3 });
var onDataCallback = window.onDataCallback || (window.onDataCallback = function(result_, integrateData) {
    function dataIsReady(integrateData) {
        return integrateData.sem > 0;
    }

    function dataReadyCallback(integrateData) {
        integrateData.sem--;
        //父數(shù)據(jù)與腳本啟動
        var mainInitBydata = window.mainInitBydata;
        if (typeof mainInitBydata === "function") {
            mainInitBydata(integrateData);
        }
        integrateData.sem++;
    }

    if (dataIsReady(integrateData)) {
        alert("非法請求");
        return;
    }
    var result = result_;
    if (typeof result_ === "string") {
        result = JSON.parse(result_);
    }

    //數(shù)據(jù)整合
    if (result.message === "success") {
        var data = result.data;
        for (var key in data) {
            integrateData.data[key] = data[key];
        }
    }
    //釋放信號量
    integrateData.sem++;
    //檢查信號量
    if (dataIsReady(integrateData)) {
        dataReadyCallback(integrateData);
    }
});
onDataCallback(result, integrateData);

此時,腳本里的相關(guān)代碼則為:

var mainInitByData = window.mainInitByData = function(integrateData) {
    //TODO...
}

var integrateData = window.integrateData;

//這里無需擔(dān)心沖突問題,因?yàn)閖s是單線程執(zhí)行,子數(shù)據(jù)整合完畢后會直接執(zhí)行父數(shù)據(jù)檢查腳本資源的行為,所以sem>0時,父數(shù)據(jù)處于就緒狀態(tài)。
if (integrateData && integrateData.sem > 0) {
    mainInitBydata(integrateData)
}
3.3 工作流圖

3.4 結(jié)果分析

其實(shí)效率相比單并發(fā)提高不多,主要是涉及的動態(tài)數(shù)據(jù)規(guī)模不大,而且每次發(fā)送的請求報文和響應(yīng)報文都會有一定大小的報頭,造成不必要的開銷。但假如動態(tài)數(shù)據(jù)足夠大的話,這種策略是可以起到很大的作用。同時,單并發(fā)模式中的雙向檢查也可以用信號量的思想實(shí)現(xiàn)。

總結(jié)

總結(jié)以上的模式,我們可以得出以下的結(jié)論:

模式 效率 易用性 性能主要影響因素 適用場景
順序 普通 容易 數(shù)據(jù)與程序的大小總和 一般的小項(xiàng)目
單并發(fā) 比順序模式高 普通 數(shù)據(jù)與程序大小比例 大多數(shù)動靜分離的網(wǎng)站應(yīng)用
多路并發(fā) 一般比單并發(fā)高,當(dāng)數(shù)據(jù)太小時效率會比單并發(fā)低 復(fù)雜 劃分?jǐn)?shù)據(jù)的比例 數(shù)據(jù)比較龐大的網(wǎng)站應(yīng)用,尤其是數(shù)據(jù)之間按相對均勻的比例歸類

除此以外,上述中,單并發(fā)與多路并發(fā)的一大缺陷就是代碼的耦合性會相對地提高,對于多路并發(fā)而言,如果子數(shù)據(jù)請求之間有依賴關(guān)系,可能還要定義多種不同的信號量,不利于管理。

利用現(xiàn)有的工具比如EventProxy,可以很好管理這些并發(fā)請求,包括任務(wù)之間的依賴關(guān)系。通過事件訂閱觸發(fā)的形式可以讓程序更好地知道當(dāng)前所完成的任務(wù)以觸發(fā)相應(yīng)的回調(diào)函數(shù)進(jìn)行處理。

希望本文可以給讀者帶來一定的幫助。

最后打個小廣告,歡迎follow我的github:https://github.com/zero-mo

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/61797.html

相關(guān)文章

  • 動靜分離數(shù)據(jù)并發(fā)加載策略

    摘要:關(guān)于動靜分離的描述,這里推薦一篇不錯的博文網(wǎng)站靜態(tài)化處理動靜分離策略。這里的解決辦法則是采用的屬性,將其應(yīng)用于數(shù)據(jù)請求相關(guān)的上,就可以達(dá)到腳本與數(shù)據(jù)并發(fā)加載的效果。 作者:莫冠釗 轉(zhuǎn)載請注明出處,保留原文鏈接和作者信息 前言 當(dāng)今許多大型網(wǎng)頁應(yīng)用尤其是SPA均采用了動靜分離的策略。關(guān)于動靜分離的描述,這里推薦一篇不錯的博文 網(wǎng)站靜態(tài)化處理—動靜分離策略。 本人是做前端的,之前有幸與一...

    Object 評論0 收藏0
  • 動靜內(nèi)容混合站點(diǎn),怎樣用全站加速支持實(shí)際業(yè)務(wù)場景?

    摘要:為了優(yōu)化動靜混合站點(diǎn)和純動態(tài)站點(diǎn)的加速效果,阿里云推出了全站加速方案,通過智能區(qū)分動靜態(tài)請求,實(shí)現(xiàn)整站加速效果的全面提升。 摘要: 伴隨著近幾年O2O的爆發(fā),網(wǎng)絡(luò)已經(jīng)不僅是傳統(tǒng)的展示企業(yè)品牌的渠道,而逐漸演變成為嫁接企業(yè)和用戶之間服務(wù)和交流的橋梁,我們開始賦予網(wǎng)絡(luò)更多的功能,比如購物、出行、學(xué)習(xí)、娛樂等等。 同時,網(wǎng)絡(luò)內(nèi)容形態(tài)的進(jìn)階發(fā)展,網(wǎng)頁內(nèi)容已經(jīng)從靜態(tài)的圖片、文字向短視頻、直播演變...

    sherlock221 評論0 收藏0
  • 微服務(wù)接入層設(shè)計與動靜資源隔離

    摘要:接入層作用一的聚合。接入層作用二服務(wù)發(fā)現(xiàn)與動態(tài)負(fù)載均衡既然統(tǒng)一的入口變?yōu)榱私尤雽樱瑒t接入層就有責(zé)任自動的發(fā)現(xiàn)后端拆分,聚合,擴(kuò)容,縮容的服務(wù)集群,當(dāng)后端服務(wù)有所變化的時候,能夠?qū)崿F(xiàn)健康檢查和動態(tài)的負(fù)載均衡。 此文已由作者劉超授權(quán)網(wǎng)易云社區(qū)發(fā)布。 歡迎訪問網(wǎng)易云社區(qū),了解更多網(wǎng)易技術(shù)產(chǎn)品運(yùn)營經(jīng)驗(yàn)。 這個系列是微服務(wù)高并發(fā)設(shè)計,所以我們先從最外層的接入層入手,看都有什么樣的策略保證高并發(fā)。...

    jindong 評論0 收藏0
  • nginx反向代理、動靜分離、負(fù)載均衡

    摘要:反向代理要說反向代理,我們就先要理解正向代理下面我們就談?wù)務(wù)虼砗头聪虼戆伞?蛻舳瞬拍苁褂谜虼怼7聪虼砜偨Y(jié)就一句話代理端代理的是服務(wù)端。因此,動態(tài)資源轉(zhuǎn)發(fā)到服務(wù)器我們就使用到了前面講到的反向代理了。 反向代理 要說反向代理,我們就先要理解正向代理 ,下面我們就談?wù)務(wù)虼砗头聪虼戆伞?正向代理 一個位于客戶端和原始服務(wù)器(origin server)之間的服務(wù)器,為了從原始...

    DTeam 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<