一、消息隊列
1.1消息隊列概述
消息隊列(Message Queue),是分佈式系統中重要的組件
1.2消息隊列使用場景
分佈式場景
- 異步處理
- 應用解耦
- 流量削峯
日誌場景
- 優化日誌傳輸,提升系統性能
及時通信
- 聊天室
解決應用解耦,異步消息,流量削鋒等問題,實現高性能,高可用,可伸縮
1.3消息隊列相關概念
- 消息隊列:即存儲消息的容器,圖中的兩條直線描繪的管道即消息隊列
- 消息:消息即需要寫入的隊列的數據,圖中用A1、A2、A3、An表示
- 入隊出隊:消息隊列是先進先出的
- 生產者:數據的來源
- 消費者:數據的去向
我們常用RabbitMQ和kafka等專業的消息隊列中間件完成異步消息隊列功能,這些專業的消息隊列功能強大特性豐富,但是使用也相對繁瑣。對於一些簡單的消息隊列場景和對消息的可靠性沒有極致的追求,可以使用Redis完成消息隊列的需求。我們這裏講的是Redis 的消息隊列,相瞭解RabbitMQ的相關知識可以關注我的另一個系列《RabbitMQ入門到進階系列》
二、Redis消息隊列
我們在上一講中已經瞭解了redis有5中數據結構,其中list 數據常被用來作爲消息隊列使用。操作redis消息隊列時,使用lpush/rpush操作入隊列,使用lpop/rpop操作出隊列。lpush/rpop是一組操作,rpush/lpop是一組操作,也就是說左邊進隊,右邊出隊,右邊進隊,左邊出隊,二者使用上沒有區別。這個很容易理解,消息隊列就像一個密閉的管道左邊進去肯定是右邊出來,右邊進去肯定是左邊出來,中間是沒辦法掉頭的。
要在.Net Core 中使用Redis的話最好是使用Service.Stack.Redis進行操作。可以使用Nuget安裝(Service.Stack.Redis是收費的,免費的一小時限制6000次請求,後面會教大家如何破解)
生產者代碼
//1、創建RedisClient
RedisClient redisClient = new RedisClient("localhost:6379");
//2、創建生產者,並插入消息
byte[] bytes = Encoding.UTF8.GetBytes("這是一條消息");
redisClient.LPush("TestMQ", bytes);//參數解析:1、消息隊列名字,消息轉換成的byte數組
第一行代碼:通過RedisClient對象(需要引入ServiceStack.Redis命名空間)創建Redis 的連接實例,參數爲redis服務器和端口
第二行代碼:由於在消息隊列中都是通過byte數組傳輸的,所以得先把要入隊的消息轉換成byte數組
第三行代碼:使用LPush方法向隊列插入信息,第一個參數爲目標隊列名字,如果redis中沒有這個隊列則會創建一個,第二個參數是剛纔轉換好的消息byte數組
代碼執行後在Redis Desktop Manager中看到響應的消息
消費者代碼
//1、創建RedisClient
RedisClient redisClient = new RedisClient("localhost:6379");
//2、消費消息
while(true){
byte[]reads= redisClient.RPop("TestMQ");
Console.WriteLine($"獲取到消息爲:{Encoding.UTF8.GetString(reads)}");
}
因爲我們生產者使用的LPush插入數據,所以消費者端需要使用RPop來獲取數據 。插入數據時需要轉換成byte數組,接受也是通過byte數組接受,後面再轉成正確類型。
BRPop/BLPop
細心的同學可能會發現,上面的代碼使用while循環獲取隊列消息,但是如果隊列已經沒有數據,肯定會陷入死循環。這時我們可以使用BRPop或者BLpop方法替換原先的RPop/LPop。Brpop 命令移出並獲取列表的最後一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。
如果有多個消費者端,將會通過輪詢的方式分攤給消費者端。下面結合BRPop進行演示
修改生產者代碼變成手動輸入消息,以便更好演示
生產者代碼
Console.WriteLine("當前爲生產者:請輸入消息,輸入exit退出!");
string input;
do
{
input = Console.ReadLine();
var msg = Encoding.UTF8.GetBytes(input);
redisClient.LPush("hello", msg);
} while (input.Trim().ToLower() != "exit");
消費者代碼
while (true)
{
RedisClient redisClient = new RedisClient("localhost:6379");
byte[] reads = redisClient.BRPopValue("hello",60);
Console.WriteLine($"獲取到消息爲:{Encoding.UTF8.GetString(reads)}");
}
然後通過命令或exe啓動兩個消費者
可以看到消息被兩個消費者端通過輪詢的方式給分攤掉了。
即使使用了BRpop阻塞獲取隊列,如果隊列長時間沒有數據,最終還是會拋異常,我們應該捕獲異常,並第一時間重試