RabbitMQ
前言
生产者与消费者的解耦分离
使用
连接
import pika
RabbitMQHost = 'localhost'
conn = pika.BlockingConnection(
pika.ConnectionParameters(host=RabbitMQHost)
)
channel = conn.channel()
conn.close()
简单模式
生产者
# 创建队列
queue_name = 'first'
channel.queue_declare(queue=queue_name)
# 往队列插入数据
msg = 'Hello World!'
channel.basic_publish(
exchange='', # 简单模式,直接不指定交换机
routing_key=queue_name, # 指定队列
body=msg,
)
消费者
# 创建队列(生产者和消费者均需定义队列)
queue_name = 'first'
channel.queue_declare(queue=queue_name)
# 定义回调函数
def callback(ch, method, propertes, body):
print(f'Received {body}')
# 确定监听队列
channel.basic_consume(
queue=queue_name,
auto_ack=True, # 默认应答,即队列中的消息发出后便销毁
on_message_callback=callback # 回调
)
# 开始监听
channel.start_consuming()
注:
- 由于不确定生产者和消费者的执行先后,因此都进行队列(和交换机)的创建
手动应答
介绍:消息默认发出即毁,但消费者不一定能完成消息的处理(消费者崩溃),为了确保数据一致性,使用手动应答。当消费者返回信号后,队列才删除该消息,否则该消息后续可交给其他消费者。
生产者
# 与 简单模式 相同
# 队列的模式创建后便不能更改,因此需更换队列名称
消费者
# 创建队列(生产者和消费者均需定义队列)
queue_name = 'second'
channel.queue_declare(queue=queue_name)
# 定义回调函数
def callback(ch, method, propertes, body):
print(f'Received {body.decode()}')
ch.basic_ack(delivery_tag=method.delivery_tag) # 手动应答的确认信号
# 确定监听队列
channel.basic_consume(
queue=queue_name,
auto_ack=False, # 手动应答,必须等该消费者的确认信号后才删除消息
on_message_callback=callback
)
# 开始监听
channel.start_consuming()
"""
与简单模式的区别:
1、auto_ack改为手动应答
2、回调函数增加确认信号
"""
持久化
介绍:当中间件RabbitMQ崩溃重启时,队列中的数据都存在内存中,故会全部丢失。持久化,会将数据存储在磁盘,重启后,能将数据重新读取到队列中,保证数据的安全。
生产者
# 创建可持久化队列
queue_name = 'third' # 已有的队列的类型不可更改
channel.queue_declare(queue=queue_name, durable=True) # 使用durable
msg = 'Hello World!!'
channel.basic_publish(
exchange='',
routing_key=queue_name,
body=msg,
properties=pika.BasicProperties(
delivery_mode=2, # 指定该信息为持久化保存,其他值为瞬态(即不持久化)
# content_type='application/json'
)
)
"""
与简单模式的区别:
1、定义持久化队列:durable=True
2、将信息指定为持久化类型:delivery_mode=2
"""
消费者
# 创建队列
queue_name = 'third'
channel.queue_declare(queue=queue_name, durable=True) # 队列的类型需要保持一致
# 其余参考简单模式,也可使用 手动应答
公平分发
介绍:消息默认是按照轮询分发的,即每个消费者1条消息,循环往复。但消息的处理耗时是不同的,为了高性能,会将消息优先发送给“空闲”的消费者。如:consumer01阻塞,而consumer02已完成消息处理,下一轮本应发给consumer01的消息会发给consumer02,直到consumer01完成消息处理后才会接受下一个消息。
生产者
# 创建队列
queue_name = 'fourth'
channel.queue_declare(queue=queue_name)
# 向队列插入数据
for i in range(1, 11):
body = f'Hello World! {i}'
channel.basic_publish(
exchange='', # 简单模式
routing_key=queue_name, # 指定队列
body=body,
)
"""与 简单模式 相同"""
消费者
# 创建队列(生产者和消费者均需定义队列)
queue_name = 'fourth'
channel.queue_declare(queue=queue_name)
# 定义回调函数
def callback(ch, method, propertes, body):
import time
print(f'Received {body.decode()}')
time.sleep(10) # 通过改变时间来模拟阻塞
print('Received Done!')
ch.basic_ack(delivery_tag=method.delivery_tag)
# 公平分发
channel.basic_qos(prefetch_count=1)
# 确定监听队列
channel.basic_consume(
queue=queue_name,
auto_ack=False,
on_message_callback=callback
)
# 开始监听
channel.start_consuming()
"""
与简单模式的区别:
1、定义 公平分发:prefetch_count=1
2、需使用 手动应答模式
"""
交换机
介绍:将一个消息同时发给多个队列
发布订阅
介绍:将消息发送给所有绑定了该交换机的队列
生产者
# 创建交换机
exchange_name = 'first_exchange'
channel.exchange_declare(
exchange=exchange_name,
exchange_type='fanout', # 扇形模式
)
msg = 'Hello World!'
channel.basic_publish(
exchange=exchange_name,
routing_key='',
body=msg
)
"""
与简单模式的区别:
1、不再需要定义队列
2、需要定义一个交换机,且指定交换机模式:fanout
"""
消费者
# 创建交换机
exchange_name = 'first_exchange'
channel.exchange_declare(
exchange=exchange_name,
exchange_type='fanout'
)
# 每次连接时,都创建随机名称的空队列
# ''代表随机创建队列
result = channel.queue_declare('', exclusive=True)
queue_name = result.method.queue # 获取队列名称
# 绑定交换机与队列
channel.queue_bind(
exchange=exchange_name,
queue=queue_name
)
def callback(ch, method, properties, body):
print(f'Received {body.decode()}')
channel.basic_consume(
queue=queue_name,
auto_ack=True, # 也可使用手动应答
on_message_callback=callback
)
channel.start_consuming()
"""
与简单模式的区别:
1、需要创建同名的交换机
2、消费者创建一个随机空队列,且需要绑定交换机
"""
关键字
介绍:将消息发送给所有绑定了该交换机的“指定”队列,队列需要指定绑定的关键字,只有生产者的消息也绑定了该关键字,才会发送给该消费者。
生产者
exchange_name = 'second_exchange'
channel.exchange_declare(
exchange=exchange_name,
exchange_type='direct' # 交换机使用direct模式
)
msg = {'info': 'info msg', 'warning': 'warning msg', 'error': 'error info'}
for key, msg_info in msg.items():
# 发布绑定了不同关键字的消息
channel.basic_publish(
exchange=exchange_name,
routing_key=key, # 指定消息绑定的关键字
body=msg_info
)
"""
与发布订阅的区别:
1、交换机的模式创建后不可更改,因此只能另创建一个交换机
2、交换机的模式:direct
3、发布消息时,需要绑定关键字
"""
消费者
exchange_name = 'second_exchange'
channel.exchange_declare(
exchange=exchange_name,
exchange_type='direct' # 交换机使用direct模式
)
result = channel.queue_declare('', exclusive=True)
queue_name = result.method.queue
for key in ['info', 'warning', 'error']:
# 为队列绑定交换机
channel.queue_bind(
exchange=exchange_name,
queue=queue_name,
routing_key=key # 关键字
)
def callback(ch, method, properties, body):
print(f'Received {body.decode()}')
channel.basic_consume(
queue=queue_name,
auto_ack=True,
on_message_callback=callback
)
channel.start_consuming()
"""
与发布订阅的区别:
1、队列绑定交换机时,需指定关键字
2、队列绑定一次只能绑定一个关键字,但可循环绑定多次,则包含多个关键字
"""
通配符
介绍:关键字模式要求绑定的关键字完全一致,但通配符模式可进行模糊匹配,将消息更细化的绑定。
生产者
exchange_name = 'third_exchange'
channel.exchange_declare(
exchange=exchange_name,
exchange_type='topic' # 交换机使用topic模式
)
msg = {'boy.learn': 'boy_learn', 'boy.play': 'boy_play',
'girl.learn': 'girl_learn', 'girl.play': 'girl_play'}
for key, msg_info in msg.items():
# 发布绑定了不同关键字的消息
channel.basic_publish(
exchange=exchange_name,
routing_key=key,
body=msg_info
)
"""
与关键字模式的区别:
1、交换机的模式:topic
2、关键字可使用多层,以.分割,如:key1.key2.keyN···
"""
消费者
exchange_name = 'third_exchange'
channel.exchange_declare(
exchange=exchange_name,
exchange_type='topic'
)
result = channel.queue_declare('', exclusive=True)
queue_name = result.method.queue
key = '#.learn'
# key = '#.play'
# key = 'girl.*'
# key = '#' # 使用此方法,则匹配所有,相当于 发布订阅模式
channel.queue_bind(
exchange=exchange_name,
queue=queue_name,
routing_key=key
)
def callback(ch, method, properties, body):
print(f'{method.routing_key} Received {body.decode()}')
channel.basic_consume(
queue=queue_name,
auto_ack=True,
on_message_callback=callback
)
channel.start_consuming()
"""
与关键字模式的区别:
1、交换机的模式:topic
2、关键字可使用通配符来模糊匹配
3、通配符只有两种:#和*
# 匹配 一个或多个词
* 仅匹配一个词
"""
事务
TODO
高级功能
TODO
其他
启动 RabbitMQ服务:rabbitmq-service.bat start
关闭 RabbitMQ服务:rabbitmq-service.bat stop
注:
1、erlang与RabbitMQ版本的对应
遇上方知有

浙公网安备 33010602011771号