主頁 > 企業開發 > 實時訊息推送整理

實時訊息推送整理

2020-10-16 13:24:18 企業開發

分不清輪詢、長輪詢?不知道什么時候該用websocket還是SSE,看這篇就夠了,

所謂的“實時推送”,從表面意思上來看是,客戶端訂閱的內容在發生改變時,服務器能夠實時地通知客戶端,進而客戶端進行相應地反應,客戶端不需要主觀地發送請求去獲取自己關心的內容,而是由服務器端進行“推送”,

注意上面的推送二字打了引號,這就意味著在現有的幾種實作方式中,并不是服務器端主動地推送,而是通過一定的手段營造了一種實時的假象,就目前現有的幾種技術而言,主要有以下幾類:

  • 客戶端輪詢:傳統意義上的輪詢(Short Polling)
  • 服務器端輪詢:長輪詢(Long Polling)
  • 全雙工通信:Websocket
  • 單向服務器推送:Server-Sent Events(SSE)

文中會以一個簡易聊天室的例子來分別通過上述的四種方式實作,代碼地址mini-chatroom(存在些許bug,主要是為了做演示用)

overview

輪詢(Short Polling)

輪詢的實作原理:客戶端向服務器端發送一個請求,服務器回傳資料,然后客戶端根據服務器端回傳的資料進行處理;然后客戶端繼續向服務器端發送請求,繼續重復以上的步驟,如果不想給服務器端太大的壓力,一般情況下會設定一個請求的時間間隔,

shortPolling

使用輪詢明顯的優點是基礎不需要額外的開發成本,請求資料,決議資料,作出回應,僅此而已,然后不斷重復,缺點也顯而易見:

  • 不斷的發送和關閉請求,對服務器的壓力會比較大,因為本身開啟Http連接就是一件比較耗資源的事情
  • 輪詢的時間間隔不好控制,如果要求的實時性比較高,顯然使用短輪詢會有明顯的短板,如果設定interval的間隔過長,會導致訊息延遲,而如果太短,會對服務器產生壓力

代碼實作

var ShortPollingNotification = {
  datasInterval: null,
  subscribe: function() {
    this.datasInterval = setInterval(function() {
      Request.getDatas().then(function(res) {
        window.ChatroomDOM.renderData(res);
      });
    }, TIMEOUT);
    return this.unsubscribe;
  },
  unsubscribe: function() {
    this.datasInterval && clearInterval(this.datasInterval);
  }
}

shortPolling

下面是對應的請求,注意左下角的請求數量一直在變化

shortNetwork

在上圖中,每隔1s就會發送一個請求,看起來效果還不錯,但是如果將timeout的值設定成5s,效果將大打折扣,如圖:

shortPolling5s

長輪詢(Long Polling)

長輪詢的基本原理:客戶端發送一個請求,服務器會hold住這個請求,直到監聽的內容有改變,才會回傳資料,斷開連接,客戶端繼續發送請求,重復以上步驟,或者在一定的時間內,請求還得不到回傳,就會因為超時自動斷開連接,

longPolling

長輪詢是基于輪詢上的改進版本,主要是減少了客戶端發起Http連接的開銷,改成了在服務器端主動地去判斷所關心的內容是否變化,所以其實輪詢的本質并沒有多大變化,變化的點在于:

  • 對于內容變化的輪詢由客戶端改成了服務器端(客戶端會在連接中斷之后,會再次發送請求,對比短輪詢來說,大大減少了發起連接的次數)
  • 客戶端只會在資料改變時去作相應的改變,對比短輪詢來說,并不是全盤接收

代碼實作

// 客戶端
var LongPollingNotification = {
    // ....
    subscribe: function() {
      var that = this;

      // 設定超時時間
      Request.getV2Datas(this.getKey(),{ timeout: 10000 }).then(function(res) {
        var data = https://www.cnblogs.com/rynxiao/archive/2020/10/16/res.data;
        window.ChatroomDOM.renderData(res);
        // 成功獲取資料后會再次發送請求
        that.subscribe();
      }).catch(function (error) {
        // timeout 之后也會再次發送請求
        that.subscribe();
      });
      return this.unsubscribe;
    }

    // ....
}

筆者采用的是express,默認不支持hold住請求,因此用了一個express-longpoll的庫來實作,

下面是一個原生不用庫的實作(這里只是介紹原理),整體的思路是:如果服務器端支持hold住請求的話,那么在一定的時間內會自輪詢,然后期間通過比較key值,判斷是否回傳新資料

  • 客戶端第一次會帶一個空的key值,這次會立即回傳,獲取新內容,服務器端將計算出的contentKey回傳給客戶端
  • 然后客戶端發送第二次請求,帶上第一次回傳的contentKey作為key值,然后進行下一輪的比較
  • 如果兩次的key值相同,就會hold請求,進行內部輪詢,如果期間有新內容或者客戶端timeout,就會斷開連接
  • 重復以上步驟
// 服務器端

router.get('/v2/datas', function(req, res) {
  const key = _.get(req.query, 'key', '');
  let contentKey = chatRoom.getContentKey();

  while (key === contentKey) {
    sleep.sleep(5);
    contentKey = chatRoom.getContentKey();
  }

  const connectors = chatRoom.getConnectors();
  const messages = chatRoom.getMessages();
  res.json({
    code: 200,
    data: { connectors: connectors, messages: messages, key: contentKey },
  });
});

以下是用 express-longpoll 的實作片段

// mini-chatroom/public/javascripts/server/longPolling.js

function pushDataToClient(key, longpoll) {
  var contentKey = chatRoom.getContentKey();

  if (key !== contentKey) {
    var connectors = chatRoom.getConnectors();
    var messages = chatRoom.getMessages();

    longpoll.publish(
      '/v2/datas',
      {
        code: 200,
        data: {connectors: connectors, messages: messages, key: contentKey},
      }
    );
  }
}

longpoll.create("/v2/datas", function(req, res, next) {
  key = _.get(req.query, 'key', '');
  pushDataToClient(key, longpoll);
  next();
});

intervalId = setInterval(function() {
  pushDataToClient(key, longpoll);
}, LONG_POLLING_TIMEOUT);

為了方便演示,我將客戶端發起請求的timeout改成了4s,注意觀察下面的截圖:

longPollingNetwork

可以看到,斷開連接的兩種方式,要么是超時,要么是請求有資料回傳,

基于iframe的長輪詢模式

這種模式的具體的原理為:

  • 在頁面中嵌入一個iframe,地址指向輪詢的服務器地址,然后在父頁面中放置一個執行函式,比如execute(data)
  • 當服務器有內容改變時,會向iframe發送一個腳本<script>parent.execute(JSON.stringify(data))</script>
  • 通過發送的腳本,主動執行父頁面中的方法,達到推送的效果

具體可以參看這里

Websocket

The WebSocket Protocol enables two-way communication between a client running untrusted code in a controlled environment to a remote host that has opted-in to communications from that code.

The protocol consists of an opening handshake followed by basic message framing, layered over TCP.

The goal of this technology is to provide a mechanism for browser-based applications that need two-way communication with servers that does not rely on opening multiple HTTP connections (e.g., using XMLHttpRequest or iframe and long polling).

The WebSocket Protocol attempts to address the goals of existing bidirectional HTTP technologies in the context of the existing HTTP infrastructure; as such, it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries, even if this implies some complexity specific to the current environment.

特征

  • websocket是雙向通信的,設計的目的主要是為了減少傳統輪詢時http連接數量的開銷
  • 建立在TCP協議之上,握手階段采用 HTTP 協議,因此握手時不容易屏蔽,能通過各種 HTTP 代理服務器
  • 與HTTP兼容性良好,同樣可以使用80和443埠
  • 沒有同源限制,客戶端可以與任意服務器通信
  • 可以發送文本,也可以發送二進制資料,
  • 協議識別符號是ws(如果加密,則為wss),服務器網址就是 URL

websocket

關于Websocket API方面的知識,這里不再作講解,可以自己查閱Websocket API MDN

兼容性

websocket兼容性良好,基本支持所有現代瀏覽器

websocket1

代碼實作

筆者這里采用的是socket.io,是基于websocket的封裝,提供了客戶端以及服務器端的支持

// 客戶端
var WebsocketNotification = {
  // ...
  subscribe: function(args) {
    var connector = args[1];
    this.socket = io();

    this.socket.emit('register', connector);

    this.socket.on('register done', function() {
      window.ChatroomDOM.renderAfterRegister();
    });

    this.socket.on('data', function(res) {
      window.ChatroomDOM.renderData(res);
    });

    this.socket.on('disconnect', function() {
      window.ChatroomDOM.renderAfterLogout();
    });
  }
  // ...
}

// 服務器端
var io = socketIo(httpServer);

io.on('connection', (socket) => {
  socket.on('register', function(connector) {
    chatRoom.onConnect(connector);

    io.emit('register done');

    var data = https://www.cnblogs.com/rynxiao/archive/2020/10/16/chatRoom.getDatas();
    io.emit('data', { data });
  });

  socket.on('chat', function(message) {
    chatRoom.receive(message);

    var data = https://www.cnblogs.com/rynxiao/archive/2020/10/16/chatRoom.getDatas();
    io.emit('data', { data });
  });
});

回應格式如下:

websocket-request-response

Server-Sent Events(SSE)

傳統意義上服務器端不會主動推送給客戶端訊息,一般都是客戶端主動去請求服務器端獲取最新的資料,SSE就是一種可以主動從服務端推送訊息的技術,

SSE的本質其實就是一個HTTP的長連接,只不過它給客戶端發送的不是一次性的資料包,而是一個stream流,格式為text/event-stream,所以客戶端不會關閉連接,會一直等著服務器發過來的新的資料流,視頻播放就是這樣的例子,

  • SSE 使用 HTTP 協議,現有的服務器軟體都支持,WebSocket 是一個獨立協議,
  • SSE 屬于輕量級,使用簡單;WebSocket 協議相對復雜,
  • SSE 默認支持斷線重連,WebSocket 需要自己實作,
  • SSE 一般只用來傳送文本,二進制資料需要編碼后傳送,WebSocket 默認支持傳送二進制資料,
  • SSE 支持自定義發送的訊息型別,

基本的使用方法,參看SSE API

sse

兼容性

目前除了IE以及低版本的瀏覽器不支持,基本支持絕大多數的現代瀏覽器,

sse2

代碼實作

// 客戶端
var SSENotification = {
  source: null,
  subscribe: function() {
    if ('EventSource' in window) {
      this.source = new EventSource('/sse');

      this.source.addEventListener('message', function(res) {
        const d = res.data;
        window.ChatroomDOM.renderData(JSON.parse(d));
      });
    }
    return this.unsubscribe;
  },
  unsubscribe: function () {
    this.source && this.source.close();
  }
}

// 服務器端
router.get('/sse', function(req, res) {
  const connectors = chatRoom.getConnectors();
  const messages = chatRoom.getMessages();
  const response = { code: 200, data: { connectors: connectors, messages: messages } };

  res.writeHead(200, {
    "Content-Type":"text/event-stream",
    "Cache-Control":"no-cache",
    "Connection":"keep-alive",
    "Access-Control-Allow-Origin": '*',
  });

  res.write("retry: 10000\n");
  res.write("data: " + JSON.stringify(response) + "\n\n");

  var unsubscribe = Event.subscribe(function() {
    const connectors = chatRoom.getConnectors();
    const messages = chatRoom.getMessages();
    const response = { code: 200, data: { connectors: connectors, messages: messages } };
    res.write("data: " + JSON.stringify(response) + "\n\n");
  });

  req.connection.addListener("close", function () {
    unsubscribe();
  }, false);
});

下面是控制臺的情況,注意觀察回應型別

sse-type

詳情中注意查看請求型別,以及EventStream訊息型別

sse3

總結

  • 短輪詢、長輪詢實作成本相對比較簡單,適用于一些實時性要求不高的訊息推送,在實時性要求高的場景下,會存在延遲以及會給服務器帶來更大的壓力
  • websocket目前而言實作成本相對較低,適合于雙工通信,對于多人在線,要求實時性較高的專案比較實用
  • SSE只能是服務器端推送訊息,因此對于不需要雙向通信的專案比較適用

參考連接

  • The WebSocket Protocol
  • Websocket API MDN
  • Server-sent events MDN
  • WebSocket 教程
  • Server-Sent Events 教程
  • webSocket(二) 短輪詢、長輪詢、Websocket、sse

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/174086.html

標籤:其他

上一篇:postman使用

下一篇:postman使用

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more