RabbitMQ官网教程2——工作队列

        第一节中我们实现了简单的发送接收消息。现在我们创建一个工作队列,用于在多个worker中分配耗时任务。工作队列是为了避免立即执行资源密集型任务并等待其完成,有了工作队列,就可以稍后再处理任务。我们把任务封装成消息并发送到队列,后台的工作进程从队列中取出任务并执行。当有多个工作进程时,任务会在它们中分发。

        web app中,在一个很短的http请求时长内不可能处理一个复杂的任务,这时工作队列就很有用。

 

准备

        用字符串模拟复杂任务,通过time.sleep()函数模拟进程繁忙,字符串中的点表示任务复杂度。

        修改上节的发送程序,实现从命令行输入消息并发送到队列。

import sys

message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(exchange='',
                  routing_key='task_queue',
                  body=message,
                  properties=pika.BasicProperties(
                     delivery_mode = 2, # make message persistent
                  ))
print(" [x] Sent %r" % message)

        接收程序中增加延时。

import time

def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    time.sleep(body.count(b'.'))
print(" [x] Done")

轮询分发

        使用任务队列的一个优点就是方便水平扩展。如果系统负载过大就可以增加新的工作进程即可。

        默认情况下,Rabbitmq会顺序的将消息发给下一个消费者,这种分发方式就是round-robin。平均下来每个消费者会获得等量的消息。

 

消息确认

        执行任务可能需要一点时间。如果消费者在执行任务时挂掉,任务只执行了一部分,这时会怎样呢?当前的代码中,rabbitmq投递消息到消费者后就立即把消息删除。这样,如果工作进程被杀掉,正在处理的消息就会丢失,包括那些分配给该进程但还没来得及处理的消息也会丢失。

        我们不希望丢失任何消息,如果一个worker挂掉,我们希望把消息发送给另一个worker。

        为了保证消息不丢失,rabbitmq支持消息确认。消费者会发送ack消息确认给rabbitmq,通知它消息已收到并处理,rabbitmq可以删除它。

        如果消费者在发送ack前挂掉(通道关闭、连接关闭、tcp连接丢失),rabbitmq认为消息未被完全处理,并将消息重新入队,如果存在其他消费者,就将消息重新投递到其他消费者。这样就保证消息不丢失。

        这里没有超时限制,消费者挂掉,rabbitmq就重新投递消息。消息处理需要很长时间时也可以很好的支持。

        默认情况下消息确认是开启的。前面的例子我们通过no_ack=True显式关闭了。现在我们需要去掉该标志,任务处理完发送消息确认。

def callback(ch, method, properties, body):
    print " [x] Received %r" % (body,)
    time.sleep( body.count('.') )
    print " [x] Done"
    ch.basic_ack(delivery_tag = method.delivery_tag)

channel.basic_consume(callback, queue='hello')

        以上代码可以保证当工作进程处理消息时被杀掉,不会丢消息,一旦程序挂掉所有未确认的消息都会被重新投递。

        忘记确认是一个非常容易犯的常见错误,结果很严重。rabbitmq会占用越来越多的内存因为它不能释放未确认的消息。要调试这种错误,可以使用rabbitmqctl打印未确认消息:

        sudo rabbitmqctl list_queue namemessages_ready messages_unacknowledged

 

消息持久化

        我们知道了如何在消费者挂掉的情况下确保不丢消息,但是如果rabbitmq服务挂了,消息还是会丢失,RabbitMQ不会记住队列及消息,除非我们设置它。要确保不丢消息,需要把队列和消息都设为可持久化durable。

        RabbitMQ中已经存在一个非durable的队列hello,以durable参数再次创建该队列并不生效。RabbitMQ不允许用不同的参数重定义一个已经存在的队列,会返回错误。

        注意,将消息标记为持久化并不能完全保证消息不丢失。虽然该标志通知RabbitMQ将消息保存到磁盘,但是在RabbitMQ收到消息但还未保存前还是有一个小小的时间窗。而且RabbitMQ并不对每个消息都做fsync操作,可能消息还只是保存在缓存中,并未真正写入磁盘。但是这种持久化对简单队列来讲已经足够了,如果需要强持久性,可以使用publisher confirms。

 

公平分发

        轮询分发有个问题,假如有两个消费者,且所有奇数消息都很繁重,所有偶数消息都很轻量,那么一个消费者就会一直很繁忙而另一个则几乎没什么工作量。RabbitMQ则对此并不知道。

        这是因为,在消息进入队列时RabbitMQ就分发消息,它并不看消费者的未确认消息数量,只是盲目的把第n个消息分配给第n个消费者。

        为防止这种情况,我们可以使用basic.qos方法,参数prefetch_count=1。这是通知RabbitMQ每次只给消费者一个消息。换句话说,在消费者处理完消息发送确认之前不再给他分发新消息,而是把消息分发给那些不繁忙的消费者。

        当然,如果所有的消费者都繁忙,队列可能会满,这是应该考虑增加消费者或其它策略。


 最终程序

        发送端:

import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

channel.queue_declare(queue='task_queue', durable=True)

message = ' '.join(sys.argv[1:]) or "Hello World!"
channel.basic_publish(exchange='',
                  routing_key='task_queue',
                  body=message,
                  properties=pika.BasicProperties(
                         delivery_mode = 2, # make message persistent
                  ))
print(" [x] Sent %r" % message)
connection.close()

        接收端:

import pika
import time

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

channel.queue_declare(queue='task_queue', durable=True)
print(' [*] Waiting for messages. To exit press CTRL+C')

def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    time.sleep(body.count(b'.'))
    print(" [x] Done")
    ch.basic_ack(delivery_tag = method.delivery_tag)

channel.basic_qos(prefetch_count=1)
channel.basic_consume(callback, queue='task_queue')

channel.start_consuming()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在OpenStack中,RabbitMQ是消息代理的核心组件,负责处理各种消息,包括API请求、队列通信和通知。为了保证高可用性和可扩展性,我们可以将RabbitMQ部署为集群。 以下是在OpenStack中部署RabbitMQ集群的步骤: 1. 安装RabbitMQ软件包 在每个节点上安装RabbitMQ软件包。可以使用以下命令进行安装: ``` sudo apt-get install rabbitmq-server ``` 2. 配置RabbitMQ 在每个节点上,编辑RabbitMQ配置文件/etc/rabbitmq/rabbitmq-env.conf,指定以下环境变量: ``` NODE_IP_ADDRESS=<本地IP> NODE_PORT=5672 CLUSTER_WITH=<其他节点IP> ``` 其中,NODE_IP_ADDRESS是本地IP地址,CLUSTER_WITH是其他节点的IP地址。这些变量将用于RabbitMQ节点间通信。 3. 启用RabbitMQ插件 在每个节点上启用RabbitMQ集群插件。可以使用以下命令启用插件: ``` sudo rabbitmq-plugins enable rabbitmq_management rabbitmq_peer_discovery_aws ``` 4. 启动RabbitMQ节点 在每个节点上启动RabbitMQ节点。可以使用以下命令启动节点: ``` sudo rabbitmq-server -detached ``` 5. 将节点加入集群 在任何一个节点上,执行以下命令将节点加入集群: ``` sudo rabbitmqctl stop_app sudo rabbitmqctl join_cluster rabbit@<其他节点名称> sudo rabbitmqctl start_app ``` 其中,<其他节点名称>是集群中的其他节点的名称。 6. 验证集群状态 在任何一个节点上,执行以下命令验证集群状态: ``` sudo rabbitmqctl cluster_status ``` 如果输出包含所有节点的信息,则集群已成功部署。 注意:在部署RabbitMQ集群时,需要确保集群节点之间的网络连接正常,并且防火墙已正确配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值