<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 基于websocket的實時單頁應用開發框架 ## Introduction ### Background 目前絕大部分web應用的都是基于HTTP協議通尊,及由客戶端主動發起請求的方式進行數據的交互,這種方式在基本上能滿足大部分網站的功能需求,是非常完善的web應用解決方案。隨著網絡的普及和快速發展,數據的變化變得越來越快,用戶的數據的實時性要求也越來越高,在對實時性要求較高的web應用上,基于HTTP通訊的方式就不太實用,因為HTTP請求只能從客戶端主動發出,服務端的有新的數據的時候,客戶端并不能第一時間獲取得到。為了解決這一問題,基于HTTP的解決方案主要有輪詢、長輪詢和Iframe流。這種三種解決方案缺點就是會浪費很多帶寬資源和服務器資源。 HTML5標準中定義了新的通訊方式:WebSocket,該通訊協議目的解決客戶端和服務端之間的雙向通訊問題,而且在主流瀏覽器中得到了廣泛的支持。目前websocket在web上的應用主要有實時聊天、實時監控、游戲等。事實證明是一個非常可靠的技術。 單頁應用是現在web開發的一個主流解決方案,它可以在一個頁面中完成傳統web應用中多個頁面才能完成的復雜功能,而且性能更好,大大提升了用戶體驗。如果結合WebSocket技術,可以構建出的實時單頁web應用,能夠進一步提升效率,適應更多復雜的數據需求。 ### Motivation 現在WebSocket的應用還不是非常廣泛,一般在特定實時場景下才會在web應用中部分采用,相對HTTP請求,WebSocket模式有很大不同,在過于復雜的場景下,會增加開發難度,而且也缺少相對成型的解決方案和參考資料。 如果在不增加太多開發復雜度的情況下全站采用websocket進行通訊,并結合單頁應用的特點,能夠使web的體驗會提高很多。在這種單頁應用+websocket模式下,一個頁面就完成所有功能,前后端只需要建立一個websocket鏈接就可以完成所有的數據交互。 所以我打算開發出一個單頁應用+WebSocket通訊的web系統開發框架,讓開發者可以快速開發出功能復雜的、高效的實時web應用,提供一個此類應用的解決方案和開發模式。 ### Current Methods ##### 基于HTTP的實時通訊方案 **輪詢** 客戶端在每隔一段較短的時間里發送一次新的HTTP請求,服務器無論是否有新的數據的時候都會立即返回結果。這種方式在服務端沒有新的數據的時候會產生很多無效的請求,從而浪費帶寬和服務器資源。 **長輪詢** 客戶端發起的請求,服務端在沒有新的數據下不會立即返回,而是掛載狀態,直到有新的數據的時候再響應這次請求,或者掛載時間達到服務器超時時間限制時候,立即返回,告訴客戶端繼續發送下一個請求,如此循環。這種方案相對“輪詢”方案可以減少HTTP請求次數,但是服務端在掛載請求的時候,依然需要消耗額外的資源,而且難以管理維護。 **iframe流** 該方案是在頁面中插入一個隱藏的iframe,其src屬性是一個長鏈接[?]請求,服務端可以不斷的傳入數據,客戶端通過JavaScript進行處理,從而獲取到持續更新的數據。這種方案的缺點依然是需要花費服務端額外的資源去維護長連接。 ![長連接](../images/長連接.jpg) ##### 多頁面應用 多頁面應用是將一個web應用根據功能劃分成多個html頁面,每個頁面完成部分功能,用戶需要在多個頁面之間跳轉完成工作。每次頁面跳轉需要重新請求html文件以及相關的css和JavaScript文件,然后進行渲染。這是目前最常見的web應用架構模式,其缺點是頁面之間跳轉會因為需要重新下載資源和重新渲染缺乏連貫性,影響用戶瀏覽體驗,而且會增加額外的HTTP請求和頁面再次渲染的資源消耗,多個頁面之間的數據也難以在瀏覽器端實現共享,需要借助服務的去維護,從而增加前后端開發的耦合度。優點是開發簡單,利于搜索引擎檢索。 ### Contributions 我所我打算實現一個基于websocket通訊的單頁應用Web開發框架,方便開發者簡單高效的開發出高性能的、實時的web應用。我們框架的主要特點有: 1. 快速上手:不會引入太多復雜的概念,便于開發者快速從HTTP開發模式中過渡到新框架。 2. 簡單:降低websocket的開發復雜度。 3. 雙向通訊:前后端可以方便主動推送數據,而不需要關心websocket的工作原理。 4. 高效:數據交互速度更快,更加節約資源 5. 低耦合:能夠方便的開發出適合更多場景的web應用,甚至是app應用。 ## Related Work ### WebSocket 絕大部分的web應用都是基于HTTP協議的,他簡單可靠,經過多年發展,現在非常完善,但是由于其無連接的特點,所以每次只能處理一個請求,處理結束之后便斷開,而且服務端無法主動發起請求,只能被動的接受請求。為了解決這個問題,于是IETF創建了WebSocket通訊協議,其主要特點是在單個TCP鏈接上進行全雙工通訊,使得客戶端和服務端都能夠互相主動的推送數據。WebSocket建立鏈接的時候首先采用HTTP協議進行協商升級協議,后續數據傳輸再通過WebSocket協議去實現。建立WebSocket鏈接的過程如圖所示。 ![](../images/ws-connect.png) 成功建立鏈接后客戶端和服務端就可以隨時進行雙向數據通信,后續每次通信都不需要攜帶完整頭部信息,直接傳輸數據文本,更加節省帶寬資源,而且還支持拓展子協議。 ### React React是用于構建用戶界面的開源JavaScript庫,誕生于FaceBook公司的工程師。React將數據和html封裝成一個一個的組件,從而構成完整的頁面,然后通過更改組件的state數據,使對應的html結構自動實現插入、刪除、更改等操作,開發者不需要關注DOM的操作,react能夠O(n)的時間復雜度內準確的實現復雜的dom更新,再結合瀏覽器的history api便能開發出功能復雜的web應用,用戶也因此不需要頻繁的切換頁面便可完成復雜的業務需求。React的核心是虛擬DOM技術和Diff算法。 ##### 組件化 React將html代碼封裝成獨立的UI組件,組件之間可以相互嵌套構成復雜的組件,最終構成整個頁面,可以簡單理解成強化版的html標簽。如下代碼是一個結合jsx語法的組件實現,該組件是繼承React.Conponent的類,其render方法返回的是要渲染的html標簽,這種類型的組件可以獲取到父組件傳遞的數據,也可以維護自己的數據,同時擁有生命周期事件和對應的方法。組件化讓UI更加靈活,易于維護。 ```js class HelloMessage extends React.Component { render() { return ( <div> Hello {this.props.name} </div> ); } } ``` ##### 虛擬DOM React在數據更新的時候,根據更新后的狀態用JavaScript構建DOM樹,然后跟上次DOM樹進行對比,找到兩棵DOM樹不同的地方,最后在瀏覽器真實DOM上對不同的地方進行更新,這樣能夠以最小改動去更新真實DOM結構,從而提高頁面的性能。 ##### Diff算法 DOM樹的比對雖然是在內存中進行,但是比對操作會在頻繁的觸發,所以依然需要保證其高效性。直接找出兩棵樹的不同處的時間復雜度是O(n^3)。React在比對過程中,從上到下逐層比較,同一層級的節點比較時,如果是同一類型的組件,繼續進行分層比較,如果組件類型不同,直接替換整個節點及子節點。這樣只需要遍歷一次樹,就可以完成DOM樹的比對,將算法復雜度降低到O(n)。 ### Redux Redux是一個開源的應用狀態管理JavaScript庫,提供可預測化的狀態管理,Redux的思想是將視圖和數據進行分離,從而實現前端的MVC模式開發[?],能夠使程序更加直觀、低耦合。Redux可以配合多種UI庫使用,在配合React使用的時候,其前端結構圖如下圖所示。 ![react-redux](../images/react-redux.png) 我們的框架采用redux作為數據管理器,在任何時候都可以方便的對組件的狀態進行同步,讓數據可以預測和維護。 ## Our framework ### Architecture 本框架分為客戶端部分和服務端部分。我們框架包含兩種消息通訊機制,分別是立即消息和訂閱消息,前者是用websocket實現類似HTTP請求的一對一消息,后者基本實現思想是發布和訂閱模式,客戶端自動通知服務端訂閱內容,服務端在有數據更新的時候通知訂閱的用戶。 客戶端是基于react和redux構建的單頁應用,由攜帶數據的action觸發store的更新,store更新觸發視圖的自動重新渲染,用戶在視圖上傳發起action,從而形成一個閉環。我們框架在對react的組件進一步包裝,使其在掛載和銷毀的時候自動通過WebSocket通知服務端更新用戶和redux的事件之間的訂閱關系。同時服務端在WebSocket基礎上實現了類似HTTP請求的立即消息機制,使得客戶端可以發起能得到立即回復的請求,服務端有對應路由匹配和控制器處理請求,并可以處理服務的主動發布的邏輯,即根據訂閱器里的用戶訂閱事件進行消息推送,消息數據內容為redux action 對象,被訂閱的組件能夠準確的收到消息并自動觸發事件,從而更新store,最終自動更新視圖。系統架構下圖所示。 ![e](../images/system.png) ##### 服務端架構 服務端主要由消息解析器、路由、控制器、訂閱器和鏈接池組成。WebSocket接受到消息后會經過消息解析器中的請求中間件層層處理,然后構造成一個請求對象,交給相匹配的路由處理,路由會調用相關的控制器對當前請求進行業務邏輯處理,例如進行數據庫操作等,處理完后,控制器可以根據訂閱器中的用戶訂閱情況和連接池中的活躍用戶構造返回內容,返回的內容依然會經過消息解析器中的返回中間件層層處理,最后通過WebSocket發送給用戶。架構圖圖所示。 ![e](../images/server.png) 我們框架中提到的路由不是傳統框架中的路由,但是實現的是相同的功能,路由的作用是對HTTP請求的路徑進行匹配,然后進行響應,事實證明這種方式能夠有效的給每一個HTTP請求添加唯一標識。我們框架也借鑒這種方式,對客戶端的每一條消息添加一個頭部對象,該對象就包含url屬性,服務端根據url建立路由機制,是框架能夠處理多樣的websocket消息。 結合中間件和路由就可以完成多種業務需求,為了實現自動推送新消息功能,我們用訂閱器實現用戶以及其訂閱關系,框架內置一個值為/subsribe的路由,該路由接受body為redux事件名的請求,并添加當前用戶與訂閱事件到訂閱器中,與之對應的/unsubscribe路由則是刪除訂閱器中當前用戶對該事件的訂閱。開發者可以創建任何需要的路由以及控制器實現需要的功能,例如登錄、獲取文章等需要立即返回的數據請求,客戶端會通過請求的唯一標示來保證同一次請求與返回。 ##### 客戶端架構 客戶端主要包括三個部分,視圖、狀態管理器和WebSocket消息處理器。我們框架的前端部分分兩個子框架去實現,分別是jayce和jayce-dom,這樣能夠將視圖層與數據處理層分離,以實現與多種視圖框架配合使用,例如vue和angular等。首先我們框架通過傳入redux的store對象和配置信息生成一個全局唯一的Jayce實例,該實例包含WebSocket執行方法和redux事件執行方法,實例化Jayce實例后,就會建立WebSocket鏈接,對于需要服務端主動推送新數據的內容,開發者在編寫react組件的時候,可以調用jayce-dom的jayceSubscribe方法將任意組件包裝成訂閱組件,該組件在生命周期內會通知服務端當前用戶訂閱redux事件,在組件銷毀的時候也請求服務端當前用戶取消redux事件的訂閱。對于立即消息,用戶可以調用Jayce實例的send方法發送請求到服務端,其回調方法里會得到服務端返回的數據,編寫上跟AJAX請求無異。前端架構如圖所示。 ![e](../images/jayce.png) ### 消息協議 除了瀏覽器初次渲染需要的文件通過HTTP請求外,我們讓后續的所有的數據交互都通過WebSocket實現,而不同的類型的消息會有不同的處理邏輯,為了保證不同類型的消息能夠被正確處理以及系統的可拓展性,我們在WebSocket的消息基礎上建立了一個簡單的協議。WebSocket傳輸的數據內容是字符串文本,在進行消息傳遞前,都需要經過message parser進行解析。發送端封構造json格式的請求對象,然后轉換成json字符串交給WebSocket傳輸,接收端再解析成json對象,由于客戶端和服務端都是基于JavaScript開發,所以json能夠直接被處理。每個消息對象包含兩個屬性,header 和 body。body為數據內容,header有兩個固有屬性url和type,前者是路由標識,告訴服務端用哪個路由處理器處理,后者是請求類型,框架內置三種類型:IMMEDIATELY、SUBSCRIBE、UNSUBSCRIBE,分別是立即型消息、訂閱型消息、取消訂閱型消息。除此外,應用也可以根據需要添加其他header信息,從而實現更靈活的業務。 ### 自動訂閱組件 React通過編寫組件的方式去構建瀏覽器DOM結構,每個組件都有自己的生命周期方法,我們框架在React組件的基礎上進行了封裝,封裝通過`jayceSubscribe()`方法實現。該方法接受兩個參數,第一個參數是需要訂閱的redux事件數組,第二參數是實例化的Jayce對象,調用`jayceSubscribe()`方法后返回的是一個匿名方法,接受一個react 組件為參數,返回包裝后的Jayce 組件。調用示例: ```js export default jayceSubscribe(['GET_NEW_ARTICLE'], jayce)(Article); ``` 其中Article為需要訂閱的組件,最終導出的是經過Jayce包裝的組件。 Jayce包裝組件的過程: 1. 創建一個新組件 2. 在componentWillMount的生命周期事件里執行請求訂閱方法 3. componentWillUnmount的生命周期事件里執行取消訂閱請求方法 4. 添加需要被包裝的組件作為子組件 5. 返回這個新組件 ### 訂閱器 為了實現服務端能夠準確、主動的推送實時性數據,所以服務端需要維護一個用戶的store訂閱器。訂閱器保存的是客戶端的redux事件與用戶的對用關系,數據結構為一個JavaScript對象,屬性名為事件名稱,值為訂閱該事件的用戶連接對象數組,框架內置的/subsribe和/unsubscribe路由及相關控制器實現了對訂閱器的自動管理,對開發者而言無需關心訂閱器內容,只需根據業務訂閱和發布事件,對應的用戶都能夠自動獲取到數據和觸發redux事件。 ### Message Parse Message Parser是服務端和客戶端可靠通信的樞紐,WebSocket只是建立服務端和客戶端之間的全雙工通信的渠道,所以我們在需要一個方便,可拓展的消息解析功能,使這些文本消息能夠被框架正確識別和處理。通過上文約定的消息協議,在服務端,Message Parser會在服務啟動的時候注冊系統級和用戶級中間件,接收到消息的時候Message Parser會根據消息內容構造json格式的請求對象,然后交個每個'request'型的中間件處理,最后到達路由處理器,服務端返回消息到客戶端的時候,返回的消息對象依然會經過消息處理器中的'response'中間件處理,最后處理成為符合消息協議規定的文本內容交給WebSocket發送給客戶端。 ### middleware 中間件是框架的一個重要組成部分。每一個消息都會流經每一個中間件,這樣創建合理的中間件能夠完成各種業務需求,例如身份認證、統計分析、消息攔截等。一個中間就是一個方法,接受兩個參數,第一個請求對象,第二個是執行下一個中間件的方法,當中間件被注冊到框架后,該中間件方法接受到的分別是上一個中間件處理完后的請求對象和調用下一個中間件的方法。如下是框架內置的構建請求對象的中間件示例,它是請求階段第一個中間件。 ```js function requestBodyParse(ctx, next) { let req = JSON.parse(ctx.message); if(req.header && req.body){ ctx.req = req; next(); } else { ctx.req = { header: { url: '/error' }, body: '' } return; } } module.exports = requestBodyParse ``` 該中間件將接受到的消息字符根據消息協議規定格式串轉換成對象,處理完后調用next()方法,執行下一個中間件。如果轉換失敗,則交給框架內置的/error路由處理。然后在框架實例對象上調用use方法注冊到消息解析器中。 ### Process 在實際使用中,主要存在三個服務端與客戶端的交互方式,分別是客戶端發送立即得到返回的消息、客戶端發送訂閱redux和取消訂閱事件的消息、服務端發送執行redux事件的消息。 ##### 立即請求返回流程 一個web應用中會有大部分操作是立即返回操作,需要馬上得到結果。例如提交表單、打開文章詳情頁面等。這種消息需要保證請求與返回一一對應,即每一次客戶端發送消息都會有服務端唯一應答,跟HTTP請求的特點一樣。為了實現這一特性,客戶端每次發出消息都會在請求對象頭部添加唯一標識,并將其添加到請求等待隊列,服務端處理請求后返回攜帶這個標識的消息對象,客戶端得到返回后會去請求對待隊列中查找并執行相關回調方法。流程簡化后如圖所示。 ![e](../images/immediately.png) ##### 自動訂閱流程 自定訂閱消息是組件自動完成的,組件會在相關的生命周期方法里發送消息通知服務端訂閱和取消訂閱redux事件,然后服務端在訂閱器中更新該用戶對redux 事件的訂閱關系。 ![e](../images/subscribe.png) 訂閱流程本質上跟立即請求消息相同,只是對應的服務端的路由控制器一般不需要返回消息,直接去更新服務器訂閱器。 ##### 服務端主動推送流程 觸發服務端主動推送操作可以來自多種行為,例如其他用戶提交數據操作、服務端定時任務等。其流程如圖所示。 ![e](../images/server-push.png) 只要能獲取到框架實例對象,就可以隨時在需要的邏輯中調用該實例觸發廣播、多播、單播操作。 ## Experimental results 【待測試】 相對于HTTP模式的web應用,我們框架的主要優勢在實現客戶端和服務端的全雙工通訊和更小的數據傳輸量上。因此我們通過三個實驗對比HTTP服務器和我們框架服務器的性能。以下實驗的服務器配置都是1核心處理器、2G運行內存,網絡環境為公網。 ### 實現全雙工通訊功能服務器所需要的資源對比 ### 相同數量用戶并發下服務器內存消耗對比 ### 相同請求內容下數據傳輸量對比 ## Conclusions 這篇文章介紹了一個基于WebSocket的單頁應用開發框架,在實時性要求越來越高和單頁應用成為主流開發方式的環境下,我們框架為開發者提供了一個滿足這兩種需求的解決方案和實現,能夠讓開發者快速高效的開發出實時單頁web應用,大大提高產品使用體驗,而且對于HTTP這種數據獲取方式,我們框架也同樣支持,從而滿足多種功能需求。實驗證明,我們框架能有效降低帶寬、服務器等資源消耗,從而降低運維成本。為了適應快速的技術的變化,我們框架盡可能降低各模塊的耦合度,在客戶端用戶可以重構組件包裝器就可以配合不同的視圖層框架使用,例如vue和angular等,在服務端也可以將訂閱器用redis等數據庫實現。 我們框架用websocket替代了HTTP協議的web應用通訊方式,雖然底層變化較大,但是對開發者而言跟傳統web應用開發模式變化不大,保持路由、控制器、請求、返回、連接池等相關概念和功能,學習簡單但是功能強大。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看