环境配置及安装

  1. 可以使用现成的类库pika、txAMQP或者py-amqplib,本例选择pika
pip install pika

生产者和消费者实现

生产者

import pika

# 证书认证,默认账密(guest,guest)
credentials = pika.PlainCredentials('guest', 'guest')
# 建立连接,虚拟主机需要指定参数`virtual_host`,默认`\`
connection = pika.BlockingConnection(pika.ConnectionParameters(host='127.0.0.1', port=5672))
# 实例化消息队列
channel = connection.channel()
# 声明消息队列,消息将在创建的队列中传递,不存在则创建,`durable=True`表示消息队列持久化存储,False非持久化存储
channel.queue_declare(queue='queue_test', durable=True)

# 消息入队,`routing_key`为队列名,`body`为消息体
# delivery_mode = 2 声明消息在队列中持久化,delivery_mod = 1 消息非持久化,消息持久化可以将消息从内存转存到硬盘,保证rabbitmq重启后,消息不会丢失
channel.basic_publish(exchange='',
                      routing_key='queue_test',
                      body="this is a message",
                      properties=pika.BasicProperties(delivery_mode=1)
                      )
# 关闭连接
connection.close()

消费者

import pika

# 证书认证,默认账密(guest,guest)
credentials = pika.PlainCredentials('guest', 'guest')
# 建立连接,虚拟主机需要指定参数`virtual_host`,默认`\`
connection = pika.BlockingConnection(pika.ConnectionParameters(host='127.0.0.1', port=5672))
# 实例化消息队列
channel = connection.channel()
# 声明消息队列,消息将在创建的队列中传递,不存在则创建
channel.queue_declare(queue='queue_test', durable=True)


def callback(ch, method, properties, body):
    """ 定义回调方法,处理消息队列中的消息 """
    # 需要发送确认消息
    ch.basic_ack(delivery_tag=method.delivery_tag)
    print("Received %s" % body)


# 绑定回调函数及消息队列
channel.basic_consume(on_message_callback=callback,
                      queue='queue_test',
                      auto_ack=False
                      )
"""
`no_ack=True`:表示没有返回消息,即无论调用callback成功与否,消息都被消费掉;
			   False时,在调用callback函数时,未收到确认标识,消息会重回队列。
`auto_ack=True`:表示RabbitMQ认定消息一旦分发,就认为接收成功,不会等待消息回复,立即将缓存中的该消息删除,在消费端异常时会导致该消息丢失;
   			     False时,表示消费者拿到消息后等待自动回复,成功则会删除该消息
"""

# 开始监听,并进入阻塞状态,队列中有消息就会调用回调方法进行处理
channel.start_consuming()

RabbitMQ持久化

  • 队列声明持久化
# `durable = True`代表消息队列持久化存储,反之非持久化存储。
result = channel.queue_declare(queue='queue-test',durable=True)
  • 交换机声明持久化
# `durable = True`代表交换机持久化存储,反之非持久化存储
channel.exchange_declare(exchange='exchange-test',durable=True)

注意:如果已存在一个非持久化的 queueexchangedurable=True会报错,因为当前状态不能更改 queueexchange存储属性,需要删除重建。

如果 queueexchange中一个声明了持久化,另一个没有声明持久化,则不允许绑定。

  • 消息持久化
  • 虽然 queueexchange 都申明了持久化,但如果消息只存在内存里,rabbitmq 重启后,内存里的消息还是会丢失。所以必须声明消息也是持久化,从内存转存到硬盘。
# `delivery_mode=2` 声明消息在队列中持久化,`delivery_mod=1`表示消息非持久化
channel.basic_publish(exchange='',
                      routing_key='queue-test',
                      body='test',
                      properties=pika.BasicProperties(delivery_mode=2)
                     )
  • acknowledgement 消息不丢失(异常反馈,消息重发)
  • 消费者(consumer)调用callback函数时,会存在处理消息失败的风险,如果处理失败,则消息丢失。但是也可以选择消费者处理失败时,将消息回退给RabbitMQ,重新再被消费者消费
"""
`no_ack=True`:表示没有返回消息,即无论调用callback成功与否,消息都被消费掉;
			   False时,在调用callback函数时,未收到确认标识,消息会重回队列。
`auto_ack=True`:表示RabbitMQ认定消息一旦分发,就认为接收成功,不会等待消息回复,立即将缓存中的该消息删除,在消费端异常时会导致该消息丢失;
   			     False时,表示消费者拿到消息后等待自动回复,成功则会删除该消息
"""
channel.basic_consume(callback,
                      queue = 'python-test',
                      no_ack = False
                     )

RabbitMQ发布与订阅

RabbitMQ的发布与订阅要借助交换机(Exchange)实现。

共三种工作模式:fanoutdirecttopicd

fanout模式

这种模式下,传递到 exchange 的消息将会转发到所有与其绑定的 queue 上。

  • 不需要指定 routing_key ,即使指定了也是无效。
  • 需要提前将 exchange 和 queue 绑定,一个 exchange 可以绑定多个 queue,一个queue可以绑定多个exchange。
  • 需要先启动 订阅者,此模式下的队列是 consumer 随机生成的,发布者 仅仅发布消息到 exchange ,由 exchange 转发消息至 queue。
  • 生产者
import pika
import json

credentials = pika.PlainCredentials('guest', 'guest')
# 虚拟队列需要指定参数 virtual_host,如果是默认的可以不填。
connection = pika.BlockingConnection(pika.ConnectionParameters(host = '127.0.0.1',port = 5672,virtual_host = '/',credentials = credentials))
channel=connection.channel()
# 声明exchange,由exchange指定消息在哪个队列传递,如不存在,则创建。durable = True 代表exchange持久化存储,False 非持久化存储
channel.exchange_declare(exchange = 'python-test',durable = True, exchange_type='fanout')for i in range(10):
    message=json.dumps({'OrderId':"1000%s"%i})
# 向队列插入数值 routing_key是队列名。delivery_mode = 2 声明消息在队列中持久化,delivery_mod = 1 消息非持久化。routing_key 不需要配置
    channel.basic_publish(exchange='python-test',
                          routing_key='',
                          body = message,
                          properties=pika.BasicProperties(delivery_mode=2)
                         )
connection.close()
  • 消费者
import pika

credentials = pika.PlainCredentials('guest', 'guest')
connection = pika.BlockingConnection(pika.ConnectionParameters(host = '127.0.0.1',port = 5672,virtual_host = '/',credentials = credentials))
channel = connection.channel()
# 创建临时队列,队列名传空字符,consumer关闭后,队列自动删除
result = channel.queue_declare('',exclusive=True)
# 声明exchange,由exchange指定消息在哪个队列传递,如不存在,则创建。durable = True 代表exchange持久化存储,False 非持久化存储
channel.exchange_declare(exchange = 'python-test',durable = True, exchange_type='fanout')
# 绑定exchange和队列  exchange 能够确切地指定消息应该到哪个队列去
channel.queue_bind(exchange = 'python-test',queue = result.method.queue)
# 定义一个回调函数来处理消息队列中的消息,这里是打印出来
def callback(ch, method, properties, body):
    ch.basic_ack(delivery_tag = method.delivery_tag)
    print(body.decode())
    
channel.basic_consume(result.method.queue,callback,auto_ack = False)
channel.start_consuming()

direct模式

这种工作模式的原理是 消息发送至 exchange,exchange 根据 **路由键(routing_key)**转发到相对应的 queue 上。

  • 可以使用默认 exchange =’ ’ ,也可以自定义 exchange
  • 这种模式下不需要将 exchange 和 任何进行绑定,当然绑定也是可以的。可以将 exchange 和 queue ,routing_key 和 queue 进行绑定
  • 传递或接受消息时 需要 指定 routing_key
  • 需要先启动 订阅者,此模式下的队列是 consumer 随机生成的,发布者 仅仅发布消息到 exchange ,由 exchange 转发消息至 queue。
  • 生产者
import pika
import json

credentials = pika.PlainCredentials('guest', 'guest')
connection = pika.BlockingConnection(pika.ConnectionParameters(host = '127.0.0.1',port = 5672,virtual_host = '/',credentials = credentials))
channel=connection.channel()
# 声明exchange,由exchange指定消息在哪个队列传递,如不存在,则创建。durable = True 代表exchange持久化存储,False 非持久化存储
channel.exchange_declare(exchange = 'python-test',durable = True, exchange_type='direct')

for i in range(10):
    message=json.dumps({'OrderId':"1000%s"%i})
# 指定 routing_key。delivery_mode = 2 声明消息在队列中持久化,delivery_mod = 1 消息非持久化
    channel.basic_publish(exchange='python-test',
                          routing_key='OrderId',
                          body = message,
                          properties=pika.BasicProperties(delivery_mode = 2)
                         )
connection.close()
  • 消费者
import pika

credentials = pika.PlainCredentials('guest', 'guest')
connection = pika.BlockingConnection(pika.ConnectionParameters(host = '127.0.0.1',port = 5672,virtual_host = '/',credentials = credentials))
channel = connection.channel()
# 创建临时队列,队列名传空字符,consumer关闭后,队列自动删除
result = channel.queue_declare('',exclusive=True)
# 声明exchange,由exchange指定消息在哪个队列传递,如不存在,则创建。durable = True 代表exchange持久化存储,False 非持久化存储
channel.exchange_declare(exchange = 'python-test',durable = True, exchange_type='direct')
# 绑定exchange和队列  exchange 使我们能够确切地指定消息应该到哪个队列去
channel.queue_bind(exchange = 'python-test',queue = result.method.queue,routing_key='OrderId')
# 定义一个回调函数来处理消息队列中的消息,这里是打印出来
def callback(ch, method, properties, body):
    ch.basic_ack(delivery_tag = method.delivery_tag)
    print(body.decode())

#channel.basic_qos(prefetch_count=1)
channel.basic_consume(result.method.queue,callback, auto_ack = False)
channel.start_consuming()

topicd模式

这种模式类似direct模式,exchange 也是通过 路由键 routing_key 来转发消息到指定的 queue 。 不同点是 routing_key 使用正则表达式支持模糊匹配,但匹配规则又与常规的正则表达式不同,比如“#”是匹配全部,“*”是匹配一个词。

.ex:routing_key = "#demo#",即将消息转发至所有 routing_key 包含 “demo” 字符的队列中。