使用Go構建實時消息系統

2020-12-25 百家號

我將談論我大約6年前開始的開源項目。該項目名為Centrifugo。這是一個實時消息伺服器。它最初是用Python編寫的(在Tornado框架中),但隨後遷移到Go語言。就在大約一周前,我發布了Centrifugo的第2版 - 所以這實際上是第一次公布新伺服器。我在這次演講中的所有觀點都與這個新版本有關。

什麼是實時消息?當我說實時消息時,我指的是你向應用程式客戶端發送的消息,作為對應用程式中發生的某些事件的反應。這種消息的重要特性是它幾乎立即傳遞給客戶端 - 在後端發送之後的幾毫秒內。這種消息在多人遊戲,聊天室,動態計數器,實時圖表等實踐中非常有用。

實時消息的即時性質要求它通過持久連接從伺服器推送到客戶端。當客戶定期詢問伺服器更新時的輪詢策略在這裡不是很好的選擇。

動機

如果你決定在應用程式中添加實時事件,那麼你實際上有很多選擇。在選擇實時解決方案時,重要的是要考慮許多因素:後端語言和前端語言。你是從頭開始項目還是已經有生產應用程式。你願意為實時解決方案付錢嗎?當然,你需要解決的任務的性質。

有許多實時消息解決方案。如下是一部分:

如果您使用Erlang,NodeJS或Go等異步並發語言製作後端,那麼即使沒有任何框架,你也可以很好 - 儘管你需要解決前端和後端方面的一些特定問題。像可擴展性,正確的連接管理一樣。

來自pusher.com的Phil Leggetter創造了一個描述現代實時技術的精彩資源。checkout - 大量優秀的伺服器,庫和服務。深度博客中也有一個很好的概述。如果你正在開始搜索實時解決方案 - 從那裡開始。

我個人的背景是Python,更具體的是Django框架。如你所知,Django是一個經典的工作線程框架,其中每個工作程序在其自己的進程和OS線程中運行並阻塞執行請求的時間。現在使用像Websocket這樣的持久連接,你很快就會耗盡可用的工作人員,因此你的主應用程式將停止接受新的請求。這就是Centrifugo最初創建的目的 - 處理持久連接,從而為後端提供服務一般短期請求的可能性,並在需要時使用Centrifugo API向客戶發布新消息。

Django並不孤單 - 在Python和其他語言中有許多類似的模型框架。由於Centrifugo作為單獨的服務工作,因此可以與其集成,而無需在應用程式代碼中引入許多更改。實際上沒有什麼能阻止使用Centrifugo和用NodeJS或Go語言編寫的應用程式,因為它有一些我很快描述的有用功能。

設計概述

簡而言之,Centrifugo是一個伺服器,它允許處理來自應用程式客戶端的持久連接,並提供API以將實時消息發布給對該消息感興趣的連接客戶端。客戶表示他們對訂閱頻道(或者換句話說主題)的特定消息感興趣。所以Centrifugo實際上只是一個PUB / SUB伺服器。

現在我們來看看Centrifugo與應用後端集成的簡化方案:

正如你所看到的,該方案涉及3個部分 - 你的應用程式後端,Centrifugo和你的應用程式用戶。用戶通過Websocket或SockJS連接到Centrifugo,使用JSON Web令牌進行身份驗證,訂閱頻道並收聽消息。只要你的後端有新事件,它就會將其發布到Centrifugo API,並且消息將傳遞給連接的客戶端。在Centrifugo中,你可以在body或GRPC中使用帶有JSON的簡單HTTP請求來調用API方法。

讓我們假設你正在製作實時評論平臺。一旦你的用戶創建新評論,你首先使用方便的方式將其發送到後端 - 例如在瀏覽器中的AJAX請求,在你的後端,你驗證評論,如果需要保存到資料庫中,然後發布到Centrifugo API以通道與此評論相關的評論將由Centrifugo向所有活躍訂閱者播出。

實際上,這意味著實時消息在此設計中以一種方式流動 - 從伺服器到客戶端。在大多數情況下,你不需要雙向實時通信。大多數現代應用程式都是面向讀取的 - 用戶主要讀取內容,寫入請求是一件非常罕見的事情 - 因此對於許多實時應用程式,我們可以輕鬆地使用非持久連接進行寫入。

讓我們來看看Centrifugo的高級功能:

語言無關與應用程式後端分離具有到期支持的JWT身份驗證與現有應用程式的簡單集成使用Redis水平縮放性能穩定短暫斷開連接後的消息恢復有關頻道中活躍訂閱者的信息跨平臺(Linux,MacOS,Windows)MIT許可證Centrifugo用於許多項目,主要用於Web應用程式。它在Python和PHP社區中非常流行。一些使用Centrifugo的公司是Mail.Ru,Badoo,sports.ru,Spot.im。Spot.im擁有我所聽說過的最大的Centrifugo安裝 - 它是在線的30萬客戶,每分鐘有300萬條消息。

實時傳輸

我們來談談Centrifugo中使用的實時傳輸。

Websocket是目前最明顯的選擇。它具有很大的優勢 - 無處不在 - 在Web瀏覽器和行動應用程式內部。它是TCP上的低開銷協議,可以在與HTTP相同的埠上運行。連接從通用HTTP上的升級機制開始,然後切換到TCP會話。我不會談論很多關於Websocket協議的詳細信息 - 當然它很熟悉,因為它現在已經廣為流傳。

我們記得那些由於瀏覽器支持不佳而幾乎不可能在Web應用程式中使用Websocket的日子。

但現在情況好一些。

caniuse.com/#search=websocket目前的瀏覽器支持率約為93%。一些客戶端仍然使用沒有websocket支持的瀏覽器,一些瀏覽器擴展可以阻止特定域的websocket流量,因此在某些情況下,如果我們希望所有用戶成功連接,我們仍然需要回退到HTTP傳輸。

為了解決這個問題,Centrifugo使用SockJS作為Websocket polyfill。這意味著當無法建立Websocket連接時,將使用其中一種替代傳輸方式。

這些傳輸中只有2種基於持久連接 - xhr-streaming和eventsourse。但由於它們基於HTTP,因此它們不能是雙向的,因此SockJS使用從客戶端到伺服器的單獨的短期HTTP請求來模擬雙向通信。

具有後備HTTP傳輸可以在Web瀏覽器環境中提供一個有趣的優勢。我們知道,我們生活在HTTP/2越來越受歡迎的時代。如果HTTP/2使用的持久HTTP連接將通過HTTP/2實現自動在一個真實的TCP會話中復用。在打開Websocket應用程式的新選項卡時,你建立與伺服器的新TCP連接。這可以通過LocalStorage或SharedWorker上的同步來解決,但HTTP/2隻是為基於HTTP的傳輸連接提供了開箱即用的多路復用。

這是Centrifugo動機和一般概念的簡短概述。現在讓我們更具體地介紹一下實施細節 - Centrifugo如何在裡面構建?

內幕

如上所述,其中有兩個:

Websocket基於Gorilla websocket庫SockJS基於Sockjs-Go庫當我在第2版上工作時,我還嘗試使用GRPC雙向流作為客戶端伺服器傳輸。但經過一些測量後,我發現與Websocket相比,GRPC雙向流沒有任何優勢。例如,如果我們將10k客戶端連接到一個Centrifugo節點,那麼在websocket情況下,伺服器上的內存消耗將約為500mb,而在GRPC情況下,它將是4倍大 - 大約2GB的RAM。在我的同步測試中,Websocket的CPU使用率只提高了3倍。

Centrifugo有自己的協議,在protobuf模式中描述 - 它在高級別上與JSON RPC非常相似。在業務邏輯方面,與MQTT也有一些相似之處。有兩種可用的序列化格式:JSON和Protobuf。

我已經提到Centrifugo可以擴展到許多節點。為了允許功能跨多個節點工作,我們有一個Engine實體。這實際上是與大量方法的接口。引擎允許:

將消息發布到通道訂戶連接的Centrifugo節點。因此,每個客戶端都可以連接到每個節點並接收可能發布到另一個節點API的消息提供一種方法,使用配置的大小和生命周期將發布保存到緩存中。當客戶端在短時間內丟失連接然後重新連接時,這用於錯過消息恢復過程。管理存在信息 - 即關於頻道中活動用戶的信息。目前有2個引擎實現 - 在Memory和Redis引擎中。

內存引擎允許運行一個Centrifugo節點,因為沒有機制以某種方式連接節點。Redis Engine允許將Centrifugo擴展到許多將通過Redis PUB / SUB連接的節點,並且還具有內置的分片支持。

消息傳遞模型

在簡單的情況下,交付模型最多一次。無法保證將傳遞消息。顯然,基於PUB/SUB模型和Redis PUB/SUB機制的Centrifugo涉及 - 無法想像另一種保證。但這對於大多數應用程式來說已經足夠了,因為Centrifugo具有消息傳輸的作用 - 所有應用程式狀態都存在於主應用程式資料庫中,因此可以在需要時恢復。

但Centrifugo提供了一種有趣的消息恢復機制。Centrifugo在緩存中保留可配置的消息緩衝區,並且可以在客戶端重新訂閱後短暫斷開連接後發送錯過的消息後自動恢復客戶端狀態。當通道中每個發布的此機制具有增量序列號時,客戶端可以記住它們收到的最後序列號,從而使用它來重新連接時恢復狀態。在這種情況下,Cenrtifugo向客戶端發送錯過的出版物,將此過程與PUB / SUB同步,以便消息以正確的順序傳遞給客戶端。如果Centrifugo不確定所有出版物是否都恢復了,那麼客戶就此使用特殊標誌作為回應。

優化

Centrifugo原始碼中使用了幾種優化。我們來看看其中一些。

要使用協議緩衝區,我們使用gogoprotobuf庫。它使用代碼生成並生成非常優化的代碼來編組和解組protobufs。此代碼的性能比標準Protobuf庫高3-6倍,後者基於反射並分配更多。

另一個優化是自動將不同的消息合併到一個客戶端到一個幀。這允許減少負載下的寫入系統調用量。

如果我們看一下實際生產Centrifugo v1實例的火焰圖,我們可以看到與寫系統調用相關的火焰有多寬。

Protocol設計有助於將消息合併在一起。在JSON格式的情況下,可以使用JSON流格式將多個消息組合在一起,其中每個單獨的協議消息由新的行符號分隔。在Protobuf案例中,我們使用長度分隔格式,其中每個單獨的消息都以varint消息長度為前綴。因此,我們可以簡單地將不同的消息寫入臨時緩衝區,然後在一次系統調用中將它們寫入連接。當然我們也可以使用sync.Pool重用那些臨時緩衝區。

下一個優化是使用Redis流水線技術,以便在使用Redis時獲得最佳性能。Pipilining允許在單個請求中向Redis發送多個命令。我們通過使用智能批處理技術收集來自不同goroutines的個別請求來構建Redis管道。讓我們看一下這個模式。

想像一下,我們有一個源渠道,我們可以從中獲取要處理的項目。我們不希望單獨處理項目,而是批量處理。為此我們等待來自頻道的第一個項目,然後嘗試從我們想要的頻道緩衝區中收集儘可能多的項目而不會阻塞和超時。然後處理我們一次收集的物品。例如,從它們構建Redis管道並在一次連接寫入調用中發送給Redis。

當由於我們有一個由幾個相關步驟組成的操作而無法使用流水線時,我們使用Lua腳本來完成艱苦的工作。Lua腳本只需一次往返Redis執行,而且它們是以原子方式執行的。

我們嘗試在客戶端組合消息 - 在我們的客戶端 - 以減少讀取系統調用量。

離心機庫

從我的演講中可以看出,Centrifugo是一個獨立的伺服器,它有自己的Go內置機制。我多次被問過的問題是可以在Go代碼中重用Centrifugo功能嗎?答案是:

嗯,是的,你可以 - 但由於Centrifugo從未為此設計過,你需要承擔風險。

在Centrifugo v2上工作時,我的目標之一就是將獨立的庫隔離開來,這個庫將成為Centrifugo v2的核心,但仍然可以被Go社區重用。

我的工作成果是離心機庫。你仍需要了解Centrifuge作為庫是非常具體的,因為它具有從Centrifugo伺服器繼承的機制。

離心機庫提供了Centrifugo伺服器功能以外的一些功能:

使用中間件進行本機認證與業務邏輯緊密集成雙向消息傳遞內置RPC調用渠道許可管理更自由讓我們看看庫的感受。由於時間非常有限,我們無法查看所有內容,但這裡是使用Centrifuge庫的完整程序,沒有任何用處。

這是你可以設置事件處理程序來處理新連接事件,客戶端斷開連接,客戶端訂閱請求和嘗試發布的方法。

客戶端庫

目前我們有幾個客戶用於離心機和Centrifugo:

centrifuge-jscentrifuge-gocentrifuge-mobilecentrifuge-dart (WIP)客戶可以使用Centrifugo伺服器和基於Centrifuge庫構建的自定義伺服器,這一點非常重要。讓我們看一下使用Javascript客戶端可能做的一些模式。

相關焦點

  • Flume+Kafka+Storm+Redis構建大數據實時處理系統
    在下面給出的完整案例中,我們將會完成下面的幾項工作:如何一步步構建我們的實時處理系統(Flume+Kafka+Storm+Redis)實時處理網站的用戶訪問日誌,並統計出該網站的PV、UV將實時分析出的PV、UV動態地展示在我們的前面頁面上如果你對上面提及的大數據組件已經有所認識,或者對如何構建大數據實時處理系統感興趣,那麼就可以盡情閱讀下面的內容了
  • 重磅 Facebook賈揚清宣布新機器學習系統Caffe2Go:可在行動裝置上實現實時風格遷移
    Facebook 最新開發的移動端深度學習平臺第一次擺脫了信號塔的束縛,可以實時捕捉、分析和處理圖像,將最新技術放進人們的手中。這一新程序被稱為 Caffe2Go,是一個完整的深度學習系統,它的架構已經嵌入手機 app 中。通過將處理圖片和視頻的人工智慧模型壓縮至百分之一大小,Facebook 現在已經可以在 iOS 和安卓系統中高效運行深度學習網絡。
  • 構建現代化的無服務 Go 應用
    從另一方面來講,將應用從虛擬機遷移到容器中可以給予我們在構建和部署過程中更多的靈活性,這樣做可以讓我們使用自動化工具快速地部署成百上千的微服務,然而成本也是非常高的。如果存在能夠完全託管的集群解決方案,那豈不是更好?
  • 使用Cettia構建實時Web應用程式第1部分 - 智能甄選
    在本教程中,我們將看看使用Cettia創建實時導向Web應用程式所需的功能,並構建Cettia入門工具包。2011年,我開始了Cettia的前任(一個用於HTTP流的jQuery插件,我曾用它演示Servlet 3.0的異步Servlet和IE 6)。
  • 利用flume+kafka+storm+mysql構建大數據實時系統
    實時日誌分析系統架構簡介系統主要分為四部分:1).數據採集負責從各節點上實時採集數據,選用cloudera的flume來實現2).數據接入由於採集數據的速度和數據處理的速度不一定同步,因此添加一個消息中間件來作為緩衝,選用apache
  • Worktile中百萬級實時消息推送服務的實現
    在團隊協同工具 Worktile的使用過程中,你會發現無論是右上角的消息通知,還是在任務面板中拖動任務,還有用戶的在線狀態,都是實時刷新。Worktile中的推送服務是採用的是基於XMPP協議、Erlang語言實現的Ejabberd,並在其源碼基礎上,結合我們的業務,對源碼作了修改以適配我們自身的需求。
  • 流域治理視角下構建彈性城市排水系統實時控制策略
    提升排水防澇能力、減少管網沿途溢流汙染、最大限度發揮管網調蓄能力和末端汙水處理能力一直是集中式城鎮排水系統追求的方向。尤其是隨著全球氣候變化,極端降雨事件增加,傳統的排水系統面臨日益嚴峻的挑戰,城鎮水系統安全成為城市管理最基礎性要素。而通過基礎設施建設增加排水系統的處理能力,不僅投資成本高回報期長,並且受土地使用等問題限制,不能廣泛應用於城鎮地區。
  • 颱風實時路徑發布系統:"山竹"最新消息預計30個小時進入廣東境內
    颱風實時路徑發布系統:"山竹"最新消息預計30個小時進入廣東境內 2018-09-15 10:26:51| 來源:颱風路徑
  • Python在實時嵌入式系統開發中的主要應用
    Python應用在在實時嵌入式系統中 眾所周知,Python跑在Linux與Windows上都沒問題,諸如Raspberry Pi等單板計算機也不在話下。這麼牛?在實時嵌入式系統中是否也有Python的一席之地?答案是肯定的。
  • 構建WhatsApp社交營銷獲得出海業務成功-用SCRM系統打造私域流量
    ◆私域流量池的特點私域流量池是指品牌運營或者個人擁有的可自由支配的流量,將業務運營構建在WhatsApp之上,利用WhatsApp平臺來構建私域流量池,具備如下幾點優勢特點: 實時在線。WhatsApp消息可以實時觸達,好友間互動是這樣,甚至陌生人之間也是實時觸達。WhatsApp用戶非常活躍,每天打開次數在20次以上,高頻互動。通過實時觸達,業務運營可將營銷內容高效地傳遞給客戶。 可成交。
  • 萬億數據下的多維實時分析系統,如何做到亞秒級響應
    啟用的內容給到推薦系統和運營系統,然後推薦系統和運營系統將內容進行C側分發。內容分發給C側用戶之後,用戶會產生各種行為,曝光、點擊、舉報等,通過埋點上報實時接入到消息隊列中。接下來我們做了兩部分工作,就是圖中有顏色的這兩部分。第一部分構建了一個騰訊看點的實時數據倉庫;第二部分就是基於OLAP存儲引擎,開發了多維實時數據分析系統。
  • 如何使用Kafka在生產環境構建大規模機器學習
    AI 前線導語:這篇文章將介紹機器學習在任務關鍵型實時系統中的應用,將 Apache Kafka 作為中心化的、可伸縮的任務關鍵型系統,同時還將介紹使用 Kafka Streams API 來構建智能流式應用。
  • 颱風路徑實時發布系統衛星雲圖——22號颱風海馬最新消息路徑圖
    颱風路徑實時發布系統衛星雲圖——22號颱風海馬最新消息路徑圖 2016-10-21 11:19:43| 來源:南方財富網
  • 颱風路徑實時發布系統:海南颱風桑卡颱風路徑實時發布系統衛星雲
    颱風路徑實時發布系統:海南颱風桑卡颱風路徑實時發布系統衛星雲 2017-07-27 10:02:15| 來源:南方財富網
  • 百億級訪問量的實時監控系統如何實現?
    在本文中,筆者將與大家分享一下在實時監控領域的一些實戰經驗,介紹WiFi***鑰匙是如何構建APM端到端的全鏈路監控平臺,從而實現提升故障發現率、縮短故障處理周期、減少用戶投訴率、樹立公司良好品牌形象等目標。
  • 使用TI-AM1808構建嵌入式導航系統
    本文探討了利用AM1808構建嵌入式導航系統的過程。並且探討了嵌入式文件系統的組成。在硬體和軟體方面對嵌入式系統構建過程中遇到的問題提出了解決方法。但是考慮到嵌入式系統成本和體積的要求,簡潔有力的設計更應該在整個設計流程中被體現出來。因此,某些功能必須被禁用,以提高系統的簡潔性和穩定性,並且降低成本。  系統的需求分析  使用AM1808構建嵌入式的導航系統,TFT顯示屏是必不可少的組件之一。TFT液晶顯示屏通過顯示不同的海圖和標誌,來引導船隻的正常航行。
  • 颱風路徑實時發布系統最新消息:黑格比颱風120小時路徑概率圖
    颱風路徑實時發布系統最新消息:黑格比颱風120小時路徑概率圖海南省氣象部門提醒,「黑格比」可能於9日前後進入南海中南部海面,將給南海中部和南部海域帶來大風天氣,請海上作業漁船和過往船隻注意避開大風影響區域。另外廣大市民朋友要及時關注該颱風的相關消息。
  • 一次使用 Go 語言編寫腳本的經歷
    Shebang類 UNIX 系統支持Shebang。Shebang 用於告訴 Shell 使用什麼解釋器來運行腳本。我們可以根據編寫腳本使用的語言來設置 Shebang 行。通常來說,我們會使用env命令最為腳本執行器,這樣就無需再使用解釋器的絕對路徑。例如:可以設置 Shebang 為 #!
  • Go 指南:頂級 Go 框架、IDE 和工具列表
    它具有熱重載功能,這意味著 dev 命令將自動查看.go 和.html 文件。然後,它將為你重建並重啟二進位文件。運行 dev 命令,你就能看到變化在你的眼前發生!Buffalo 不僅僅是一個框架——它也是一個整體的 Web 開發生態系統,可以讓你直接構建應用程式。
  • AI實時翻譯助手亮相釘釘發布會
    據釘釘介紹,AI實時翻譯助手這款產品,主要是為解決辦公場景中不同類語言障礙帶來的低效溝通,讓商業跨越語言邊界。舉個簡單的例子,用中文版釘釘的你,和用英文版釘釘的同事溝通,因為語言版本不同,系統會智能提示你開啟實時翻譯功能,一鍵開啟之後,你輸入的中文消息內容會自動翻譯為英文,對方收到的消息就是翻譯好的英文;對方用英文給你發消息,也可實時翻譯為中文發給你。