使用者請求佇列化 訊息佇列看過來!

2021-10-14 16:11:09 字數 3881 閱讀 8263

當我試圖用一則通俗的比喻來說明這個概念的時候,我想到乙個有意思的比喻:如果把佇列抽象成乙個集合體,那麼訊息佇列也就是一堆訊息的集合。按照這個思路我想到了「雜誌」。這不就是一堆訊息的集合嗎,關心這些訊息的人都能通過「購買」來獲得這些訊息,而我可以通過不同種類的「雜誌」或許到不同的訊息。並且如果我作為出版方,我可以提供所有出版過的「雜誌」,也可以選擇讓讀者只能購買近期的。

假設我們做了乙個會議室預定系統,我們的乙個裝置壞了。我們需要通知預定這個會議室的所有人,於是我們需要發郵件,偽**如下:

@service

public class equipmentserviceimpl implements equipmentservice 

}

問題來了,如果我們後來發現裝置壞了並且需要更改可用庫存的數量,這時候我們是不是要在這裡加入inventoryservice庫存服務的**呢?後來如果經理說裝置壞了應該通知他才對啊,所以我們要不要加入emailservice.sendemailto(manager)這樣的**呢?

隨著我們業務模組接入越來越多,我們的**與其他模組越來越耦合,修改**的難度也指數級的增加,所以我們引入「訊息佇列」,把「裝置壞了」這樣的訊息傳送到佇列中,其他關心這條訊息的業務就會得到這樣的「通知」,然後就會去做對應的事,這樣各個模組之間就解耦了。偽**看上去如下:

public void setequipmentbroken(long id) 

接著上面的例子,假設我們已經把「傳送郵件」、「修改庫存」以及「通知經理」的**都寫入了我們的 service **中,它們分別耗時:30ms、50ms、80ms,並且我們得知,原本最主要的功能其實是「傳送郵件」,但我們完成主要的功能之後卻等待了更多的額外時間,這顯示是不合理的。

所以我們為了提高使用者體驗&提高吞吐量,我們其實可以引入「訊息佇列」來進行非同步的操作。

假設我們的伺服器最多能支援每秒 1000 個請求,而我們公司在節日要搞**,為了避免伺服器掛掉我們額外申請了兩台伺服器做了負載均衡,於是我們現在的機器最理想的情況能夠支援每秒 3000 個請求,但奈何活動太火爆了,每秒來的請求有大概 4000 個,這些多出來的請求就可能導致伺服器給直接掛掉了。

所以我們就引入了乙個「訊息佇列」,讓訊息不直接到達伺服器,而是先讓「訊息佇列」儲存這些資料,然後讓下面的伺服器每一次都取各自能處理的請求數再去處理,這樣當請求數超過伺服器最大負載時,就不至於把伺服器搞掛了。

基於上面的描述,我們大概能想到「訊息佇列」的侷限性,例如當「生產者」需要「從消費者獲得反饋」時,就會出現一定的問題。例如我之前嘗試著使用「事件驅動」的方式編碼時,我想要把 service 的一些主邏輯給轉移到關注該事件的***上時,發現有點問題,我原本的意圖是想讓一部分**解耦,但作為主邏輯的一部分我需要保證它們準確的執行,當我使用「訊息」的方式傳遞出去時,我無法得到消費者的反饋,所以最終我還是把主邏輯給遷回來了,算是一次失敗的嘗試吧。

通過上述的問題你也看到了,「訊息佇列」適用於非同步處理,並且是那些不期望從消費者得到反饋的處理。就好像一開始說到的裝置壞了的問題,我只需要通知裝置壞了,至於之後需要做什麼事,關心的人自然會去做相應的處理。

上面提到的非同步處理,跟日誌系統似乎搭配起來也很好。特別是當你需要把日誌發往單獨的資料平台的時候,「訊息佇列」尤為有用,我們不再需要在業務**裡面侵入我們的各種打點or日誌,只需要簡單的發布一條訊息,再去關注做處理就好了。

基於上面的例子你應該也能感受一二了。

這也是「訊息佇列」常見的場景,通過引入「訊息佇列」,我們一來可以控制請求的人數,二來也可以緩解短時間內高流量的壓力。

訊息通訊是指,訊息佇列一般都內建了高效的通訊機制,因此也可以用在純的訊息通訊。比如實現點對點訊息佇列,或者聊天室等。

我們在討論市面上常見的「訊息佇列」中介軟體之前,我們先來考慮自己造乙個怎麼樣?如果是你自己來設計,你會怎麼做?乍一想,似乎每個語言都會有自己實現的「佇列」,往佇列裡塞資料,再從佇列裡面挨個取就行了?

但是一細想好像事情並不簡單。作為乙個「訊息佇列」,你首先要保證資料不能給人家弄丟了吧?存記憶體?萬一斷電了怎麼辦?寫磁碟?訊息量超過系統寫磁碟速率上限了怎麼辦?備份又該怎麼做呢?

好,假設我一整搗鼓,保證了我的資料不會丟失了,下乙個問題,生產者怎麼往「訊息佇列」裡面塞資料?我的意思是,生產者可能不止乙個,把全量的訊息放在乙個佇列似乎不太合適,我需要給這些訊息分個類吧?新來了乙個分類的訊息我怎麼動態的擴容呢?消費者又如何消費這些資料呢?多個消費者之間又如何進行協調呢?

好吧..總之問題挺多的..並不像表面那麼簡單。

rabbitmq 是使用 erlang 編寫的乙個開源的訊息佇列,本身支援很多的協議:amqp,xmpp, smtp, stomp,也正因如此,它非常重量級,更適合於企業級的開發。同時實現了 broker 構架,這意味著訊息在傳送給客戶端時先在中心佇列排隊。對路由,負 載均衡或者資料持久化都有很好的支援。

redis 也能用來做「訊息佇列」。redis 是乙個基於 key-value 對的 nosql 資料庫,開發維護很活躍。雖然它是乙個 key-value 資料庫儲存系統,但它本身支援 mq 功能, 所以完全可以當做乙個輕量級的佇列服務來使用。對於 rabbitmq 和 redis 的入隊和出隊操作,各執行 100 萬次,每 10 萬次記錄一次執行時間。測試 資料分為 128 bytes、512 bytes、1 k和 10 k四個不同大小的資料。實驗表明:入隊時,當資料比較小時 redis 的效能要高於 rabbitmq,而如果資料大小超過了 10 k,redis 則慢的無法忍受;出隊時,無論資料大小,redis 都表現出非常好的效能,而 rabbitmq 的出隊效能則遠低於redis。

kafka 是 apache 下的乙個子專案,是乙個高效能跨語言分布式 publish/subscribe 訊息佇列系統,而 jafka 是在 kafka 之上孵化而來的,即 kafka 的乙個公升級版。

具有以下特性:

zeromq 號稱最快的訊息佇列系統,尤其針對大吞吐量的需求場景。zeromq 能夠實現 rabbitmq 不擅長的高階 / 複雜的佇列,但是開發人員需要自己組合多種技術框架,技術上的複雜度是對這 mq 能夠應用成功的挑戰。zeromq 具有乙個獨特的非中介軟體的模式,你不需要安裝和執行乙個訊息伺服器或中介軟體,因為你的應用程式將扮演這個伺服器角色。你只需要簡單的引用 zeromq 程式庫,可以使用 nuget 安裝,然後你就可以愉快的在應用程式之間傳送訊息了。但是 zeromq 僅提供非永續性的佇列,也就是說如果宕機,資料將會丟失。其中,twitter 的 storm 0.9.0 以前的版本中預設使用 zeromq 作為資料流的傳輸(storm 從 0.9 版本開始同時支援 zeromq 和 netty 作為傳輸模組)。

activemq 是 apache 下的乙個子專案。類似於 zeromq,它能夠以**人和點對點的技術實現佇列。同時類似於 rabbitmq,它少量**就可以高效地實現高階應用場景。

kafka 設計解析(一):kafka 背景及架構介紹

**訊息佇列及常見的訊息中介軟體

訊息佇列介紹及常用mq對比

什麼是訊息佇列?

訊息佇列的使用場景是怎樣的?- 知乎

訊息佇列 訊息佇列

輪詢排程 一次性分發所有訊息,保證訊息平均分配,不管消費者是否能正常消費 訊息應答 保證消費端能確實消費,不丟失 公平 乙個乙個分發所有訊息,在保證分發到的執行緒確認回覆後,才分發下個訊息給下個空閒的消費者,訊息持久化 保證佇列中的訊息不丟失,包括3要素 交換器 訊息佇列 訊息都必須宣告持久化 發布...

訊息佇列 訊息佇列 kafka

kafka是乙個分布式的基於發布 訂閱模式的訊息佇列,主要用於大資料實時處理領域。要理解kafka首先要有分布式的概念,要有訊息佇列的概念。分布式系統最大的優勢就是解耦和削峰,這種情況下,a系統生成了乙個訊息,b系統非同步獲取,那麼就需要乙個存放訊息的訊息佇列 mq 相比較傳統的訊息佇列,訊息被消費...

linux訊息佇列 Linux訊息佇列

訊息佇列,unix的通訊機制之一,可以理解為是乙個存放訊息 資料 容器。將訊息寫入訊息佇列,然後再從訊息佇列中取訊息,一般來說是先進先出的順序。可以解決兩個程序的讀寫速度不同 處理資料速度不同 系統耦合等問題,而且訊息佇列裡的訊息哪怕程序崩潰了也不會消失。最簡單的訊息記憶體的使用流程 ftok函式生...