隊列是什麼意思?
能詳細的說下嗎,整天聽到隊列,頭都疼了,百度也找不到好的解釋
首先,隊列是一種數據結構,用鏈表和數組都可以實現,隊列的特點就是先放入隊列的數據先出隊列。
不過看到話題標籤有Redis,猜測題主想問的應該是現在很廣泛使用的消息隊列(MQ)。這裡的消息不只是簡單的文本信息,也可以是序列化後的對象。現在比較流行的開源消息隊列系統有Beanstalkd,RabbitMQ,Redis(可以作為隊列系統使用)等,其核心作用都是先將消息數據通過系統介面按順序放入隊列(暫存於內存),需要時再按放入的順序依次取出以作後續處理。
就拿我前段時間做的郵件發送系統舉例:
系統以HTTP協議提供服務,對外提供的主要功能是發送郵件,API地址為 /api/msg.send。使用者只要以POST請求此地址,傳入subject,body,recipient這三個參數,即可發送一封郵件。
方案1: 不使用資料庫,不使用隊列
請求介面時,控制器直接調用sendmail或外部smtp伺服器發送郵件,整個發送過程HTTP請求處於等待狀態,待郵件發送完成,返回發送結果(只是調用發送程序是否成功的結果,不是郵件真正的發送結果,真正發送結果需要分析郵件日誌)
這個方案最簡單直接,平時發送少量郵件是可以的,但是缺點也很明顯:
1. 因為介面調用時整個郵件發送過程都是同步進行的,每次請求都要等待郵件發送端完成處理,必然導致每次調用介面的等待時間增長。
2. 當系統介面並發請求較高時,系統可用性不僅受限於WebServer的處理能力,還完全受限於郵件發送端軟體的處理能力,其中任一環節故障就會導致整個系統無法提供服務。
3. 若郵件發送端軟體出現故障(比如SMTP連接超時),導致某次請求時郵件發送失敗,那這封郵件內容便徹底丟失了,系統沒有任何存留,不能實現自動重發。
方案2: 使用資料庫,不使用隊列
請求介面時,將要發送的郵件信息存入資料庫,表結構如下:
id | subject | body | recipient | sent_at | failed_at | failed_times
然後在伺服器上運行一個定時任務,每秒一次讀取 sent_at=0 failed_times &< 10 的記錄,隨後調用郵件發送端發送郵件,成功後把sent_at設為當前時間,失敗後設置failed_at並累加failed_times。 方案2已經用到了類似隊列的思想。
相對於方案1的提升:
1. 去掉了同步發送郵件的操作,介面請求響應會快很多
2. 郵件發送失敗後可以重發,郵件不會丟失
3. 當郵件發送端完全失效後系統也可以接受郵件發送請求,待發送端恢復後可以繼續發送郵件。
但還是存在缺點:
每次請求都會寫一次資料庫,當大並發量或者大數據量(一次請求包含100萬個收件人)時,資料庫負載過高影響穩定性,同時也會嚴重增加介面的響應時間(一下子寫入100萬條記錄不是鬧著玩的)
方案3: 資料庫 + 隊列
請求介面時,將要發送的郵件信息以JSON格式存入隊列系統,放入的單個消息形如:
{
"subject" : "今天沒吃藥",
"body" : "感覺自己",
"recipient" : "mengmeng@da.com"
}
現在的隊列基本上都是內存隊列,數據存取非常快,一瞬間寫入100萬條數據再也不是難事。
隨後,在伺服器上運行一個常駐進程任務(Worker),實時監聽隊列中是否有新的消息(Job,此處指郵件信息)。當新消息進入時,從隊列中取出消息,調用郵件發送端完成處理,發送成功後將此消息銷毀並將消息內容插入資料庫(同方案2),如果發送失敗,將此消息重新放入隊列,並加入一個60秒的延時標記,意味60秒後再取出處理。
這樣改進後整個系統的吞吐量和響應速度將大大提升,而且同時也讓系統支持了分散式運行的能力。每個Worker進程都可以視為一個處理節點,倘若把worker分散到不同的伺服器上,便實現整個系統的分散式處理了,這也是隊列的一個重要特性之一。
在我實際項目中還是做了許多基於方案3的改進,對於群發還使用了郵件列表和郵件模板等設計,整個系統類似Mailgun和Sendcloud的設計,等整個系統穩定下來,我會考慮將代碼開源到Github。
這個例子只是隊列的一個常見使用場景,一般來說在需要緩解資料庫寫入壓力的場景下面,都可以考慮使用消息隊列,還有一些需要分散式處理的情況下,也是隊列很好的使用場景。
現在大部分語言都有成熟的消息隊列處理組件,可以很方便的使用各種隊列系統,比如我常用的
Laravel 便原生支持了 Beanstalkd,Amazon SQS,IronMQ,Redis。
抱磚引玉,不足之處請指正,謝謝。
排隊聽說過吧?先來的站前面(隊首),後來的跟在後面(隊尾),出隊的時候從隊首出,入隊的跟在隊尾,這就是隊列!至於實現,鏈表和數組都行!
推薦閱讀: