最近学习RabbitMQ C# 遇到几个问题请教一下大神
Publish/Subscribe
Routing
Topics
这三中队列消费消息问题,
官方给出的消费消息的方法中间都有一个获取与Exchange随机绑定的队列名称,
var queueName = channel.QueueDeclare().QueueName;
然后消费这个队列的消息消费队列中的消息
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
var routingKey = ea.RoutingKey;
Console.WriteLine(" [x] Received '{0}':'{1}'",
routingKey,
message);
};
例如:Publish/Subscribe 模式
如果C1 断掉之后重新消费怎么保证消费的是之前的队列
而不会消费到其他队列中未消费的消息
PS: 使用事件驱动
var factory = new ConnectionFactory() { HostName = "localhost" };
using(var connection = factory.CreateConnection())
using(var channel = connection.CreateModel())
{
channel.ExchangeDeclare(exchange: "logs", type: "fanout");
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queue: queueName,
exchange: "logs",
routingKey: "");
Console.WriteLine(" [*] Waiting for logs.");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] {0}", message);
};
channel.BasicConsume(queue: queueName,
noAck: true,
consumer: consumer);
Console.WriteLine(" Press [enter] to exit.");
Console.ReadLine();
}
官方给出的示例
我就是不明白 如果消费者在线的时候消费完一波消息后,再次登录重新消费,怎么保持和之前消费的队列一样
我一般用的时候都是先在mq上建好exchange和队列及其关系。代码中仅处理发送消费的场景。
我就是不明白 如果消费者在线的时候消费完一波消息后,再次登录重新消费,怎么保持就是之前消费的队列
是不是我对 消息队列的理解有问题。
@挨踢新手: 我的意思是我在使用前,直接在mq上建好相关信息,比如
exchange1--->queue1
生产者直接在代码中往exchange1中发数据,消费者直接从queue1中取数据。你所谓的再登录是指消费者重启么?如果是这个也没任何关系,反正queue1这个作为常量已经写到配置或代码中了,所以每次连mq都是从相同地方取数据。
@Daniel Cai:
关于消费者消费消息,官方给出的示例中
工作队列是你说的这样的逻辑
channel.QueueDeclare(queue: "task_queue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
Console.WriteLine(" [*] Waiting for messages.");
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);
};
可是消息模式分多种的:
Publish/Subscribe
channel.ExchangeDeclare(exchange: "logs", type: "fanout");
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queue: queueName,
exchange: "logs",
routingKey: "");
Console.WriteLine(" [*] Waiting for logs.");
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] {0}", message);
};
channel.BasicConsume(queue: queueName,
noAck: true,
consumer: consumer);
Routing、Topics(类似消息推送,只是需要绑定Bind)
这种消费方式好像没有直接指导具体的队列上。
也就是说获取了 与指定交换机按照指定的路由绑定在一起的队列集合,中的随机一个队列进行消费,
也就是这段代码 var queueName = channel.QueueDeclare().QueueName;
这个才是消费者最终消费的队列。
不知道我理解的是否正确。
那么问题来了:
如果是工作队列,那就我目前看到官方的资料,消费者是不关心交换机和路由的 而是直接指定某个队列。
但是如果是Publish/Subscribe、Routing、Topics模式的 好像不直接指定队列名称而是按某种交换机路由约定,获得一个随机队列进行消费。这个时候如果消费者重启,那么消费的队列还是之前消费的队列吗?
我对消息队列理解不深刻,请多多赐教
@挨踢新手: rabbitmq中exchange三种模式,fanout(字面理解,所以你消费方创建一个队列并绑定到这个exchange后是可以接受到消息,就算你下线等你再次上线创建新队列时也可以收到从这个exchange发送过来的消息,但之前的消息则不在你新建的这个队列中,这种模式下routingkey被忽略)
direct(字面理解,指哪打哪,根据routingkey走)
topic(这个可实现的功能较前者多,包括根据routingkey做匹配进行路由)
在消费者看来,的确它只关心队列,前面乱七八糟的都不归它管,而且这个跟push/pull模式也没关系
在生产者看来,严格意义上我感觉是全部都要关系,走哪个exchange,怎么路由,到哪个队列。
你的实例代码刚好也就是fanout的一种形式,根据上面所说的,这个q和exchange只需要存在绑定关系即可,后面发送过来的消息会发送到这个exchange下绑定的队列中。
@Daniel Cai:
多谢你耐心回复。
按照你这样的方式,是不是说任何消费者都是只关心队列。而不关心Exchange,routingKey
一个消费者消费消息时必须指定一个队列名?以确定消费的是哪个队列。
那我是不是应该这样理解。
简单的消息队列是不是针对每个消费者都会有唯一的一个队列。
当一个队列被多个消费者消费的时候,队列中的消息一般应该只被消费一次。对吧。
例如:消息推送模式,是不是每个消费者都会有单独的一个队列。
不知道我理解的正确吗?
另外
var queueName = channel.QueueDeclare().QueueName;
官方示例的这段代码会生成
amq.gen-7L4bwv57iTlt_JIuirn_aQ 类似于这样子的随机队列名
这个队列有什么用处吗?
@挨踢新手: 消费者只关心队列,这个和你不关心你买的手机具体怎么制造的一样的逻辑。
消费者当然需要指明队列名,我都不知道你要买啥我给你什么?
消息队列不一定是一个队列一个消费者,一般会有多个消费者挂在同一队列后,一般用于处理异步请求并方便横向扩展。
一个队列中消息被消费时正常情况下只被一个消费者消费,但是如果你队列设为需要ack而你消费者没有正常ack的话这条消息是可以被其他消费者消费的。但autoack方式下是不存在这个问题。
这种随机名字就是你建的队列的名称啊,比如在你前面例子的fanout模式下,你消费者主动创建队列并完成绑定,那么你就需要考虑如果后面服务重启这种情况,你应该将这个q的name持久化下来避免重启后丢消息(消息实际上没有丢,只是你重新创建了队列,新队列中只有新接收到的消息,而之前的消息会在你fanout对应的队列中继续留存)。
明白了,
万分感谢
每个队列是不是有个token 消费者是通过token去消费对应的队列。
队列Token?我没找到有这方面的说明呀。官方提供的和再网上搜索的,都没有提到过这个呀。
我也是学习RabbitMQ的,有没有这方面的资料?