Python实现RabbitMQ6种消息模型的示例代码


Posted in Python onMarch 30, 2020

RabbitMQ与Redis对比

​ RabbitMQ是一种比较流行的消息中间件,之前我一直使用redis作为消息中间件,但是生产环境比较推荐RabbitMQ来替代Redis,所以我去查询了一些RabbitMQ的资料。相比于Redis,RabbitMQ优点很多,比如:

  • 具有消息消费确认机制
  • 队列,消息,都可以选择是否持久化,粒度更小、更灵活。
  • 可以实现负载均衡

 RabbitMQ应用场景

  •  异步处理:比如用户注册时的确认邮件、短信等交由rabbitMQ进行异步处理
  • 应用解耦:比如收发消息双方可以使用消息队列,具有一定的缓冲功能
  • 流量削峰:一般应用于秒杀活动,可以控制用户人数,也可以降低流量
  • 日志处理:将info、warning、error等不同的记录分开存储

 RabbitMQ消息模型

​ 这里使用 Pythonpika 这个库来实现RabbitMQ中常见的6种消息模型。没有的可以先安装:

pip install pika

1.单生产单消费模型:即完成基本的一对一消息转发。

Python实现RabbitMQ6种消息模型的示例代码

# 生产者代码
import pika


credentials = pika.PlainCredentials('chuan', '123') # mq用户名和密码,没有则需要自己创建
# 虚拟队列需要指定参数 virtual_host,如果是默认的可以不填。
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost',
                                port=5672,
                                virtual_host='/',
                                credentials=credentials))

# 建立rabbit协议的通道
channel = connection.channel()
# 声明消息队列,消息将在这个队列传递,如不存在,则创建。durable指定队列是否持久化
channel.queue_declare(queue='python-test', durable=False)

# message不能直接发送给queue,需经exchange到达queue,此处使用以空字符串标识的默认的exchange
# 向队列插入数值 routing_key是队列名
channel.basic_publish(exchange='',
           routing_key='python-test',
           body='Hello world!2')
# 关闭与rabbitmq server的连接
connection.close()
# 消费者代码
import pika

credentials = pika.PlainCredentials('chuan', '123')
# BlockingConnection:同步模式
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost',
                                port=5672,
                                virtual_host='/',
                              credentials=credentials))
channel = connection.channel()
# 申明消息队列。当不确定生产者和消费者哪个先启动时,可以两边重复声明消息队列。
channel.queue_declare(queue='python-test', durable=False)
# 定义一个回调函数来处理消息队列中的消息,这里是打印出来
def callback(ch, method, properties, body):
  # 手动发送确认消息
  ch.basic_ack(delivery_tag=method.delivery_tag)
  print(body.decode())
  # 告诉生产者,消费者已收到消息

# 告诉rabbitmq,用callback来接收消息
# 默认情况下是要对消息进行确认的,以防止消息丢失。
# 此处将auto_ack明确指明为True,不对消息进行确认。
channel.basic_consume('python-test',
           on_message_callback=callback)
           # auto_ack=True) # 自动发送确认消息
# 开始接收信息,并进入阻塞状态,队列里有信息才会调用callback进行处理
channel.start_consuming()

2.消息分发模型:多个收听者监听一个队列。

Python实现RabbitMQ6种消息模型的示例代码

# 生产者代码
import pika


credentials = pika.PlainCredentials('chuan', '123') # mq用户名和密码
# 虚拟队列需要指定参数 virtual_host,如果是默认的可以不填。
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost',
                                port=5672,
                                virtual_host='/',
                                credentials=credentials))

# 建立rabbit协议的通道
channel = connection.channel()
# 声明消息队列,消息将在这个队列传递,如不存在,则创建。durable指定队列是否持久化。确保没有确认的消息不会丢失
channel.queue_declare(queue='rabbitmqtest', durable=True)

# message不能直接发送给queue,需经exchange到达queue,此处使用以空字符串标识的默认的exchange
# 向队列插入数值 routing_key是队列名
# basic_publish的properties参数指定message的属性。此处delivery_mode=2指明message为持久的
for i in range(10):
  channel.basic_publish(exchange='',
             routing_key='python-test',
             body='Hello world!%s' % i,
             properties=pika.BasicProperties(delivery_mode=2))
# 关闭与rabbitmq server的连接
connection.close()
# 消费者代码,consume1与consume2
import pika
import time

credentials = pika.PlainCredentials('chuan', '123')
# BlockingConnection:同步模式
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost',
                                port=5672,
                                virtual_host='/',
                                credentials=credentials))
channel = connection.channel()
# 申明消息队列。当不确定生产者和消费者哪个先启动时,可以两边重复声明消息队列。
channel.queue_declare(queue='rabbitmqtest', durable=True)
# 定义一个回调函数来处理消息队列中的消息,这里是打印出来
def callback(ch, method, properties, body):
  # 手动发送确认消息
  time.sleep(10)
  print(body.decode())
  # 告诉生产者,消费者已收到消息
  ch.basic_ack(delivery_tag=method.delivery_tag)

# 如果该消费者的channel上未确认的消息数达到了prefetch_count数,则不向该消费者发送消息
channel.basic_qos(prefetch_count=1)
# 告诉rabbitmq,用callback来接收消息
# 默认情况下是要对消息进行确认的,以防止消息丢失。
# 此处将no_ack明确指明为True,不对消息进行确认。
channel.basic_consume('python-test',
           on_message_callback=callback)
           # auto_ack=True) # 自动发送确认消息
# 开始接收信息,并进入阻塞状态,队列里有信息才会调用callback进行处理
channel.start_consuming()

3.fanout消息订阅模式:生产者将消息发送到Exchange,Exchange再转发到与之绑定的Queue中,每个消费者再到自己的Queue中取消息。

Python实现RabbitMQ6种消息模型的示例代码

# 生产者代码
import pika


credentials = pika.PlainCredentials('chuan', '123') # mq用户名和密码
# 虚拟队列需要指定参数 virtual_host,如果是默认的可以不填。
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost',
                                port=5672,
                                virtual_host='/',
                                credentials=credentials))
# 建立rabbit协议的通道
channel = connection.channel()
# fanout: 所有绑定到此exchange的queue都可以接收消息(实时广播)
# direct: 通过routingKey和exchange决定的那一组的queue可以接收消息(有选择接受)
# topic: 所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息(更细致的过滤)
channel.exchange_declare('logs', exchange_type='fanout')


#因为是fanout广播类型的exchange,这里无需指定routing_key
for i in range(10):
  channel.basic_publish(exchange='logs',
             routing_key='',
             body='Hello world!%s' % i)

# 关闭与rabbitmq server的连接
connection.close()
import pika

credentials = pika.PlainCredentials('chuan', '123')
# BlockingConnection:同步模式
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost',
                                port=5672,
                                virtual_host='/',
                                credentials=credentials))
channel = connection.channel()

#作为好的习惯,在producer和consumer中分别声明一次以保证所要使用的exchange存在
channel.exchange_declare(exchange='logs',
             exchange_type='fanout')

# 随机生成一个新的空的queue,将exclusive置为True,这样在consumer从RabbitMQ断开后会删除该queue
# 是排他的。
result = channel.queue_declare('', exclusive=True)

# 用于获取临时queue的name
queue_name = result.method.queue

# exchange与queue之间的关系成为binding
# binding告诉exchange将message发送该哪些queue
channel.queue_bind(exchange='logs',
          queue=queue_name)

# 定义一个回调函数来处理消息队列中的消息,这里是打印出来
def callback(ch, method, properties, body):
  # 手动发送确认消息
  print(body.decode())
  # 告诉生产者,消费者已收到消息
  #ch.basic_ack(delivery_tag=method.delivery_tag)

# 如果该消费者的channel上未确认的消息数达到了prefetch_count数,则不向该消费者发送消息
channel.basic_qos(prefetch_count=1)
# 告诉rabbitmq,用callback来接收消息
# 默认情况下是要对消息进行确认的,以防止消息丢失。
# 此处将no_ack明确指明为True,不对消息进行确认。
channel.basic_consume(queue=queue_name,
           on_message_callback=callback,
           auto_ack=True) # 自动发送确认消息
# 开始接收信息,并进入阻塞状态,队列里有信息才会调用callback进行处理
channel.start_consuming()

4.direct路由模式:此时生产者发送消息时需要指定RoutingKey,即路由Key,Exchange接收到消息时转发到与RoutingKey相匹配的队列中。

Python实现RabbitMQ6种消息模型的示例代码

# 生产者代码,测试命令可以使用:python produce.py error 404error
import pika
import sys

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

# 声明一个名为direct_logs的direct类型的exchange
# direct类型的exchange
channel.exchange_declare(exchange='direct_logs',
             exchange_type='direct')

# 从命令行获取basic_publish的配置参数
severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
message = ' '.join(sys.argv[2:]) or 'Hello World!'

# 向名为direct_logs的exchage按照设置的routing_key发送message
channel.basic_publish(exchange='direct_logs',
           routing_key=severity,
           body=message)

print(" [x] Sent %r:%r" % (severity, message))
connection.close()
# 消费者代码,测试可以使用:python consume.py error
import pika
import sys

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

# 声明一个名为direct_logs类型为direct的exchange
# 同时在producer和consumer中声明exchage或queue是个好习惯,以保证其存在
channel.exchange_declare(exchange='direct_logs',
             exchange_type='direct')

result = channel.queue_declare('', exclusive=True)
queue_name = result.method.queue

# 从命令行获取参数:routing_key
severities = sys.argv[1:]
if not severities:
  print(sys.stderr, "Usage: %s [info] [warning] [error]" % (sys.argv[0],))
  sys.exit(1)

for severity in severities:
  # exchange和queue之间的binding可接受routing_key参数
  # fanout类型的exchange直接忽略该参数。direct类型的exchange精确匹配该关键字进行message路由
  # 一个消费者可以绑定多个routing_key
  # Exchange就是根据这个RoutingKey和当前Exchange所有绑定的BindingKey做匹配,
  # 如果满足要求,就往BindingKey所绑定的Queue发送消息
  channel.queue_bind(exchange='direct_logs',
            queue=queue_name,
            routing_key=severity)

def callback(ch, method, properties, body):
  print(" [x] %r:%r" % (method.routing_key, body,))


channel.basic_consume(queue=queue_name,
           on_message_callback=callback,
           auto_ack=True)

channel.start_consuming()

5.topic匹配模式:更细致的分组,允许在RoutingKey中使用匹配符。

  • *:匹配一个单词
  • #:匹配0个或多个单词

Python实现RabbitMQ6种消息模型的示例代码

# 生产者代码,基本不变,只需将exchange_type改为topic(测试:python produce.py rabbitmq.red 
# red color is my favorite
import pika
import sys

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

# 声明一个名为direct_logs的direct类型的exchange
# direct类型的exchange
channel.exchange_declare(exchange='topic_logs',
             exchange_type='topic')

# 从命令行获取basic_publish的配置参数
severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
message = ' '.join(sys.argv[2:]) or 'Hello World!'

# 向名为direct_logs的exchange按照设置的routing_key发送message
channel.basic_publish(exchange='topic_logs',
           routing_key=severity,
           body=message)

print(" [x] Sent %r:%r" % (severity, message))
connection.close()
# 消费者代码,(测试:python consume.py *.red)
import pika
import sys

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

# 声明一个名为direct_logs类型为direct的exchange
# 同时在producer和consumer中声明exchage或queue是个好习惯,以保证其存在
channel.exchange_declare(exchange='topic_logs',
             exchange_type='topic')

result = channel.queue_declare('', exclusive=True)
queue_name = result.method.queue

# 从命令行获取参数:routing_key
severities = sys.argv[1:]
if not severities:
  print(sys.stderr, "Usage: %s [info] [warning] [error]" % (sys.argv[0],))
  sys.exit(1)

for severity in severities:
  # exchange和queue之间的binding可接受routing_key参数
  # fanout类型的exchange直接忽略该参数。direct类型的exchange精确匹配该关键字进行message路由
  # 一个消费者可以绑定多个routing_key
  # Exchange就是根据这个RoutingKey和当前Exchange所有绑定的BindingKey做匹配,
  # 如果满足要求,就往BindingKey所绑定的Queue发送消息
  channel.queue_bind(exchange='topic_logs',
            queue=queue_name,
            routing_key=severity)

def callback(ch, method, properties, body):
  print(" [x] %r:%r" % (method.routing_key, body,))


channel.basic_consume(queue=queue_name,
           on_message_callback=callback,
           auto_ack=True)

channel.start_consuming()

6.RPC远程过程调用:客户端与服务器之间是完全解耦的,即两端既是消息的发送者也是接受者。

Python实现RabbitMQ6种消息模型的示例代码

# 生产者代码
import pika
import uuid


# 在一个类中封装了connection建立、queue声明、consumer配置、回调函数等
class FibonacciRpcClient(object):
  def __init__(self):
    # 建立到RabbitMQ Server的connection
    self.connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))

    self.channel = self.connection.channel()

    # 声明一个临时的回调队列
    result = self.channel.queue_declare('', exclusive=True)
    self._queue = result.method.queue

    # 此处client既是producer又是consumer,因此要配置consume参数
    # 这里的指明从client自己创建的临时队列中接收消息
    # 并使用on_response函数处理消息
    # 不对消息进行确认
    self.channel.basic_consume(queue=self._queue,
                  on_message_callback=self.on_response,
                  auto_ack=True)
    self.response = None
    self.corr_id = None

  # 定义回调函数
  # 比较类的corr_id属性与props中corr_id属性的值
  # 若相同则response属性为接收到的message
  def on_response(self, ch, method, props, body):
    if self.corr_id == props.correlation_id:
      self.response = body

  def call(self, n):
    # 初始化response和corr_id属性
    self.corr_id = str(uuid.uuid4())

    # 使用默认exchange向server中定义的rpc_queue发送消息
    # 在properties中指定replay_to属性和correlation_id属性用于告知远程server
    # correlation_id属性用于匹配request和response
    self.channel.basic_publish(exchange='',
                  routing_key='rpc_queue',
                  properties=pika.BasicProperties(
                    reply_to=self._queue,
                    correlation_id=self.corr_id,
                  ),
                  # message需为字符串
                  body=str(n))

    while self.response is None:
      self.connection.process_data_events()

    return int(self.response)


# 生成类的实例
fibonacci_rpc = FibonacciRpcClient()

print(" [x] Requesting fib(30)")
# 调用实例的call方法
response = fibonacci_rpc.call(30)
print(" [.] Got %r" % response)
# 消费者代码,这里以生成斐波那契数列为例
import pika

# 建立到达RabbitMQ Server的connection
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

# 声明一个名为rpc_queue的queue
channel.queue_declare(queue='rpc_queue')

# 计算指定数字的斐波那契数
def fib(n):
  if n == 0:
    return 0
  elif n == 1:
    return 1
  else:
    return fib(n - 1) + fib(n - 2)

# 回调函数,从queue接收到message后调用该函数进行处理
def on_request(ch, method, props, body):
  # 由message获取要计算斐波那契数的数字
  n = int(body)
  print(" [.] fib(%s)" % n)
  # 调用fib函数获得计算结果
  response = fib(n)

  # exchage为空字符串则将message发送个到routing_key指定的queue
  # 这里queue为回调函数参数props中reply_ro指定的queue
  # 要发送的message为计算所得的斐波那契数
  # properties中correlation_id指定为回调函数参数props中co的rrelation_id
  # 最后对消息进行确认
  ch.basic_publish(exchange='',
           routing_key=props.reply_to,
           properties=pika.BasicProperties(correlation_id=props.correlation_id),
           body=str(response))
  ch.basic_ack(delivery_tag=method.delivery_tag)


# 只有consumer已经处理并确认了上一条message时queue才分派新的message给它
channel.basic_qos(prefetch_count=1)

# 设置consumeer参数,即从哪个queue获取消息使用哪个函数进行处理,是否对消息进行确认
channel.basic_consume(queue='rpc_queue', on_message_callback=on_request)

print(" [x] Awaiting RPC requests")

# 开始接收并处理消息
channel.start_consuming()

到此这篇关于Python实现RabbitMQ6种消息模型的示例代码的文章就介绍到这了,更多相关Python RabbitMQ消息模型 内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python连接sql server乱码的解决方法
Jan 28 Python
python标准算法实现数组全排列的方法
Mar 17 Python
在Python中使用Neo4j数据库的教程
Apr 16 Python
Python计算三角函数之asin()方法的使用
May 15 Python
Python语言的变量认识及操作方法
Feb 11 Python
python3 实现一行输入,空格隔开的示例
Nov 14 Python
python开发之anaconda以及win7下安装gensim的方法
Jul 05 Python
python3.x提取中文的正则表达式示例代码
Jul 23 Python
python 队列基本定义与使用方法【初始化、赋值、判断等】
Oct 24 Python
详解mac python+selenium+Chrome 简单案例
Nov 08 Python
解决torch.autograd.backward中的参数问题
Jan 07 Python
Python实现Word文档转换Markdown的示例
Dec 22 Python
Anconda环境下Vscode安装Python的方法详解
Mar 29 #Python
配置python的编程环境之Anaconda + VSCode的教程
Mar 29 #Python
Anaconda+VSCode配置tensorflow开发环境的教程详解
Mar 30 #Python
利用django model save方法对未更改的字段依然进行了保存
Mar 28 #Python
Python Tornado之跨域请求与Options请求方式
Mar 28 #Python
关于python 跨域处理方式详解
Mar 28 #Python
对python中各个response的使用说明
Mar 28 #Python
You might like
PHP_MySQL教程-第一天
2007/03/18 PHP
joomla实现注册用户添加新字段的方法
2016/05/05 PHP
php简单的上传类分享
2016/05/15 PHP
javascript URL锚点取值方法
2009/02/25 Javascript
转换json格式的日期为Javascript对象的函数
2010/07/13 Javascript
JS+ACTIVEX实现网页选择本地目录路径对话框
2013/03/18 Javascript
javascript中验证大写字母、数字和中文
2014/01/15 Javascript
jQuery制作简洁的多级联动Select下拉框
2014/12/23 Javascript
window.setInterval()方法的定义和用法及offsetLeft与style.left的区别
2015/11/11 Javascript
javascript 解决浏览器不支持的问题
2016/09/24 Javascript
微信小程序 前端源码逻辑和工作流详解
2016/10/08 Javascript
jQuery中on方法使用注意事项详解
2017/02/15 Javascript
Bootstrap modal 多弹窗之叠加引起的滚动条遮罩阴影问题
2017/02/27 Javascript
jQuery is not defined 错误原因与解决方法小结
2017/03/19 Javascript
JavaScript代码判断输入的字符串是否含有特殊字符和表情代码实例
2017/08/17 Javascript
js数据类型检测总结
2018/08/05 Javascript
React Router V4使用指南(精讲)
2018/09/17 Javascript
Vue利用History记录上一页面的数据方法实例
2018/11/02 Javascript
详解一个基于react+webpack的多页面应用配置
2019/01/21 Javascript
vue插槽slot的理解和使用方法
2019/04/03 Javascript
vue-cli3 DllPlugin 提取公用库的方法
2019/04/24 Javascript
浅谈python jieba分词模块的基本用法
2017/11/09 Python
Python实现查看系统启动项功能示例
2018/05/10 Python
OpenCV+python手势识别框架和实例讲解
2018/08/03 Python
Python八皇后问题解答过程详解
2019/07/29 Python
python隐藏类中属性的3种实现方法
2019/12/19 Python
对python中list的五种查找方法说明
2020/07/13 Python
使用scrapy ImagesPipeline爬取图片资源的示例代码
2020/09/28 Python
cookies应对python反爬虫知识点详解
2020/11/25 Python
Canvas与Image互相转换示例代码
2013/08/09 HTML / CSS
Mavi牛仔裤美国官网:土耳其著名牛仔品牌
2016/09/24 全球购物
基层党员四风问题自我剖析材料
2014/09/29 职场文书
会计试用期自我评价
2015/03/10 职场文书
爱心捐书倡议书
2015/04/27 职场文书
领导视察通讯稿
2015/07/18 职场文书
人力资源部工作计划
2019/05/14 职场文书