RabitMQ---Work Queues

1. 消息确认

RabitMQ是如何做到消息永远不会丢失?

处理(从发送到接收)一个消息需要花费几秒。如果当消费者(consumers)开始了一个长时间的任务并且处理了一部分就死掉了会发生什么呢?

如果没有消息确认机制,一旦RabitMQ传递了一个消息到消费者,就会立即标记为删除状态。在这种情况下,如果你kill掉一个consumer,该消息就会在处理过程中丢失。也会丢失所有派遣到该consumer的所有还没有被处理的消息。

显然,我们是不愿意丢失任何消息的,希望在一个consumer死了以后,消息还能继续被传递给其他consumer.

为了确保消息永远不会丢失,RabitMQ有消息确认机制(ack)。

ack是consumer发送给RabitMQ,并且告诉RabitMQ该消息已经收到被处理了,可以免责的删掉了。如果一个consumer死了(channer关闭了,或者connection关闭了,或者tcp 连接丢失了)就不会有ACK发送给RabitMQ,此时RabitMQ就能理解该消息没有被完全处理需要重新排队处理。如果此时刚好有其他consumers在线,RabitMQ会将该消息传递给其他的Consumers。这种方式下不会有消息丢失,即便有consumer偶然的死掉。

RabitMQ里面没有消息超时。当consumer死掉以后RabitMQ会重发该消息。即便是处理一个消息非常耗时也是没问题的。

2. 如果忘了发送ack怎么办?

忘记发送ack是常见的错误。这个错误很容易犯,但是导致的结果却是很严重的。虽然当客户端退出后RabitMQ会重发这些消息,但是RabitMQ会吃掉更多的内存,因为不能释放任何未被确认的消息。

为了调试该错误,可以使用如下命令查看:

在linux系统下

sudo rabbitmqctl list_queues name messages_ready messages_unacknowledged

在windows系统下

rabbitmqctl.bat list_queues name messages_ready messages_unacknowledged

3. 消息持久性

通过前面第一点这个知识,你已经跟我一样知道了怎样确保当consumer死掉以后保证消息不会丢失。但是任务也总有丢失的时候如果RabitMQ服务停止了。

当RabitMQ退出或者崩溃时,它将忘记队列和消息,除非你告诉他。有两件事能确保消息不丢失:需要将队列和消息标记为持久化的。

1.首先,需要确保RabitMQ永远不会丢失消息队列。因此需要将队列声明为durable.对于已经存在的queue,RabitMQ不允许重定义queue里的参数,如果重新定义会返回error.解决办法是定义一个不同name的queue.durable属性需要同时应用于producer和consumer。一旦声明为了durable,那么即使RabitMQ重启该queue也不会丢失。

q, err := ch.QueueDeclare(
  "task_queue", // name
  true,         // durable
  false,        // delete when unused
  false,        // exclusive
  false,        // no-wait
  nil,          // arguments
)
failOnError(err, "Failed to declare a queue")

2. 标记   DeliveryMode为presistent

err = ch.Publish(
  "",           // exchange
  q.Name,       // routing key
  false,        // mandatory
  false,
  amqp.Publishing {
    DeliveryMode: amqp.Persistent,
    ContentType:  "text/plain",
    Body:         []byte(body),
})

注意:DeliveryMode属性=presistent并不能保证消息不会被丢失,仅仅只是告诉RabitMQ存储该消息到磁盘,当RabitMQ已经接收消息还没有保存的时候还是有一个短暂的时间窗口。RabitMQ有可能只是将消息存储在cache并不是真的写到磁盘。虽然这个persistent并不是很强,但足够支撑我们这些简单的任务队列。

4.公平调度

你可能已经注意到了,任务调度仍然不是按照我们期望的那样。例如,在某个情况下有两个consumers,所有奇数消息很大, 而偶数消息很小,其中一个consumer可能一直都在忙着处理消息而另一个consumer则几乎没什么工作。在这种情况下,RabitMQ是不知情的仍然会均匀发送消息。

这种情况之所以发生是由于一旦一个message进入了queue就会被RabitMQ调度,它不会查看某个consumer未确认的消息数量,仅仅是盲目的调度将第n个消息传递给第n个consumer.

设置预取数量:

为了避免这种现象,我们可以设置预取数量为1.这个告诉RabitMQ不要一次性给一个consumer超过1个消息。或者,换句话说,直到一个consumer处理了消息并且确认了前一个消息才调度消息给该consumer.如果没有处理完前一个消息也没有发送确认,就应该把这个消息调度给其他不忙的consumer.

err = ch.Qos(
  1,     // prefetch count
  0,     // prefetch size
  false, // global
)
failOnError(err, "Failed to set QoS")

注意:队列大小。如果所有的consumers都在忙,那么queue可能会填满。此时你可能就要注意到队列大小了,可能需要添加更多的consumers或者采取其他策略。

总结:使用消息确认和预取计数可以设置工作队列。durable属性可以让任务存活即使RabitMQ重启。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值