由於公司業務需求,最近想上RabbitMQ,之前我研究了一段時間微軟的MSMQ。開源佇列有很多,各有優劣。就先拿RabbitMQ練練手吧。本篇著重程式碼部分,至於怎麼安裝,怎麼配置不在贅述。而且程式碼是在RabbitMQ.NET Client 類庫基礎上實現。
假設閱讀本文的人已經安裝好RabbitMQ並且做了相應的使用者配置。而且專案中已經從nuget安裝了rabbitmq.client.dll.我們開始做一個簡單的佇列傳送和接收訊息。
- 將需要配置的東西放在配置檔案裡,例如主機地址,埠,使用者名稱,密碼等。
- 實現訊息傳送端:Product
- 實現訊息接收端:Customer
- Demo測試
將以下內容作為可配置部分放在配置檔案中
<appSettings> <!--RabbitMQ--> <add key="RabbitMQ_HostUri" value="amqp://192.168.1.119:5672/"/> <add key="RabbitMQ_HostName" value="192.168.1.119"/> <add key="RabbitMQ_UserName" value="test_user"/> <add key="RabbitMQ_Password" value="123456"/> <add key="RabbitMQ_VirtualHost" value="ms_mq"/> </appSettings>
由於只是對RabbitMQ.Client.dll中的又一次封裝,所以程式碼不過多解釋,其中要注意的就是某些配置問題,例如是否持久化,訊息處理模式是怎麼樣的等等。
首先我們建立一個連線工廠:
public ConnectionFactory CreateFactory() { if (_factory == null) { const ushort heartbeat = 0; //主機地址 Uri uri = new Uri(RabbitMQConfig.HostUri); _factory = new ConnectionFactory(); //_factory.HostName = RabbitMQConfig.HostName; //使用者名稱 _factory.UserName = RabbitMQConfig.UserName; //密碼 _factory.Password = RabbitMQConfig.PassWord; //虛擬主機名 _factory.VirtualHost = RabbitMQConfig.VirtualHost; //連線終端 _factory.Endpoint = new AmqpTcpEndpoint(uri); _factory.RequestedHeartbeat = heartbeat; //自動重連 _factory.AutomaticRecoveryEnabled = true; } return _factory; }
一個簡單的訊息釋出:(對程式碼研究不夠透徹,只能一切從簡~~)
public void Publish(string message, string queueName=null) { if (queueName == null) { queueName = _queueName; } var factory = RabbitMQFactory.Instance.CreateFactory(); using (var connection = factory.CreateConnection()) { using (var model = connection.CreateModel()) { //訊息持久化,防止丟失 model.QueueDeclare(queueName, RabbitMQConfig.IsDurable, false, false, null); var properties = model.CreateBasicProperties(); properties.Persistent = RabbitMQConfig.IsDurable; properties.DeliveryMode = 2; //訊息轉換為二進位制 var msgBody = Encoding.UTF8.GetBytes(message); //訊息發出到佇列 model.BasicPublish("", queueName, properties, msgBody); } } }
訊息接收:
public void Consume() { var factory = RabbitMQFactory.Instance.CreateFactory(); var connection = factory.CreateConnection(); connection.ConnectionShutdown += Connection_ConnectionShutdown; ListenChannel = connection.CreateModel(); bool autoDeleteMessage = false; var queue = ListenChannel.QueueDeclare(_queueName, RabbitMQConfig.IsDurable, false, false, null); //公平分發,不要同一時間給一個工作者傳送多於一個訊息 ListenChannel.BasicQos(0, 1, false); //建立事件驅動的消費者型別,不要用下邊的死迴圈來消費訊息 var consumer = new EventingBasicConsumer(ListenChannel); consumer.Received += Consumer_Received; //消費訊息 ListenChannel.BasicConsume(_queueName, autoDeleteMessage, consumer); }
我在Customer中定義了一個 ReceiveMessageCallback Func回撥,這裡就是當客戶端從佇列接收到訊息之後,怎麼處理由客戶端來決定
public Func<string, bool> ReceiveMessageCallback { get; set; }
處理訊息:
private void Consumer_Received(object sender, BasicDeliverEventArgs args) { try { var body = args.Body; var message = Encoding.UTF8.GetString(body); //將訊息業務處理交給外部業務 bool result = ReceiveMessageCallback(message); if (result) { if (ListenChannel != null && !ListenChannel.IsClosed) { ListenChannel.BasicAck(args.DeliveryTag, false); } } else { } } catch (Exception ex) { throw ex; } }
基本程式碼已經完成,我們寫一個測試,訊息傳送端:
static void Main(string[] args) { var testQueueName = "test"; IMessageProduct product = new MessageProduct(testQueueName); for (int i = 0; i < 10000; i++) { Console.WriteLine("正在傳送第" + i + "條訊息..."); product.Publish("訊息體" + i); } Console.Read(); }
訊息接收端:(開多個口接收)
static void Main(string[] args) { Parallel.For(0, RabbitMQConfig.ThreadCount, i => { IMessageCustomer customer = new MessageCustomer("test");
//開始監聽 customer.StartListening(); customer.ReceiveMessageCallback = message => {
//客戶端處理訊息(列印) Console.WriteLine("接收到訊息:" + message); return true; }; }); Console.Read(); }
開啟傳送訊息端:
開啟訊息接收端:
到此為止,RabbitMQ佇列的簡單測試就完成了,沒有介紹什麼新知識,基本就是套DLL中的方法,不過也有很多不合理的地方,如果真正應用到專案中,還需要多加測試和修改。