rabbitmq使用方法(二)

Work Queues

In the first tutorial we wrote programs to send and receive messages from a named queue. In this one we‘ll create a Work Queue that will be used to distribute time-consuming tasks among multiple workers.

The main idea behind Work Queues (aka: Task Queues) is to avoid doing a resource-intensive task immediately and having to wait for it to complete. Instead we schedule the task to be done later. We encapsulate a task as a message and send it to the queue. A worker process running in the background will pop the tasks and eventually execute the job. When you run many workers the tasks will be shared between them.

Round-robin dispatching

One of the advantages of using a Task Queue is the ability to easily parallelise work. If we are building up a backlog of work, we can just add more workers and that way, scale easily.

By default, RabbitMQ will send each message to the next consumer, in sequence. On average every consumer will get the same number of messages. This way of distributing messages is called round-robin.

Message acknowledgment

Doing a task can take a few seconds. You may wonder what happens if one of the consumers starts a long task and dies with it only partly done. With our current code once RabbitMQ delivers message to the customer it immediately removes it from memory. In this case, if you kill a worker we will lose the message it was just processing. We‘ll also lose all the messages that were dispatched to this particular worker but were not yet handled.

But we don‘t want to lose any tasks. If a worker dies, we‘d like the task to be delivered to another worker.

In order to make sure a message is never lost, RabbitMQ supports message acknowledgments. An ack(nowledgement) is sent back from the consumer to tell RabbitMQ that a particular message had been received, processed and that RabbitMQ is free to delete it.

If consumer dies without sending an ack, RabbitMQ will understand that a message wasn‘t processed fully and will redeliver it to another consumer. That way you can be sure that no message is lost, even if the workers occasionally die.

There aren‘t any message timeouts; RabbitMQ will redeliver the message only when the worker connection dies. It‘s fine even if processing a message takes a very, very long time.

Message durability

We have learned how to make sure that even if the consumer dies, the task isn‘t lost. But our tasks will still be lost if RabbitMQ server stops.

When RabbitMQ quits or crashes it will forget the queues and messages unless you tell it not to. Two things are required to make sure that messages aren‘t lost: we need to mark both the queue and messages as durable.

First, we need to make sure that RabbitMQ will never lose our queue. In order to do so, we need to declare it as durable:

1 channel.queue_declare(queue=‘hello‘, durable=True)

Although this command is correct by itself, it won‘t work in our setup. That‘s because we‘ve already defined a queue called hello which is not durable. RabbitMQ doesn‘t allow you to redefine an existing queue with different parameters and will return an error to any program that tries to do that. But there is a quick workaround - let‘s declare a queue with different name

This queue_declare change needs to be applied to both the producer and consumer code.

At that point we‘re sure that the task_queue queue won‘t be lost even if RabbitMQ restarts. Now we need to mark our messages as persistent - by supplying a delivery_mode property with a value 2.

1 channel.basic_publish(exchange=‘‘,
2                       routing_key="task_queue",
3                       body=message,
4                       properties=pika.BasicProperties(
5                          delivery_mode = 2, # make message persistent
6                       ))

Fair dispatch

You might have noticed that the dispatching still doesn‘t work exactly as we want. For example in a situation with two workers, when all odd messages are heavy and even messages are light, one worker will be constantly busy and the other one will do hardly any work. Well, RabbitMQ doesn‘t know anything about that and will still dispatch messages evenly.

This happens because RabbitMQ just dispatches a message when the message enters the queue. It doesn‘t look at the number of unacknowledged messages for a consumer. It just blindly dispatches every n-th message to the n-th consumer.

In order to defeat that we can use the basic.qos method with the prefetch_count=1 setting. This tells RabbitMQ not to give more than one message to a worker at a time. Or, in other words, don‘t dispatch a new message to a worker until it has processed and acknowledged the previous one. Instead, it will dispatch it to the next worker that is not still busy.

1 channel.basic_qos(prefetch_count=1)

Putting it all together

Final code of our new_task.py script:

 1 #!/usr/bin/env python
 2 import pika
 3 import sys
 4
 5 connection = pika.BlockingConnection(pika.ConnectionParameters(
 6         host=‘localhost‘))
 7 channel = connection.channel()
 8
 9 channel.queue_declare(queue=‘task_queue‘, durable=True)
10
11 message = ‘ ‘.join(sys.argv[1:]) or "Hello World!"
12 channel.basic_publish(exchange=‘‘,
13                       routing_key=‘task_queue‘,
14                       body=message,
15                       properties=pika.BasicProperties(
16                          delivery_mode = 2, # make message persistent
17                       ))
18 print " [x] Sent %r" % (message,)
19 connection.close()

And our worker:(new_task.py source)

 1 #!/usr/bin/env python
 2 import pika
 3 import time
 4
 5 connection = pika.BlockingConnection(pika.ConnectionParameters(
 6         host=‘localhost‘))
 7 channel = connection.channel()
 8
 9 channel.queue_declare(queue=‘task_queue‘, durable=True)
10 print ‘ [*] Waiting for messages. To exit press CTRL+C‘
11
12 def callback(ch, method, properties, body):
13     print " [x] Received %r" % (body,)
14     time.sleep( body.count(‘.‘) )
15     print " [x] Done"
16     ch.basic_ack(delivery_tag = method.delivery_tag)
17
18 channel.basic_qos(prefetch_count=1)
19 channel.basic_consume(callback,
20                       queue=‘task_queue‘)
21
22 channel.start_consuming()
时间: 2024-08-04 02:03:36

rabbitmq使用方法(二)的相关文章

获得执行计划方法二-explain plan

1 安装 $ORACLE_HOME/rdbms/admin/utlxplan.sql (UNIX)  脚本生成一个表这个程序会创建一个名为plan_table的表 2 运行 explain PLAN [ SET STATEMENT_ID [=] < string literal > ] [ INTO < table_name > ] FOR < sql_statement > 其中: STATEMENT_ID:是一个唯一的字符串,把当前执行计划与存储在同一PLAN中的其

java-第十四章-代参的方法(二)-实现MyShopping系统的添加会员功能

 package com.wxws.sms; public class Customer {  int No;  int integarl; } package com.wxws.sms; public class Customers {  Customer[] customers = new Customer[100];  public void add(Customer cust){   for (int i = 0; i <customers.length; i++) {    if (c

java-第十四章-代参的方法(二)-查找会员积分

 package com.wxws.smsB; public class Customer {  int No;  int integarl; } package com.wxws.smsB; public class Customers {  Customer[] customers = new Customer[100];  public void add(Customer cust) {   for (int i = 0; i < customers.length; i++) {    i

Java-第十四章-代参的方法(二)-编程实现,输入班里10名学生的身高,获得身高最高的学生要求对象数组类型方法

package com.ww.yzpA; public class Students { int No; int Height; } package com.ww.yzpA; public class Height { public Students getMaxHeigth(Students[] str) { Students A = new Students(); for (int i = 0; i < str.length; i++) { if (str[i].Height > A.He

1-2+3-4+........+M方法一;方法二

1-2+3-4+......+m=(1-2)+(3-4)+...+[(m-2)-(m-1)]+m=-1+(-1)+(-1)+...+(-1)+m,一共有(m-1)/2个-1相加,再加上m,所以上式等于:(-1)*(m-1)/2+m代码:function sum(m as integer)sum=(-1)*(m-1)/2+mend function 1-2+3-4+........+M方法一:方法二,布布扣,bubuko.com

Javascript 正确使用方法 二

好的,废话不多说,接着上篇来. 变量(variables) 始终使用 var 关键字来定义变量,如果不这样将会导致 变量全局化,造成污染. //bad superPower = new SuperPower(); //good var superPower = new SuperPower(); 使用 一个 var关键字来定义多个变量...并且每个变量一行.. // bad var items = getItems(); var goSportsTeam = true; var dragonba

多台linux无密码访问之方法二

一:实验环境同"多台linux无密码访问之方法一" 二:配置过程 前3步同"多台linux无密码访问之方法一" ★第四步不同于方法一 4.在.ssh目录下创建一个名为config的文件并设置正确权限 manager:~/.ssh # touch config manager:~/.ssh # cat config StrictHostKeyChecking no UserKnownHostsFile /dev/null manager:~/.ssh # chmod

Cocos2d-x 精灵碰撞检測(方法二)

将"Cocos2d-x 精灵碰撞检測(方法一)" update函数改动一下. 使用精灵boundingBox函数获取直接精灵边界框, 不用自己计算精灵矩形大小了,还比較精确,然后调用intersectsRect计算2个精灵矩形是否存在交集. 代码: void HelloWorld::update(float delta) { //返回精灵边界框 CCRect cr1 = sp1->boundingBox(); CCRect cr2 = sp2->boundingBox();

Centos 6.8安装erlang&rabbitmq的方法

Centos 6.8安装erlang&rabbitmq的方法   1. 安装 erlang1.1 准备工作,先安装依赖库和应用工具yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel  rsync perl-net-snmp wx fop  net-snmp unixODBC unixODBC-devel  vim make lrzsz bc sysstat lsof wget xz 1.2 下载e

Cocos2d-x 精灵碰撞检测(方法二)

将"Cocos2d-x 精灵碰撞检测(方法一)" update函数修改一下. 使用精灵boundingBox函数获取直接精灵边界框, 不用自己计算精灵矩形大小了,还比较精确,然后调用intersectsRect计算2个精灵矩形是否存在交集. 代码: void HelloWorld::update(float delta) { //返回精灵边界框 CCRect cr1 = sp1->boundingBox(); CCRect cr2 = sp2->boundingBox();