淺談雲巴實時通信的編程模型

来源:http://www.cnblogs.com/yunba/archive/2016/09/28/5915759.html
-Advertisement-
Play Games

有人常問,雲巴實時通信系統到底提供了一種怎樣的服務,與其他提供推送或 IM 服務的廠商有何本質區別。其實,從技術角度分析,雲巴與其它同類廠商都是面向開發者的通信服務,巨集觀的編程模型都是大同小異,真正差異則聚焦於產品定位,業務模式,基礎技術水平等諸多細節上。本文暫不討論具體產品形態上的差異,著重從技術... ...


概要

有人常問,雲巴實時通信系統到底提供了一種怎樣的服務,與其他提供推送或 IM 服務的廠商有何本質區別。其實,從技術角度分析,雲巴與其它同類廠商都是面向開發者的通信服務,巨集觀的編程模型都是大同小異,真正差異則聚焦於產品定位,業務模式,基礎技術水平等諸多細節上。本文暫不討論具體產品形態上的差異,著重從技術角度淺談實時通信的編程模型。

什麼是實時通信

「實時」(realtime) 一詞在語義層面上隱含著對時間的約束(real-time constraint),在工程上,我們習慣對「需要在一定時間內」 完成的操作稱為「實時操作」。通常,實時可細分為 「軟實時」(soft realtime),「準實時」(firm realtime)和 「硬實時」(hard realtime)。它們之間的差異,簡單來說,就是對無法在指定時間區間內(deadline)完成事務的容忍程度。維基百科上對這三者有如下解釋

  • Hard – missing a deadline is a total system failure.
  • Firm – infrequent deadline misses are tolerable, but may degrade the system's quality of service. The usefulness of a result is zero after its deadline.
  • Soft – the usefulness of a result degrades after its deadline, thereby degrading the system's quality of service.

假如我們把無法按時完成任務(missing a deadline)稱為異常事件,那麼硬實時系統無法容忍異常事件;準實時系統則可容忍極少量的異常事件,但超過一定數量後系統可用性為 0;軟實時系統可容忍異常事件,但是每發生一次異常事件,系統可用性降低。

綜上所述,我們可以舉例:

  • 火星上的無人探測器是硬實時系統,因為一次異常事件就極有可能導致探測器不可用,同理可類推核電站的監控系統,軍用無人機系統,遠程導彈的導航系統等一系列軍工產品;

  • 金融交易系統是準實時系統,此類系統可容忍極少數的交易故障,一旦故障次數增加,系統就會陷入崩潰狀態;

  • 簡訊 / 手機推送 / 電商購物等都是軟實時系統。對於此類系統,用戶都可以容忍異常事件,但是太多的異常事件則會大幅降低系統可用程度,用戶體驗急劇下滑。

就目前來說,絕大多數互聯網產品(甚至可以說是 100%)都是軟實時系統。雲巴實時通信系統的目標則是要做一個高可用的軟實時系統

一個最簡單的實時通信編程模型

在軟體工程中,很多複雜的項目其實都可以用一個非常簡潔的模型來概括。正如愛因斯坦所說的:「一切都應該儘可能地簡單,但不要太簡單」(Everything should be made as simple as possible, but not simpler)。雖然這是描述物理世界的經驗之談,但同樣適用於電腦領域,將物理世界的關係投射到某種人為語言(物理公式/電腦編程語言),其規律其實都是共通的。

讓我們假設這麼一個簡單的場景:對 10 個客戶端發送一條消息

這個需求其實可以用偽碼表示為:

for (i..10) {
    send_message(get_socket(i))
}

如果下圖所示:

在這個簡單的需求下,我們只需要讓這 10 個客戶端分別跟伺服器建立 TCP 連接(本文暫時只討論 TCP 協議),然後遍歷地發送消息即可。顯而易見,這是一個 O(N) 複雜度的邏輯。

基於這個簡單的模型,我們可以認為一條消息從發出到接收,有以下幾個延時:

  • 網路延遲 ,一般是一個較為穩定的值,比如從北京到深圳,ping 延遲大約為 40 ms 左右;

  • 系統處理延遲,較之網路延遲,該值變化幅度較大,且可能因處理請求數的增加而急劇增大;

雲巴實時通信系統以 200 ms 延遲作為總延遲標準,也就是說,假如網路鏈路是從北京到深圳,除去網路延遲的 40 ms,要想達到 200 ms 的通信時間,系統延遲必須小於 160 ms。

可以想象,當客戶端數量達到一定數量級(比如百萬級別)時,以上系統模型的實時性將面臨極其嚴峻的考驗。

分而治之

在海量用戶下保持穩定的實時性,其實很多時候就只有一個手段:分而治之

圖 1 表示的是單機處理情況。當單機的處理能力,帶寬都無法應對客戶端數量急劇增加的時候,我們就必須將線路進行分割。而且圖 1 只體現了推送的意圖(單向),但通信往往是一個雙向的概念,綜上,我們將 圖 1 改成下麵的 圖 2

pic

這樣每台機器就可以處理符合其當前水位的連接。

在現實開發中,我們可能不僅僅滿足於一個如此簡單的消息系統,我們可能想要有離線消息,數據統計,數據緩存,限流等一系列操作,所以我們還可以再優化一下架構:

  • 將整體架構劃分成業務邏輯層和數據存儲層;

  • 數據存儲層又可以根據存儲數據類型的不同來進一步劃分;

  • 前端可以單獨劃分一個網路接入層;

  • 數據包的流向可以用 MQ 來串聯;

這樣我們可以得到以下的圖 3:

在這個模型中,網路接入層和消息業務邏輯層整體上應該是一個 stateless 的模塊,可以較為輕鬆地做橫行擴展。存儲層作為一個有狀態的模塊,想要做到橫行擴展是一件很不容易的事情。如果撇開這點來看,至此,這個模型理論上在應對海量用戶的場景下應該是有效的。

通信協議和技術棧的選擇

做一個消息系統,不可避免地要涉及到對通信協議的選擇。我們在對通信協議的選擇上,遵循以下幾個原則:

  • 協議儘可能精簡輕量,因為在系統設計之初我們就考慮了對物聯網的支持,省電,節約流量都是目標之一;

  • 通用性好,擴展性強,方便後期做特性開發;

  • 協議在業界被廣泛認可,且儘可能多的有不同語言的開源實現,以方便不同技術棧的客戶做集成;

綜上,我們沒有重新自定義一份通信協議,而是選擇了基於長連接的 MQTT。從很多角度來看,MQTT 非常適合做消息匯流排的通信協議,而且協議棧也足夠輕巧和易於實現。雲巴實時消息系統傳輸的消息體積較小(一般小於 4 KB),比如控制信號,普通聊天信息等。就這點上,針對物聯網設計的 MQTT 有著天然的優勢。後面,在不斷地研究中我們又發現,MQTT 其實不僅僅適用於物聯網場景,在很多要求低延遲高穩定性的非物聯網場景也同樣適用(比如手機端 app 推送,IM,直播彈幕等)。

從前面幾個章節我們看到,雲巴消息系統是一個典型的 IO 密集型系統。在出於開發效率和穩定的考慮下,我們選了 Erlang/OTP 作為主力開發語言。Erlang/OTP 作為一門小眾開發語言(無論是國內還是國際),在應付這類 IO 密集型系統上,有著得天獨厚的優勢(可參考 RabbitMQ 這個基於 Erlang/OTP 的著名開源項目):

  • 基於 actor 的進程創建模型,可以為每個數據包創建一個 Erlang 處理進程,充分利用多核;

  • OTP 的開發框架抽象了分散式開發的許多細節,使得開發者在很小的心智負擔下就能輕鬆快速地開發出功能原型;

  • Erlang/OTP 充分運用了容錯思想,應對異常不是防,而是容,很多時候我們寫出一些安全邏輯上有漏洞的代碼,在 Erlang/OTP 上居然也能工作得好好的;

隨著不斷深入地使用 Erlang/OTP, 其性能問題也漸漸凸顯出來。我們發現,當客戶端請求量增加的時候,用 Erlang/OTP 寫出的模塊輕而易舉地就可以將 CPU 跑滿,從而讓當前實例超負荷運轉。很多時候出於成本上的考量,我們無法選擇更多核數的機器來提升 Erlang 虛擬機運行的性能(此點未明確驗證過),所以只好選擇適度增加服務處理實例來緩解壓力。

不過,通過對業務模塊更細粒度的劃分,我們可以將一些核心的小模塊用 C/C++ 語言改寫,在一定範圍的複雜度內,可以有效提升整體處理性能。這也是我們接下來優化核心系統的思路之一。

MQTT 的 Pub/Sub 模型與高可用 KV 存儲

MQTT 協議採用的是 Pub/Sub 的編程模型。其中有三個比較關鍵的動作:publishsubscribe 和 unsubsribe。通過前面幾個章節的討論,我們又可以得到這麼一個場景:

假如存在一個訂閱量巨大的 topic(百萬級),如何在單次 publish 中保證實時性 ?

其實,解決思路跟之前的場景是一致的:分而治之。我們必須通過某種策略對 topic 進行分片,然後將分片分發到不同的 publish 模塊上進行處理。在一定的演算法複雜度下,這個問題理論上是可以被有效解決的。於是,topic 的分片策略就成了高性能 publish 的關鍵。其實,如果想採用 MQTT 做海量消息系統,訂閱關係的管理一定是無法繞開的大問題。它主要有以下幾個設計難點:

  • 如果採用 KV 方式存儲,如何設計數據結構 ?同上,我們要怎樣去設計一種高效的 topic 分片存儲策略;

  • 訂閱關係的管理是 MQTT 消息系統的核心模塊,假如這個存儲模塊失效,就必定會導致消息通信失敗,從而讓客戶端收不到消息,這就必須要求這個模塊一定是高可用的,也就意味著我們必須構建一個高可用的 KV 存儲集群,該集群要能容忍一定程度的節點失效;

  • 冷熱 topic 要有淘汰機制,要有一定策略將不活躍的 topic 定期淘汰到磁碟以節約記憶體容量;

  • KV 存儲集群要能高效地動態擴容;

在很長一段時間的實踐中,我們採用過好幾種 KV 存儲的集群方案,踩了不少坑,最後還是決定自己造輪子來開發一個高可用的 KV 存儲模塊。不過這又是一個很大的話題,我們將在後續博客中具體闡述我們的做法。

缺陷與不足

在團隊發展初期,由於人力和時間等種種因素,我們把業務邏輯模塊開發成了一個巨大的單體架構應用。在團隊規模較小的情況下,單體架構的應用確實較好維護和開發,但隨著新人的加入,單體架構則嚴重製約著特性開發和性能優化。從架構層面上來看,合理地劃分更細粒度的模塊,在性能和可維護性上採用微服務(microservice)設計模式,成了我們未來優化系統的方向之一。

總結

軟體工程上有「沒有銀彈」(No Silver Bullet)這條金科玉律,用戶選擇雲服務商亦是如此,絕對沒有完美的第三方雲服務商,每一家都可能存在明顯的優點和缺陷。用戶必須從自己應用場景和痛點出發,選擇合適的後端服務。雲巴將會在自己產品的核心競爭力上持續發力,精打細磨,吸取行業內的高效實踐經驗,打造出更加優秀的高可用實時通信系統。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 系統:Windows 7 64位系統 安裝之前,首先下載軟體: Nginx: http://nginx.org/en/download.html PHP Stable PHP 5.6.26: http://php.net/downloads.php mysql: http://dev.mysql.c ...
  • (-1)寫在前面 這兩天讀《javaweb開發王者歸來》,學到Spring的PropertyPlaceholderConfigurer時出現一個問題,我已${jdbc.name}的形式賦值給bean中的屬性,用main方法測試後,輸出屬性的值仍然是${jdbc.name}。 (0)解決問題之路 a. ...
  • 感謝原作者:Vamei 出處:http://www.cnblogs.com/vamei 怎麼能快速地掌握Python?這是和朋友閑聊時談起的問題。 Python包含的內容很多,加上各種標準庫、拓展庫,亂花漸欲迷人眼。我一直希望寫一個快速的、容易上手的Python教程,而且言語簡潔,循序漸進,讓沒有背 ...
  • 感謝原作者:Vamei 出處:http://www.cnblogs.com/vamei Java是面向對象語言。這門語言其實相當年輕,於1995年才出現,由Sun公司出品。James Gosling領導了Java的項目小組。該項目的最初只想為家電設計一門容易移植的語言。然而,在獲得了Netscape ...
  • tail命令也是一個非常常用的文件查看類的命令,今天就為大家介紹下Linux tail命令的用法。 更多Linux命令詳情請看:Linux命令速查手冊 Linux tail命令主要用來從指定點開始將文件寫到標準輸出。很多人喜歡使用tail –f 來監控日誌文件。 一、Linux tail命令格式 L ...
  • 1.說明:Idea 下,項目對應於 Eclipse 下的 workspace,Module 對應於 Eclipse 下的項目。Idea 下,新添加的項目既可以單獨作為一個 Project,也可以作為一個 Project 下的 Module。 2.本篇文章介紹內容: (1)如何在 Project 新建 ...
  • 初學swift 但是網上只有很多swift用xib創建的cell,就算是有也不是我想要的。今天自己弄了一個不用xib純代碼寫的,來上代碼 自定義cell 下麵是controller 例外說一點懶載入 OC的懶載入 @property (nonatomic, strong) NSMutableArra ...
  • 裡面主要包含了一些與字元串關聯的函數的聲明,這些函數有如下的命名規則: 以"mem"開頭的函數操作任意的字元序列 以"strn"開頭的函數操作非空字元序列 以"str"開頭的函數操作空字元結尾的字元序列 數據類型 size_t 巨集 NULL 函數 複製 memcpy() memmove() strc ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...