RoutingKey
每個項目都需要記錄日志,日志則一般會分為多種級別,常見的是
Info、debug、warn、Error
對於前三種日志,在項目運行中會產生大量的消息,但是一般多數情況下是不會用到的即時性不高,而error則不同,對於error級別的消息需要迅速通知開發人員去修改項目中的錯誤
使用RabbitMq應該怎么設計? 可以分析一下
首先我們需要一個exchange,對於即時性不高的日志速度慢一些是沒有關系的,所以可以把它們放到一個隊列中,而針對error級別即時性較高的需要放到一個單獨的隊列中。在之前我們是在消息生產者中去聲明exchange、queue以及它們的綁定關系,這顯然是不對的。
對於消息發布者而言它只負責把消息發布出去,甚至它也不知道消息是發到哪個queue,消息通過exchange到達queue,exchange的職責非常簡單,就是一邊接收發布者的消息一邊把這些消息推到queue中。
而exchange是怎么知道消息應該推到哪個queue呢,這就要通過綁定queue與exchange時的routingkey了,通過代碼進行綁定並且指定routingkey,下面有一張關系圖,p(發布者) ---> x(exchange) bindding(綁定關系也就是我們的routingkey) 紅色代表着queue
在第一個例子中我們並沒有聲明exchange而是使用是默認的,在發布的時候routingkey則是使用的隊列名,事實上如果沒有指定routingkey隊列的名稱就是routingkey
//聲明隊列 channel.QueueDeclare("firstTest", true, false, false, null); //發布消息 var msg = Encoding.UTF8.GetBytes("Hello RabbitMQ"); channel.BasicPublish(string.Empty, routingKey: "firstTest", basicProperties: null, body: msg);
這時可以繼續回到我們日志記錄了,info、warn、debug 我希望放到Log_else隊列中,它們的routingkey分別是info、warn、debug,errror級別的日志我希望放到Log_error隊列中,它的routingkey是error。這時可以去編寫代碼了
在發布者中的代碼中我把聲明exchange、queue與它們的綁定關系移除掉了,放到了consumber中,,方便演示這里的日志級別只有info、debug、error。下面分別生成了對應的消息
//創建返回一個新的頻道 using (var channel = RabbitMqHelper.GetConnection().CreateModel()) { //發布一百個消息 for (var i = 0; i < 100; i++) { //對i進行求余來決定日志的級別 var routingkey = i % 2 == 0 ? "info" : i % 3 == 0 ? "debug" : "error"; var msg = Encoding.UTF8.GetBytes($"{i} :{routingkey}Message"); channel.BasicPublish("LogExchange", routingKey: routingkey, basicProperties: null, body: msg); } Console.Write("發布成功!"); }
對於consumer而言,這里根據控制台傳入的級別創建不同的queue與它們與exchange的關系
bool flag = true; string level = ""; while (flag) { Console.WriteLine("請指定要接收的消息級別"); level = Console.ReadLine(); if (level == "info" || level == "error" || level == "debug") flag = false; else Console.Write("僅支持info、debug與error級別"); } using (var channel = RabbitMqHelper.GetConnection().CreateModel()) { //聲明交換機 direct模式 channel.ExchangeDeclare("LogExchange", "direct", true, false, null); //根據聲明使用的隊列 var queueName = level == "info" ? "Log_else" : level == "debug" ? "Log_else" : "Log_error"; channel.QueueDeclare(queueName, true, false, false, null); //進行綁定 channel.QueueBind(queueName, "LogExchange", level, null); //創建consumbers var consumer = new EventingBasicConsumer(channel); consumer.Received += (sender, e) => { var msg = Encoding.UTF8.GetString(e.Body); Console.WriteLine(msg); }; //進行消費 channel.BasicConsume(queueName, true, consumer); Console.ReadKey(); }
運行了三個consumer,指定三個消息級別
打開Web工具,可以看到已經有了log_else與log_error兩種queue。
還有它們的綁定關系
運行消息發布者,這時候就可以看到三個consumer已經在消費了,error的consumer只消費了log_error隊列中的消息、其它兩個consumer消費的是log_else中的消息