更多资料参考 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);