C# 操作rabbitmq(一)

更多资料参考 http://www.rabbitmq.com/tutorials/tutorial-one-dotnet.html

一、简单的Helloworld

这里写图片描述

 #region pulisher

{
      var factory = new ConnectionFactory() { HostName = "localhost", UserName = "jesen", Password = "jesen", Port = 5672 };
      using (var connection = factory.CreateConnection())
      {
          while (true)
          {
              var sendMessage = Console.ReadLine();
              if (sendMessage.Equals("exit")) return;
              using (var channel = connection.CreateModel())
              {
                  channel.QueueDeclare(queue: "helloworld", durable: false, exclusive: false, autoDelete: false, arguments: null); //声明一个队列

                  string message = sendMessage;
                  var body = Encoding.UTF8.GetBytes(message);
                  channel.BasicPublish(exchange: "", routingKey: "helloworld", basicProperties: null, body: body);

                  Console.WriteLine($"send message : {message}");
              }
        }            
    }
}

 #endregion

#region consumer
{
    var factory = new ConnectionFactory() { HostName = "localhost", UserName = "jesen", Password = "jesen", Port = 5672 };

    using (var connection = factory.CreateConnection())
    {
          using (var channel = connection.CreateModel())
          {
              channel.QueueDeclare(queue: "helloworld",
                                   durable: false,
                                   exclusive: false,
                                   autoDelete: false,
                                   arguments: null);

              var consumer = new EventingBasicConsumer(channel);

              consumer.Received += (model, ea) =>
              {
                   var body = ea.Body;
                   var message = Encoding.UTF8.GetString(body);
                   Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00") } : Received Message {message}");
              };

                   channel.BasicConsume(queue: "helloworld",
                                        autoAck: true,
                                        consumer: consumer);

                   Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00") } : Press [enter] to exit.");

                   Console.ReadLine();
           }
     }
}

#endregion

二、 Work Queue 在多个workers中分发耗时的任务

这里写图片描述

//publisher
{
    var factory = new ConnectionFactory() { HostName = "localhost", UserName = "jesen", Password = "jesen", Port = 5672 };
     using (var connection = factory.CreateConnection())
     {
         while (true)
         {
             var sendMessage = Console.ReadLine(); //命令行已空格隔开,加...模拟将要耗时的时间
             if (sendMessage.Equals("exit")) return;
             using (var channel = connection.CreateModel())
             {
                 var message = sendMessage;
                 var body = Encoding.UTF8.GetBytes(message);

                 var properties = channel.CreateBasicProperties();
                 properties.Persistent = true;

                 channel.BasicPublish(exchange: "",
                                      routingKey: "task_queue",
                                      basicProperties: properties,
                                      body: body);

                 Console.WriteLine($"send message : {message}");
             }
         }

     }
}

//consumer
{
 //var factory = new ConnectionFactory() { HostName = "localhost", UserName = "jesen", Password = "jesen", Port = 5672 };

     using (var connection = factory.CreateConnection())
     {
         using (var channel = connection.CreateModel())
         {
             channel.QueueDeclare(queue: "task_queue",
                                  durable: false,
                                  exclusive: false,
                                  autoDelete: false,
                                  arguments: null);

             var consumer = new EventingBasicConsumer(channel);

             consumer.Received += (model, ea) =>
             {
                 var body = ea.Body;
                 var message = Encoding.UTF8.GetString(body);
                 Console.WriteLine(" [x] Received {0}", message);

                 int dots = message.Split('.').Length - 1;  //模拟耗时任务
                 Thread.Sleep(dots * 1000);

                 Console.WriteLine(" [x] Done");
             };

             channel.BasicConsume(queue: "task_queue", autoAck: true, consumer: consumer);
             Console.ReadLine();
         }
     }
}

打开两个Consumer端,查看结果,可以看到分发是按循环分发的,即给一个consumer1,接下来给consumer2,再给consumer1,再给consumer2 ….这样如果consumber1处理耗时很长的任务,而consumer2处理很快,就得等consumer处理完,才能获取到下个task,而且如果有一个挂掉了,rabbitmq发给消费折后就把队列的消息删除了,因此消息也丢失了。很不合适业务
这里写图片描述
这里写图片描述
这里写图片描述

为了解决上述问题,rabbitmq引入了消息确认机制 Message acknowledgment
修改上面消费者的代码,可以确保即使工作中的消费者挂掉了,消息也不会丢失,可以继续分发,消息确认回复必须是在同一个channel里面,在不同的channel回复确认会抛异常

var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
    var body = ea.Body;
    var message = Encoding.UTF8.GetString(body);
    Console.WriteLine(" [x] Received {0}", message);

    int dots = message.Split('.').Length - 1;
    Thread.Sleep(dots * 1000);

    Console.WriteLine(" [x] Done");

    channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
};
channel.BasicConsume(queue: "task_queue", autoAck: false, consumer: consumer);

虽然上面的确认机制可以消费者挂掉不丢失数据,可是如果rabbitmq服务器挂掉了呢?
rabbitmq提供了消息持久化机制,Message Durablity
rabbitmq不允许重复声明已经存在的队列,因此设置的时候必须是第一次声明队列的时候

//确保rabbitmq服务器挂掉不丢失数据
channel.QueueDeclare(queue: "task_queue",
                     durable: true,
                     exclusive: false,
                     autoDelete: false,
                     arguments: null);

//确保rabbitmq服务器重启不丢失数据
var properties = channel.CreateBasicProperties();
properties.Persistent = true;

合理的分发 Fair Dispatch

//下面的代码告诉rabbitmq一次只给同一个worker一条消息,也就是说要等到接收到确认消息时再分发消息给worker,相反的,将消息分发给下一个处于空闲的worker进行处理。
channel.BasicQos(0, 1, false);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值