目录

前文列表

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

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

前言

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

安装 RabbitMQ 的 Python 客户端 pika

  1. pip install pika

通道 Channel

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

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

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

消费者

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

生产者

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

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

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

运行结果

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

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

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

  1. $ rabbitmqctl list_queues -p web_app
  2. Listing queues ...
  3. app 0
  4. ...done.

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

应用预取计数

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

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

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

应用 ACK 机制

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

  1. def consumer_callback(channel, method, header_props, body):
  2. print " [x] Consumed %r" % (body,)
  3. # 在处理完消息之后,返回 ACK 消息应答
  4. channel.basic_ack(delivery_tag=method.delivery_tag)
  5. # 订阅队列的时候,参数 no_ack=False 表示告诉消息代理要等待 ACK 之后才将消息丢弃
  6. 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. cx_Oracle 操作oracle数据库

    cx_Oracle 操作oracle数据库 class MyOracle(): def __init__(self, host_name="ip", port=1521, sid= ...

  2. laravel 的lnmp 的配置

    装了lnmp后,一般用 lnmp vhost add 添加网站 一般 只用重写和ssl功能 再发laravel官方的配置 server { listen 80; server_name example ...

  3. Linux系统账户管理指令

    sudo passwd -l weblogic 锁定账户 sudo passwd -u weblogic 解锁账户 useradd weblogic -p xxxxx 添加账户指定密码 sudo us ...

  4. vue项目中利用popstate处理页面返回操作

    需求背景:项目中需要做一个返回确认,避免用户误触返回键而退出当前页面. 原理:利用history和浏览器刷新popstate状态 实现: 1.在mounted() 阶段判断并添加popstate事件监 ...

  5. DevExpress WinForms v19.1新版亮点:Spreadsheet/Sunburst控件功能增强

    行业领先的.NET界面控件DevExpress v19.1终于正式发布,本站将以连载的形式介绍各版本新增内容.在本系列文章中将为大家介绍DevExpress WinForms v19.1中新增的一些控 ...

  6. Taro 压缩图片api

    Taro API里面没有写支持compressImage,ts提示也是,开发者工具提示暂时不支持此API调试,请使用真机进行开发.这是因为Taro这个库没有把新的api加上,其实还是调用了wx.com ...

  7. oracle 创建表的规则

    1.表明首字母 应该为字母 2.表名的最大长度为30个字符 3.不能使用oracle保留字和关键字来作表名 4.同一用户下的不同表不能具有相同的名称 5.可以使用下划线.数字和字母,但不能使用空格与单 ...

  8. python技巧31[移植python2.x到3.x]

    我们都知道python从2.x升级到3.x的过程中有一些不兼容的改动,但是有时还我们不得不将2.x的程序升级到3.x. 主要不兼容如下图: 移植过程: 1) 确保存在的代码有足够的测试覆盖.从2.x到 ...

  9. PHP基础教程探讨一些php编程性能优化总结

      兄弟连PHP培训 小编最近在做php程序的性能优化,一些经过测试后发现的东西就先记录下来,以备后用. 首先对于一些反应慢的操作或页面要跟踪处理一下,可以使用webGrind的方式看一下主要问题出在 ...

  10. POJ 3660 Cow Contest 任意两点之间的关系 Floyd

    题意:牛之间有绝对的强弱,给出一些胜负关系,问有多少头牛可以确定其绝对排名. #include <iostream> #include <cstdio> #include &l ...