目录

前文列表

快速入门分布式消息队列之 RabbitMQ(1)

快速入门分布式消息队列之 RabbitMQ(2)

前言

在前文列表中,分别介绍了 RabbitMQ 的对象概念及其关键特性,本篇将通过编程的方式来回溯这些知识点,从实践应用的角度继续深入 RabbitMQ。

安装 RabbitMQ 的 Python 客户端 pika

pip install pika

通道 Channel

在代码实现之前我们还需要补充一个通讯概念——通道

通道 channel:也被称为频道,指在 TCP 连接中建立的虚拟通信渠道。RabbitMQ 客户端与服务器之间的通讯并没有直接使用 TCP 连接,因为每一次通讯都需要创建和销毁 TCP 连接,这对操作系统来说是一笔昂贵的开销。并且 TCP 连接数量是有限制的,会成为通讯性能的瓶颈。解决该问题的方法就是在一个 TCP 连接中创建多个虚拟连接通道,而且通道的数量并没有限制,性能也很好。所以,通常我们只需要在应用程序中维护少量甚至一个 TCP 连接即可满足需求。

一个基本的生产者/消费者实现

消费者

import pika

# 消费者回调任务,在这里定义消费者处理消息数据的逻辑。
def consumer_callback(channel, method, properties, body):
print " [x] Consumed %r" % (body,) # 定义 AMQP URL,这里使用前文中已经创建好的超级管理员 mickey 和虚拟主机 web_app
params = pika.URLParameters('amqp://mickey:passw0rd@localhost:5672/web_app') # 创建与 RabbitMQ 的连接,也称为消息代理连接
conn = pika.BlockingConnection(params) # 新建一个连接中的通道
channel = conn.channel() # 声明一个直连交换机,通过消息路由键和绑定路由键的匹配来完成路由转发策略
# 参数 durable=True, auto_delete=False 表示希望持久化交换机
# 其中 durable=True 表示 RabbitMQ 重启后会自动重建该交换机
channel.exchange_declare(exchange='web', exchange_type='direct',
passive=False, durable=True, auto_delete=False) # 声明一个队列,如果生产者将消息发送给了一个不存在的队列,那么 RabbitMQ 会自动丢弃该消息
channel.queue_declare(queue='app') # 将队列绑定到交换机,并设置一个路由键
channel.queue_bind(queue='app',
exchange='web',
routing_key='web_app_route') # 指定消费者订阅的队列,并且告诉消息代理不需要等待 ACK
channel.basic_consume(consumer_callback, queue='app', no_ack=True) # 开始监听订阅队列,直到 CTRL+C 退出。
try:
print(" [*] Waiting for messages. To exit press CTRL+C")
channel.start_consuming()
except KeyboardInterrupt as err:
channel.stop_consuming() conn.close()

生产者

import json
import pika
import sys # 同样需要建立连接和通道
params = pika.URLParameters('amqp://mickey:passw0rd@localhost:5672/web_app')
conn = pika.BlockingConnection(params)
channel = conn.channel() # 将在生产者中声明的 RabbitMQ 对象再重新声明一次,如果已经存在了则不会重复创建
# 这段逻辑实际上可有可无,只是为了说明声明一个 RabbitMQ 对象并不表示一定会创建
# 只有在第一次声明该对象的时候才会创建,之后无论在生成者或消费者中都可以再次声明
# channel.exchange_declare(exchange='web', exchange_type='direct',
# passive=False, durable=True, auto_delete=False)
# channel.queue_declare(queue='app')
# channel.queue_bind(queue='app',
# exchange='web',
# routing_key='web_app_route') # 配置 AMQP 消息的 BasicProperties 基本属性
# 在 AMQP 协议中定义了 14 种 Properties,会随消息一同传递,这里表示使用 JSON 格式数据流
# 参数 delivery_mode=2 表示希望持久化消息,在 RabbitMQ 重启后自动重建消息
# 前文也提到过,持久化消息需要考虑到性能成本的问题
props = pika.BasicProperties(content_type='application/json', delivery_mode=2)
message = ' '.join(sys.argv[1:]) or "Hello World!"
body = {'msg': message} # 发布消息,指定消息传递的交换机和所携带的路由键
print(" [x] Publish %s", message)
channel.basic_publish(exchange='web',
routing_key='web_app_route',
body=json.dumps(body),
properties=props) conn.close()

NOTE:如果你希望使用缺省的虚拟主机和 guest 用户时,你的 AMQP URL 应该是这样的:

# '%2F' 是缺省虚拟主机 '/' 的转义
params = pika.URLParameters('amqp://guest:guest@localhost:5672/%2F')

运行结果

在两个终端分别运行生产者和消费者。

  • 生产者
$ python producer.py First message.
(' [x] Publish %s', 'First message.') $ python producer.py First message
(' [x] Publish %s', 'First message') $ python producer.py Second message
(' [x] Publish %s', 'Second message') $ python producer.py Third message
(' [x] Publish %s', 'Third message') $ python producer.py
(' [x] Publish %s', 'Hello World!')
  • 消费者
$ python consumer.py
[*] Waiting for messages. To exit press CTRL+C
[x] Consumed '{"msg": "First message"}'
[x] Consumed '{"msg": "Second message"}'
[x] Consumed '{"msg": "Third message"}'
[x] Consumed '{"msg": "Hello World!"}'

可以通过 CLI 来查看队列的消息情况:

$ rabbitmqctl list_queues -p web_app
Listing queues ...
app 0
...done.

NOTE:需要注意的是,上例中仅运行了一对生产者/消费者。实际上我们可以尝试同时运行多个消费者,并订阅到一个队列。这样的话,RabbitMQ 就会默认以分摊的方式将消息分别给多个消费者。

应用预取计数

我们知道,因为每个消费者执行的任务长度不尽相同,如果使用分摊的方式来分配消息的话,那么任务粒度小、执行时间短的消费者就会闲置下来。解决的方法就是在消费者中应用预期计数来实现公平调度(Fair dispatch)的效果。

# 当预取计数为 1 时,RabbitMQ 不会同时为消费者分配多个任务,只有等消费者处理完消息之后,才会接收下一个消息
channel.basic_qos(prefetch_count=1) # 当预取计数为 10 时,RabbitMQ 会同时让消费者取出 10 个消息,直到 10 个消息都处理完之后,再继续接收下一次 10 个消息
# channel.basic_qos(prefetch_count=10)

可见,当消费者执行的任务长度较短时,应该给予更大的预取计数,来充当发挥消费者的性能。

应用 ACK 机制

应用 ACK 机制,来保证消息的有效传递。

def consumer_callback(channel, method, header_props, body):
print " [x] Consumed %r" % (body,)
# 在处理完消息之后,返回 ACK 消息应答
channel.basic_ack(delivery_tag=method.delivery_tag) # 订阅队列的时候,参数 no_ack=False 表示告诉消息代理要等待 ACK 之后才将消息丢弃
channel.basic_consume(consumer_callback, queue='app', no_ack=False)

最后

如果你常接触 RabbitMQ,那么建议你结合上篇和中篇里提到的对象概念以及特性来浏览代码,相信会有更深的感触。除此之外,我们还可以在 RabbitMQ Tutorials 中获取更多的 Samples。

快速入门分布式消息队列之 RabbitMQ(3)的更多相关文章

  1. 快速入门分布式消息队列之 RabbitMQ(2)

    目录 目录 前文列表 RabbitMQ 的特性 Message Acknowledgment 消息应答 Prefetch Count 预取数 RPC 远程过程调用 vhost 虚拟主机 插件系统 最后 ...

  2. 快速入门分布式消息队列之 RabbitMQ(1)

    目录 目录 前言 简介 安装 RabbitMQ 基本对象概念 Message 消息 Producer 生产者 Consumer 消费者 Queue 队列 Exchange 交换机 Binding 绑定 ...

  3. 【转】快速理解Kafka分布式消息队列框架

     from:http://blog.csdn.net/colorant/article/details/12081909 快速理解Kafka分布式消息队列框架 标签: kafkamessage que ...

  4. 分布式消息队列XXL-MQ

    <分布式消息队列XXL-MQ>     一.简介 1.1 概述 XXL-MQ是一款轻量级分布式消息队列,支持串行.并行和广播等多种消息模型.现已开放源代码,开箱即用. 支持三种消息模式: ...

  5. RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙

    消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以 ...

  6. Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇

    目前业界流行的分布式消息队列系统(或者可以叫做消息中间件)种类繁多,比如,基于Erlang的RabbitMQ.基于Java的ActiveMQ/Apache Kafka.基于C/C++的ZeroMQ等等 ...

  7. EQueue - 一个C#写的开源分布式消息队列的总体介绍

    前言 本文想介绍一下前段时间在写enode时,顺便实现的一个分布式消息队列equeue.这个消息队列的思想不是我想出来的,而是通过学习阿里的rocketmq后,自己用c#实现了一个轻量级的简单版本.一 ...

  8. 分享一个c#写的开源分布式消息队列equeue

    分享一个c#写的开源分布式消息队列equeue 前言 equeue消息队列中的专业术语 Topic Queue Producer Consumer Consumer Group Broker 集群消费 ...

  9. 消息队列之 RabbitMQ

    https://www.jianshu.com/p/79ca08116d57 关于消息队列,从前年开始断断续续看了些资料,想写很久了,但一直没腾出空,近来分别碰到几个朋友聊这块的技术选型,是时候把这块 ...

随机推荐

  1. 使用window10系统搭建完善的Linux开发环境

    https://juejin.im/post/5d22e46ee51d45775746b9b1 导读 在使用window系统开发时由于系统环境和线上环境不一致可能导致各种问题,以及部分扩展库只支持li ...

  2. AIX中的进程管理

    1.AIX中的进程 (1)后台进程 后台进程运行时,用户不必等待当前后台进程的结束,即可以运行下一个进程. 后台进程的运行方式: # command &   (2)提高进程优先等级 -- ni ...

  3. Java内部类的整理。

    Java 内部类 分四种:成员内部类.局部内部类.静态内部类和匿名内部类. 成员内部类: (1)即作为外部类的一个成员存在,与外部类的属性.方法并列. 注意:成员内部类中不能定义静态变量,但可以访问外 ...

  4. Linux本机和远程服务器之间文件的上传和下载 rz sz

    tar zxvf lrzsz-0.12.20.tar.gz 解压安装包   下载地址:链接:https://pan.baidu.com/s/1KMS1QlyOhpXiYeaWdNBAyw 提取码:08 ...

  5. 对所有的webview添加userAgent

    在appdelegate中- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDicti ...

  6. Spring Boot JPA - Querydsl

    https://lufficc.com/blog/spring-boot-jpa-querydsl

  7. 使用RegisterPointerInputTarget时的一些注意事项

    RegisterPointerInputTarget :允许调用者注册一个目标窗口,指定类型的所有指针输入都重定向到该窗口. 要使用它必须使 UIAccess = true,见下图 在设置完之后,需要 ...

  8. 【BZOJ1999】树网的核

    题目大意:题目过长,无法简单描述... 题解: 由于树网的核一定是树直径的一段,因此考虑先将直径取出,通过两次 BFS 即可.要求的东西是树上任意一点到这条取出的线段的距离的最大值,发现这个最大值有可 ...

  9. Python面向对象的三大特性之继承和组合

    继承和组合 一.组合 组合:组合指的是,在一个类中以另外一个类的对象(也就是实例)作为数据属性,称为类的组合 也就是说:一个类的属性是另一个类的对象,就是组合 例子: 圆环是由两个圆组成的,圆环的面积 ...

  10. R中的常用命令(持续更新)

    (1)工作环境 #Ctrl+L键:清屏#Ctrl+Shift+C键:注释.取消注释(仅在RStudio中)(可以多行) rm(变量) #清除某变量 ls() #列出内存中的变量 rm(list=ls( ...