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版本的对应

posted @ 2022-05-29 01:56  F___Q  阅读(46)  评论(0)    收藏  举报