Python RabbitMQ
前言
什么是消息队列?#
MQ 全称为 Message Queue 消息队列 (MQ)是一种应用程序的通信方法, MQ是生产-消费者模型的一个经典代表,一端往消息队列中不断写入消息,另一端不断可以读取队列中的消息。
消息队列出现的原因?#
消息队列主要是解决应用中解耦 异步消息 流量削锋等问题, 实现高性能 高可用 可伸缩的最终一致性架构。
RabbitMQ 使用
RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。目前使用最多的消息队列。
RabbitMQ 安装#
# centos 7 中安装
yum install rabbitmq-server
# 启动
systemctl enable rabbitmq-server.service
systemctl start rabbitmq-server.service
RabbitMQ 工作模式#
简单模式#
生产者#
"""生产者
生产者流程:
1. 链接 rabbitmq
2. 创建队列
3. 向指定的队列插入数据
"""
import pika
# 创建凭证
# credentials = pika.PlainCredentials("admin","123456")
# 连接 rabbitMQ
connection = pika.BlockingConnection(pika.ConnectionParameters('172.27.135.11'))
channel = connection.channel()
# 创建一个消息队列
channel.queue_declare(queue='hello')
# 向指定队列插入数据
channel.basic_publish(exchange='', # 简单模式
routing_key='hello', # 指定队列
body=b"Hello World!")
print(" [x] Sent 'Hello World!'")
消费者#
import pika
# 创建一个连接
connection = pika.BlockingConnection(pika.ConnectionParameters('172.27.135.11'))
channel = connection.channel()
# 创建一个消息队列
channel.queue_declare(queue='hello')
# 确定回调参数
def callback(ch, method, properties, body):
"""
前三个参数先不用考虑
:param body:
:return:
"""
print(" [x] Received %r" % body)
# 确定监听队列
channel.basic_consume(queue='hello',
auto_ack=True,
on_message_callback=callback)
print(' [*] Waiting for messages. To exit press CTRL+C')
# 开始消费 到此才真正执行
channel.start_consuming()
参数#
应答参数#
应答参数分为 自动应答
和手动应答
;
出现这两种模式的原因是:如果消费者取出消息队列中的数据后,消费者内部程序出现错误,
修复消费者的 bug 后想要再次取出消息队列中的数据,但此时的消息队列中的数据已经不存在了。
所以手动应答 需要消费者 callback
后,消息队列才会将消费者取出的队列删除。
消费者示例
def callback(ch, method, properties, body):
print(" [x] Received %r" % body)
ch.basic_ack(delivery_tag=method.delivery_tag)
channel.basic_consume(queue='hello1',
# 默认应答:会造成数据丢失问题:auto_ack=True;
# 默认应答需要改为手动应答: auto_ack=False,
# 手动应答,意思为 MQ 队列中的数据,需要 消费者回调删除
# 手动应答适用于 安全
# 默认应答适用于 性能
auto_ack=False,
on_message_callback=callback)
print(' [*] Waiting for messages. To exit press CTRL+C')
# 开始消费 到此才真正执行
channel.start_consuming()
持久化参数#
持久化参数:让消息队列中的数据存储在硬盘上 (队列持久化)
目的: 解决 rabbitMQ 的服务崩溃从而数据丢失问题
生产者
# 创建一个消息队列
channel.queue_declare(queue='hello5', durable=True)
# durable 用于持久化队列 不能持久化队列中的消息
# 向指定队列插入数据
channel.basic_publish(exchange='', # 简单模式
routing_key='hello5', # 指定队列
body="Hello World!",
properties=pika.BasicProperties(
delivery_mode=2 # make message persistent
)
)
# properties 指的是消费者端 callback 函数中的 properties 参数;
# delivery_mode=2 # make message persistent 持久化消息
消费者
channel.queue_declare(queue='hello5', durable=True)
# 确定监听队列
channel.basic_consume(queue='hello5',
auto_ack=False,
on_message_callback=callback)
分发参数#
轮询分发 一个生产者产生的数据 轮询分发给消费者。
# 消费者中增加一句 公平分发:谁快谁先多得到数据
channel.basic_qos(prefetch_count=1)
例如:生产者分别产生 111, 222, 333 。
消费者 1 接收到 111;
消费者 2 接收到 222;
消费者 3 接收到 333;
交换机模式#
发布订阅模式#
模式说明:生产者创建交换机 消费者创建唯一消息队列并将自己的消息队列绑定在交换机中,当生产者发布信息时,所有绑定交换机的消息队列都会收到一份数据。
生产者
""" 生产者
1. 连接rabbitMQ
2. 确认交换机
3. 发布信息
4. 关闭
"""
import pika
# 连接rabbitMQ
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='172.27.135.11'))
channel = connection.channel()
# 交换机模式
channel.exchange_declare(
exchange='logs', # 交换机的名称
exchange_type='fanout' # 交换机的模式 这里为发布订阅模式
)
message = "info: Hello World"
channel.basic_publish(
exchange='logs',
routing_key='',
body=message
)
connection.close()
消费者
"""消费者模式
1. 连接rabbitMQ
2. 确认交换机 没有则创建
3. 确认唯一消息队列
4. 绑定交换机
"""
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='172.27.135.11'))
channel = connection.channel()
# 确认交换机
# 消费者也要确认交换机 目的是为了防止生产者后产生交换机而无法绑定
channel.exchange_declare(
exchange='logs', # 交换机名称
exchange_type='fanout' # 交换机模式类型
)
# 确认消息队列; exclusive=True 是为了随机产生不重复的消息队列名称
result = channel.queue_declare("", exclusive=True)
# 获取队列名称
queue_name = result.method.queue
# 消息队列绑定交换机
channel.queue_bind(exchange='logs',
queue=queue_name)
def callback(ch, method, properties, body):
"""
body 是就收的数据
"""
print(" [x] %r" % body)
# 确认消费者模式
channel.basic_consume(queue=queue_name,
auto_ack=True,
on_message_callback=callback)
# 真正开始消费
channel.start_consuming()
关键字模式#
发布-订阅模式
:会出现一个问题:当某个消费者不需要一些数据时,可是交换机还是会给消费者发送。
关键字模式
:为了这一问题,出现关键字模式, 我们需要对消费者和生产者定义关键字,只有关键字相互匹配,消费者才接收当前数据。
- 解释图中信息
- P:生产者
- C1,C2: 消费者
- X:交换机, type=direct 关键字模式
- error info warning: 声明的关键字
- amqp.gen-S...: 队列名
生产者
import pika
# 连接rabbitMQ
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='172.27.135.11'))
channel = connection.channel()
# 交换机模式
channel.exchange_declare(
exchange='logs2', # 交换机的名称
exchange_type='direct' # 交换机的模式 这里为关键字模式
)
message = "info: Hello World"
channel.basic_publish(
exchange='logs2',
routing_key='info',
body=message,
)
print(" [x] Sent %r" % message)
connection.close()
消费者
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='172.27.135.11'))
channel = connection.channel()
# 确认交换机
# 消费者也要确认交换机 目的是为了防止生产者后产生交换机而无法绑定
channel.exchange_declare(
exchange='logs2', # 交换机名称
exchange_type='direct' # 交换机模式类型 关键字模式
)
# 确认消息队列; exclusive=True 是为了随机产生不重复的消息队列名称
result = channel.queue_declare("", exclusive=True)
# 获取队列名称
queue_name = result.method.queue
# 给消息队列绑定交换机 并声明接收关键字为info的数据
channel.queue_bind(exchange='logs2',
queue=queue_name,
routing_key="info")
# 给消息队列绑定交换机 并声明接收关键字为error的数据
channel.queue_bind(exchange='logs2',
queue=queue_name,
routing_key="error")
def callback(ch, method, properties, body):
print(" [x] %r" % body)
# 确认消费者模式
channel.basic_consume(queue=queue_name,
auto_ack=True,
on_message_callback=callback)
# 真正开始消费
channel.start_consuming()
通配符模式#
通配符交换机
与之前的路由模式相比,它将信息的传输类型的key更加细化,以“key1.key2.keyN....”的模式来指定信息传输的key的大类型和大类型下面的小类型,让消费者可以更加精细的确认自己想要获取的信息类型。而在消费者一段,不用精确的指定具体到哪一个大类型下的小类型的key,而是可以使用类似正则表达式(但与正则表达式规则完全不同)的通配符在指定一定范围或符合某一个字符串匹配规则的key,来获取想要的信息。
通配符交换机
(Topic Exchange)将路由键和某模式进行匹配。此时队列需要绑定在一个模式上。符号“#”匹配一个或多个词,符号“”仅匹配一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.”只会匹配到“audit.irs”。(这里与我们一般的正则表达式的“*”和“#”刚好相反,这里我们需要注意一下。)
下面是一个解释通配符模式交换机工作的一个样例
举例
# 生产者的 routing_key
routing_key = "china.news"
routing_key = "china.foods"
routing_key = "usa.news"
routing_key = "usa.foods"
# 消费者想要获取所有的 news
routing_key = "#.news"
# 消费者想要获取关于 china 的所有信息
routing_key = "china.#"
# 消费者想要获取关于 usa 的信息
routing_key = "usa.#"
生产者
import pika
# 连接rabbitMQ
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='172.27.135.11'))
channel = connection.channel()
# 交换机模式
channel.exchange_declare(
exchange='logs3', # 交换机的名称
exchange_type='topic' # 交换机的模式 这里为通配符模式
)
message = "info: this is a china time"
channel.basic_publish(
exchange='logs3',
routing_key='china.time',
body=message,
)
print(" [x] Sent %s" % message)
connection.close()
消费者
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='172.27.135.11'))
channel = connection.channel()
# 确认交换机
# 消费者也要确认交换机 目的是为了防止生产者后产生交换机而无法绑定
channel.exchange_declare(
exchange='logs3', # 交换机名称
exchange_type='topic' # 交换机模式类型
)
# 确认消息队列; exclusive=True 是为了随机产生不重复的消息队列名称
result = channel.queue_declare("", exclusive=True)
# 获取队列名称
queue_name = result.method.queue
# 消息队列绑定交换机
channel.queue_bind(exchange='logs3',
queue=queue_name,
routing_key="china.#")
def callback(ch, method, properties, body):
print(" [x] %r" % body)
# 确认消费者模式
channel.basic_consume(queue=queue_name,
auto_ack=True,
on_message_callback=callback)
# 真正开始消费
channel.start_consuming()
相关面试题
- RabbitMQ 如何在消费者获取任务后未处理完前就挂掉,保证数据不丢失?
为了预防消息丢失,rabbitmq提供了auto_ack:应答参数;
即工作进程在收到消息并处理后,callback:发送ack给rabbitmq,告知rabbitmq这时候可以把该消息从队列中删除了。
如果工作进程挂掉了,rabbitmq没有收到ack,那么会把该消息,重新分发给其他工作进程。
不需要设置timeout,即使该任务需要很长时间也可以处理。
auto_ack = Fales 是设置为手应答;
- RabbitMQ 如何对消息做持久化?
# 创建队列时声明队列为持久化队列
# 向队列中插入消息时,声明消息为持久化消息
# 创建一个消息队列
channel.queue_declare(queue='hello5', durable=True)
# 向指定队列插入数据
channel.basic_publish(exchange='', # 简单模式
routing_key='', # 指定关键字
body="Hello World!",
properties=pika.BasicProperties(
delivery_mode=2 # make message persistent
)
)
- RabbitMQ 如何控制消息被消费顺序?
# 可以将消息队列拆分为多个 queue, 保证一个 queue 中的消息只被一个消费者所消费。
- 以下 RabbitMQ 的 exchange type 分别代表什么意思? 如 fanout, direct, topic ?
fanout: 发布订阅模式;
此模式由生产者创建交换机,每个消费者分别创建自己唯一的消息队列,(消息队列)并绑定交换机。
当生产者产生一条数据,所有的绑定在此交换机的队列都会收到一份消息。
direct: 关键字模式;
此模式由生产者这创建交换机,每个消费者分别创建自己的消息队列,声明 routing_key(消息队列)并绑定交换机。
当生产者产生一条数据,向指定关键字(routing_key)中消息队列中插入数据
topic: 通配符模式;
此模式和关键字模式类似,routing_key 从完全匹配改为模糊匹配,比关键字模式更加灵活。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步