高併發之消息隊列

来源:https://www.cnblogs.com/xiangkejin/archive/2018/07/07/9278356.html
-Advertisement-
Play Games

消息隊列已經逐漸成為企業IT系統內部通信的核心手段。它具有低耦合、可靠投遞、廣播、流量控制、最終一致性等一系列功能,成為非同步RPC的主要手段之一。 消息被處理的過程相當於流程A被處理。我們這裡以一個實際的模型來討論下,比如用戶下單成功時給用戶發簡訊,如果沒有這個消息隊列,我們會選擇同步調用發簡訊的接 ...


消息隊列已經逐漸成為企業IT系統內部通信的核心手段。它具有低耦合、可靠投遞、廣播、流量控制、最終一致性等一系列功能,成為非同步RPC的主要手段之一。

消息被處理的過程相當於流程A被處理。我們這裡以一個實際的模型來討論下,比如用戶下單成功時給用戶發簡訊,如果沒有這個消息隊列,我們會選擇同步調用發簡訊的介面,

並等待短息發送成功,這時候假設簡訊介面實現出現問題了,或者簡訊調用端超時了,又或者簡訊發送達到上限了,我們是選擇重試幾次還是放棄,還是選擇把這個放到資料庫

過一段時間再看看呢,不管怎樣,實現都很複雜。

我們可以將發簡訊這個請求放在消息隊列里,消息隊列按照一定的順序挨個處理隊列里的消息,當處理到發送簡訊的任務時,通知簡訊服務發送消息,如果出現之前出現的問題,那麼把這個消息重新放到消息隊列中。

 

消息隊列的好處:

1.成功完成了一個非同步解耦的過程。簡訊發送時只要保證放到消息隊列中就可以了,接著做後面的事情就行。一個事務只關心本質的流程,需要依賴其他事情但是不那麼重要的時候,有通知即可,無需等待結果。每個成員不必受其他成員影響,可以更獨立自主,只通過一個簡單的容器來聯繫。

對於我們的訂單系統,訂單最終支付成功之後可能需要給用戶發送簡訊積分什麼的,但其實這已經不是我們系統的核心流程了。如果外部系統速度偏慢(比如簡訊網關速度不好),那麼主流程的時間會加長很多,用戶肯定不希望點擊支付過好幾分鐘才看到結果。那麼我們只需要通知簡訊系統“我們支付成功了”,不一定非要等待它處理完成。

 

2.保證了最終一致性,通過在隊列中存放任務保證它最終一定會執行。

最終一致性指的是兩個系統的狀態保持一致,要麼都成功,要麼都失敗。當然有個時間限制,理論上越快越好,但實際上在各種異常的情況下,可能會有一定延遲達到最終一致狀態,但最後兩個系統的狀態是一樣的。
業界有一些為“最終一致性”而生的消息隊列,如Notify(阿裡)、QMQ(去哪兒)等,其設計初衷,就是為了交易系統中的高可靠通知。
以一個銀行的轉賬過程來理解最終一致性,轉賬的需求很簡單,如果A系統扣錢成功,則B系統加錢一定成功。反之則一起回滾,像什麼都沒發生一樣。
然而,這個過程中存在很多可能的意外:

  1. A扣錢成功,調用B加錢介面失敗。
  2. A扣錢成功,調用B加錢介面雖然成功,但獲取最終結果時網路異常引起超時。
  3. A扣錢成功,B加錢失敗,A想回滾扣的錢,但A機器down機。

可見,想把這件看似簡單的事真正做成,真的不那麼容易。所有跨VM的一致性問題,從技術的角度講通用的解決方案是:

  1. 強一致性,分散式事務,但落地太難且成本太高,後文會具體提到。
  2. 最終一致性,主要是用“記錄”和“補償”的方式。在做所有的不確定的事情之前,先把事情記錄下來,然後去做不確定的事情,結果可能是:成功、失敗或是不確定,“不確定”(例如超時等)可以等價為失敗。成功就可以把記錄的東西清理掉了,對於失敗和不確定,可以依靠定時任務等方式把所有失敗的事情重新搞一遍,直到成功為止。
    回到剛纔的例子,系統在A扣錢成功的情況下,把要給B“通知”這件事記錄在庫里(為了保證最高的可靠性可以把通知B系統加錢和扣錢成功這兩件事維護在一個本地事務里),通知成功則刪除這條記錄,通知失敗或不確定則依靠定時任務補償性地通知我們,直到我們把狀態更新成正確的為止。

3.廣播

消息隊列的基本功能之一是進行廣播。如果沒有消息隊列,每當一個新的業務方接入,我們都要聯調一次新介面。有了消息隊列,我們只需要關心消息是否送達了隊列,至於誰希望訂閱,是下游的事情,無疑極大地減少了開發和聯調的工作量。

3.提速。假設我們還需要發送郵件,有了消息隊列就不需要同步等待,我們可以直接並行處理,而下單核心任務可以更快完成。增強業務系統的非同步處理能力。甚至幾乎不可能出現併發現象。

4.削峰和流控。不對於不需要實時處理的請求來說,當併發量特別大的時候,可以先在消息隊列中作緩存,然後陸續發送給對應的服務去處理

試想上下游對於事情的處理能力是不同的。比如,Web前端每秒承受上千萬的請求,並不是什麼神奇的事情,只需要加多一點機器,再搭建一些LVS負載均衡設備和Nginx等即可。但資料庫的處理能力卻十分有限,即使使用SSD加分庫分表,單機的處理能力仍然在萬級。由於成本的考慮,我們不能奢求資料庫的機器數量追上前端。
這種問題同樣存在於系統和系統之間,如簡訊系統可能由於短板效應,速度卡在網關上(每秒幾百次請求),跟前端的併發量不是一個數量級。但用戶晚上個半分鐘左右收到簡訊,一般是不會有太大問題的。如果沒有消息隊列,兩個系統之間通過協商、滑動視窗等複雜的方案也不是說不能實現。但系統複雜性指數級增長,勢必在上游或者下游做存儲,並且要處理定時、擁塞等一系列問題。而且每當有處理能力有差距的時候,都需要單獨開發一套邏輯來維護這套邏輯。所以,利用中間系統轉儲兩個系統的通信內容,併在下游系統有能力處理這些消息的時候,再處理這些消息,是一套相對較通用的方式。

總而言之,消息隊列不是萬能的。對於需要強事務保證而且延遲敏感的,RPC是優於消息隊列的。
對於一些無關痛癢,或者對於別人非常重要但是對於自己不是那麼關心的事情,可以利用消息隊列去做。
支持最終一致性的消息隊列,能夠用來處理延遲不那麼敏感的“分散式事務”場景,而且相對於笨重的分散式事務,可能是更優的處理方式。
當上下游系統處理能力存在差距的時候,利用消息隊列做一個通用的“漏斗”。在下游有能力處理的時候,再進行分發。
如果下游有很多系統關心你的系統發出的通知的時候,果斷地使用消息隊列吧。

 

消息隊列的使用場景:

主要特點是非同步處理,主要目的是減少請求響應時間和解耦。所以主要的使用場景就是將比較耗時而且不需要即時(同步)返回結果的操作作為消息放入消息隊列。

使用場景的話,舉個例子:
假設用戶在你的軟體中註冊,服務端收到用戶的註冊請求後,它會做這些操作:
  1. 校驗用戶名等信息,如果沒問題會在資料庫中添加一個用戶記錄
  2. 如果是用郵箱註冊會給你發送一封註冊成功的郵件,手機註冊則會發送一條簡訊
  3. 分析用戶的個人信息,以便將來向他推薦一些志同道合的人,或向那些人推薦他
  4. 發送給用戶一個包含操作指南的系統通知
  5. 等等……

但是對於用戶來說,註冊功能實際只需要第一步,只要服務端將他的賬戶信息存到資料庫中他便可以登錄上去做他想做的事情了。至於其他的事情,非要在這一次請求中全部完成麽?值得用戶浪費時間等你處理這些對他來說無關緊要的事情麽?所以實際當第一步做完後,服務端就可以把其他的操作放入對應的消息隊列中然後馬上返回用戶結果,由消息隊列非同步的進行這些操作。

或者還有一種情況,同時有大量用戶註冊你的軟體,再高併發情況下註冊請求開始出現一些問題,例如郵件介面承受不住,或是分析信息時的大量計算使cpu滿載,這將會出現雖然用戶數據記錄很快的添加到資料庫中了,但是卻卡在發郵件或分析信息時的情況,導致請求的響應時間大幅增長,甚至出現超時,這就有點不划算了。面對這種情況一般也是將這些操作放入消息隊列(生產者消費者模型),消息隊列慢慢的進行處理,同時可以很快的完成註冊請求,不會影響用戶使用其他功能。

 

為什麼需要消息隊列?

生產和消費的速度或者穩定性不一致。

 

當今市面上有很多主流的消息中間件,如老牌的ActiveMQ、RabbitMQ,炙手可熱的Kafka,阿裡巴巴自主開發的Notify、MetaQ、RocketMQ等。

Kafka的介紹

Kafka是一種高吞吐量的分散式發佈訂閱消息系統,它可以處理消費者規模的網站中的所有動作流數據。
Kafka 有如下特性:

  • 以時間複雜度為O(1)的方式提供消息持久化能力,即使對TB級以上數據也能保證常數時間複雜度的訪問性能。
  • 高吞吐率。即使在非常廉價的商用機器上也能做到單機支持每秒100K條以上消息的傳輸。
  • 支持Kafka Server間的消息分區,及分散式消費,同時保證每個Partition內的消息順序傳輸。
  • 同時支持離線數據處理和實時數據處理。
  • Scale out:支持線上水平擴展。

kafka的術語

  • Broker:Kafka集群包含一個或多個伺服器,這種伺服器被稱為broker。
  • Topic:每條發佈到Kafka集群的消息都有一個類別,這個類別被稱為Topic。(物理上不同Topic的消息分開存儲,邏輯上一個Topic的消息雖然保存於一個或多個broker上但用戶只需指定消息的Topic即可生產或消費數據而不必關心數據存於何處)
  • Partition:Partition是物理上的概念,每個Topic包含一個或多個Partition。
  • Producer:負責發佈消息到Kafka broker。
  • Consumer:消息消費者,向Kafka broker讀取消息的客戶端。
  • Consumer Group:每個Consumer屬於一個特定的Consumer Group(可為每個Consumer指定group name,若不指定group name則屬於預設的group)。

 

下麵來介紹RabbitMQ里的一些基本定義,主要如下:
RabbitMQ Server:提供消息一條從Producer到Consumer的處理。
Exchange:一邊從發佈者方接收消息,一邊把消息推送到隊列。
producer只能將消息發送給exchange。而exchange負責將消息發送到queues。Procuder Publish的Message進入了exchange,exchange會根據routingKey處理接收到的消息,判斷消息是應該推送到指定的隊列還是是多個隊列,或者是直接忽略消息。這些規則是通過交換機類型(exchange type)來定義的主要的type有direct,topic,headers,fanout。具體針對不同的場景使用不同的type。
queue也是通過這個routing keys來做的綁定。交換機將會對綁定鍵(binding key)和路由鍵(routing key)進行精確匹配,從而確定消息該分發到哪個隊列。
Queue:消息隊列。接收來自exchange的消息,然後再由consumer取出。exchange和queue可以一對一,也可以一對多,它們的關係通過routingKey來綁定。
Producer:Client A & B,生產者,消息的來源,消息必鬚髮送給exchange。而不是直接給queue
Consumer:Client 1,2,3消費者,直接從queue中獲取消息進行消費,而不是從exchange中獲取消息進行消費。

 

 

 

 

 


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

-Advertisement-
Play Games
更多相關文章
  • 切庫的基礎和實際運用—讀寫分離: 如何方便進行讀寫分離,目前有兩種方式: 1.動態數據源切換 它是指程式運行時,把數據源動態的織入到程式中,讓指定的程式連接主庫還是從庫 自定義註解完成資料庫切庫 2.直接定義查數據源和寫數據源 直接在項目里定義兩個資料庫連接,一個是主庫連接一個是從庫連接,更新數據的 ...
  • 通過jdbc插入java.util.date類型的時間代碼示例: 資料庫裡面的列是datetime類型的用 ps.setString(3, time); 插入數據,完全可以的。 資料庫插入的數據顯示: 網頁數據顯示: ...
  • python3 最重要的新特性是對文本和二進位數據作了更為清晰的區分,文本總會Unicode,有str類型表示,二進位數據則是由bytes類型表示。python3不會以任意隱式的方式混用str和bytes,正是這使得兩者的區分特別明顯。 但是,字元串可以編碼成位元組包,而位元組包可以解碼成字元串。如圖所 ...
  • 本文適應人群:C# or Python3 基礎鞏固 代碼褲子: https://github.com/lotapp/BaseCode 線上編程: https://mybinder.org/v2/gh/lotapp/BaseCode/master 線上預覽:http://github.lesschin ...
  • 前面我們已經提到單個伺服器再優化,它的處理能力都是有上限的,因此我們選擇多擴容以及使用緩存和消息隊列等對程式進行優化。 下麵介紹另一種方法,隨著項目需求完成越來越多,應用自然也會越來越大,架構師將一個應用整體拆分成多個應用。 拆分的原則: 1.業務優先,確定業務邊界 2.循序漸進,邊拆分邊測試 3. ...
  • A.所屬包 java.lang B.聲明 public class Object C.概述 是所有類的父類 D.構造方法: Object() E.常用成員方法 a.protected Object clone() 創建並返回該對象的一個副本,需要實現Cloneable介面重寫此方法 b.public ...
  • Sublime下配置python編譯環境及新手使用指導: 1.編譯環境配置 1.點擊 "官方網址" 從 Sublime Text 3 官方獲取用於安裝的代碼。從菜單欄View Show Console或者使用Ctrl+`(esc鍵下麵的按鍵),調出sublime的控制台,將官網中的python代碼粘 ...
  • 遇到這樣一個問題。我想要統計某個文件夾下有多少個py文件怎麼辦。 用python能解決嗎?答案,能。 解決辦法,使用glob 代碼如下: 很簡單的幾行代碼,。 第一步,導入glob庫 第二步,使用glob下麵的glob的方法,參數是路徑下判斷的文件 第三步:列印: 結果: 這個是和我當前目錄所有的p ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...