天天看點

随筆 - 消息隊列用戶端從服務端擷取消息時的原理

同僚:阿裡雲MNS擷取消息的API使用起來不夠友善,需要不斷手動長輪詢,看起來有點原始。

我:em。。。那其它消息隊列使用什麼方式?難道不是長輪詢?

在使用阿裡雲MNS(Message Notification Service)服務時,意識到其從服務端擷取消息的方式是手動長輪詢。寫代碼時可能會用到while-true看似危險的控制結構,而檢視手冊這也是官方推薦的使用方式。于是想找找其它消息隊列拉取消息的原理。

長輪詢

在此之前,我們首先做一些基礎知識準備——什麼是長輪詢?

而要回答這個問題之前,我們需要區分:長連接配接、短連結、長輪詢、短輪詢。

長連接配接、短連接配接

長短連接配接,指的的TCP連接配接。當用戶端通過socket連接配接到服務端時,就建立了一個TCP連接配接,當雙方都不關閉該連接配接時,它就是長連接配接。而如果在一次請求-響應後,關閉了這個TCP連接配接,則稱這個連接配接為短連結。

在TCP協定中,并沒有長連接配接、短連接配接的概念,這完全是根據使用方法區分。

需要注意TCP長連接配接會因為網絡波動、作業系統環境等問題出現連接配接丢失的問題,需要進行額外的連接配接保活工作,如發送定時心跳(心跳保活機制在很多場景下都有使用,比如Zookeeper;比如AMQP)。

長輪詢、短輪詢

輪詢,即用戶端與用戶端不斷重複 請求資料-消費資料 這一過程。有兩種政策

  • 用戶端請求,服務端有資料則傳回資料,沒有資料則傳回空。
  • 用戶端請求,服務端有資料則傳回資料,沒有資料則将連接配接保持,等有資料時再傳回。

HTTP的keep-alive

TCP是傳輸層協定,而HTTP是應用層協定。而頭部Connection: Keep-Alive表示維持TCP連接配接,即我們常說的HTTP長連接配接。

Keep-Alive的工作原理如下:

  • 用戶端請求攜帶Connection: Keep-Alive頭部,服務端接收後,會在響應該條HTTP請求後繼續保持TCP連接配接;否則關閉。
  • 用戶端在收到的服務端響應後,如果有攜帶Connection: Keep-Alive頭部,則會使用同一個TCP連接配接發送下一個HTTP請求。

此外,Keep-Alive還可以設定timeout參數,用于指定預期的連接配接逾時時間。

注意HTTP協定本身是一個請求-響應協定,無狀态,即一個事務在請求-響應後就結束了,再次請求就是一個新事務。而HTTP協定中定義了幾種HTTP連接配接:

  • 正常連接配接:即請求響應後即關閉TCP連接配接。
  • 持久連接配接(又稱長連接配接):即可以傳輸多個事務的連接配接,傳輸後不關閉TCP連接配接,下次傳輸繼續使用。對應Keep-Alive。
  • 管道式連接配接:即在一個TCP連接配接上一次發送多個事務的請求,再依次接收多個事務的請求。傳統的是一個事務的請求-響應完成後再進行下一個事務。

阿裡雲MNS

阿裡雲MNS的資料傳輸走的是HTTP協定,意味着無論是點對點、還是釋出-訂閱模式,都要通過HTTP來進行。這就導緻了文章一開始所說的使用方式的問題,由于HTTP協定本身無狀态,隻能是用戶端請求-服務端響應,用戶端拉取資料時隻能采用輪詢的方式。而為了拉取操作過于頻繁帶來的損失,采用的長輪詢:用戶端請求,服務端沒有消息時,挂起該連接配接,等到有消息時再響應。

也許你很好奇,如果走HTTP協定,那用戶端如何訂閱主題,即用戶端如何被動收到消息?答案是設定回調位址;或将主題消息推送到另一個隊列,然後長輪詢該隊列。

總之阿裡雲MNS擷取消息的方式因為其采用HTTP傳輸協定而受到了限制。不過這也有一個好處,就是任何語言都能使用MNS服務,甚至不需要SDK,手動調起HTTP接口即可。

RabbitMQ

RabbitMQ走的是AMQP協定,它和HTTP一樣,也是應用層協定,建構于TCP之上。與HTTP不同的是,它專門設計用于消息傳輸。用戶端和服務端采用TCP長連接配接,前面說過,隻要建立了TCP連接配接,資料就能雙向傳輸。這樣就能做到用戶端不需要主動輪詢,服務端推送消息到用戶端的效果。使用起來友善很多。對AMQP協定,就是Basic.Consume。

WebSocket

既然說到了AMQP的雙向傳輸功能,就聯想到了WebSocket,它是如何實作雙向傳輸的呢?在翻看一些資料後,發現它也是建立了一個持久的TCP連接配接,利用TCP雙向傳輸的特性,實作服務端的消息推送。

通常說WebSocket時,是對比HTTP協定,HTTP協定無狀态,事務間隔離,建立在請求-響應這樣的被動機制下,在應用上無法實作服務端主動推送消息的場景。WebSocket的出現就是為了解決該問題,

如果把WebSocket和AMQP進行對比,在服務端主動發送消息到用戶端這一點來說,二者原理一緻。甚至可以說WebSocket是AMQP的功能子集。當然也不能這麼說,畢竟他們目标不同,WebSocket聚焦于推送消息,而AMQP聚焦于消息模型的抽象,順帶解決消息傳輸的問題。且就消息推送這一點來說,盡管原理一緻,實作方式可能也是不同的。

還可深入探索的點

當然消息隊列除了上述兩個,還有很多其它的,比如流行的Kafka、RocketMQ、ActiveMQ等。由于他們并不是基于AMQP協定,是以具體方式還不知道。不過我想,傳輸層都是TCP,實作雙向傳輸的機制應該逃不過TCP長連接配接的。

繼續閱讀