python操作rabbitmq

328 阅读6分钟

1 rabbitmq前提

1-1 什么是rabbitmq

实现发布/订阅,异步处理,或者工作队列

2-2 rabbitmq的安装

docker-compose.yml

version: "3"
services:
  rabbitmq: 
    container_name: rabbitmq
    ports:
      - 15672:15672
      - 5672:5672
    restart: always
    volumes:
      - /etc/localtime:/etc/localtime
      - /home/mycontainers/rabbitmq/rabbitmq:/var/lib/rabbitmq
    environment:
      - RABBITMQ_DEFAULT_USER=admin
      - RABBITMQ_DEFAULT_PASS=admin
    image: rabbitmq:3.8.34-management 

docker命令

docker container ls -a # 查看docker所有的容器
docker image ls -a # 查看docker所有的镜像
docker-compose up -d # 使用docker-compose 部署项目,加d表示后台运行
docker system prune -f -a # 清理没有用的镜像

2 rabbitmq简单使用

2-1 rabbitm1初始

producer

  #生产者
  import pika

  #链接rabbitmq
  creadentials = pika.PlainCredentials(username='用户名',password='密码')
  connection = pika.BlockingConnection(pika.ConnectionParameters(host='地址',port=端口))
  channel = connection.channel()

  #创建队列
  channel.queue_declare(queue='ceshi')

  #向指定队列插入数据 [exchange:交换机模式,简单模式为空   routing_key:指定队列  body:要插入的值]
  channel.basic_publish(exchange='',routing_key='ceshi',body='hello world!')
  print('[x]--')

consumer

  #消费者
  import pika

  #创建链接
  pika.PlainCredentials(username='用户名',password='密码')
  connection = pika.BlockingConnection(pika.ConnectionParameters(host='地址',port=端口))
  channel = connection.channel()

  #创建队列
  channel.queue_declare(queue='ceshi')

  #确定回调函数
  def callback(ch,method,properties,body):
  print("[x]:",body)

  #确定监听队列 [auto_ack:默认应答]
  channel.basic_consume(queue='ceshi',auto_ack=True,on_message_callback=callback)
  print('[*]:---')
  #正式监听
  channel.start_consuming()

2-2 参数说明

1.应答参数

消费者确定监听队列时的一个参数
auto_ack
  True:默认应答:
  从队列取走一条数据后队列里这条数据就不存在了,如果在数据处理过程中程序出现问题,会造成数据丢失现象
  False:手动应答:
  从队列取走一条数据后,这条数据还存在于队列中
  ch.basic_ack(delivery_tag=method.delivery_tag)添加在回调函数最后 
  最后执行这条命令会告诉队列,我已经执行完成了,可以删除这条数据了
  手动应答必然会影响效率,具体根据项目需求抉择:是追求数据安全还是效率

2.持久化

  将数据保存到磁盘,防止程序运行中途rabbitmq服务异常导致数据丢失
  生产者
  创建队列时进行申明 durable=True 
  channel.queue_declare(queue='ceshi',durable=True) 
  插入数据时申明 properties=pika.BasicProperties(delivery_mode=2)
  channel.basic_publish(exchange='',
      routing_key='ceshi',
      body='hello world!',
      properties=pika.BasicProperties(
          delivery_mode=2)
      )
  消费者
  创建队列时进行申明 durable=True
  channel.queue_declare(queue='ceshi2',durable=True)

3.分发参数

  轮询分发
  正常开启多个消费者都是轮询分发,比如队列有8个数据,每人4个
  公平分发
  处理快的获取到的数据便越多,,公平分发需要使用手动应答方式才可以
  消费者添加 channel.basic_qos(prefetch_count=1)

3 AMQP

3-1 模型简介

AMQP 0-9-1的工作过程如下图:消息(message)被发布者(publisher)发送给交换机(exchange),交换机常常被比喻成邮局或者邮箱。然后交换机将收到的消息根据路由规则分发给绑定的队列(queue)。最后AMQP代理会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取。

enter image description here

发布者(publisher)发布消息时可以给消息指定各种消息属性(message meta-data)。有些属性有可能会被消息代理(brokers)使用,然而其他的属性则是完全不透明的,它们只能被接收消息的应用所使用。

从安全角度考虑,网络是不可靠的,接收消息的应用也有可能在处理消息的时候失败。基于此原因,AMQP模块包含了一个消息确认(message acknowledgements)的概念:当一个消息从队列中投递给消费者后(consumer),消费者会通知一下消息代理(broker),这个可以是自动的也可以由处理消息的应用的开发者执行。当“消息确认”被启用的时候,消息代理不会完全将消息从队列中删除,直到它收到来自消费者的确认回执(acknowledgement)。

在某些情况下,例如当一个消息无法被成功路由时,消息或许会被返回给发布者并被丢弃。或者,如果消息代理执行了延期操作,消息会被放入一个所谓的死信队列中。此时,消息发布者可以选择某些参数来处理这些特殊情况。

队列,交换机和绑定统称为AMQP实体(AMQP entities)

3-2 交换机

交换机是用来发送消息的AMQP实体。交换机拿到一个消息之后将它路由给一个或零个队列。它使用哪种路由算法是由交换机类型和被称作绑定(bindings)的规则所决定的。AMQP 0-9-1的代理提供了四种交换机

Name(交换机类型)Default pre-declared names(预声明的默认名称)
Direct exchange(直连交换机)(Empty string) and amq.direct
Fanout exchange(扇型交换机)amq.fanout
Topic exchange(主题交换机)amq.topic
Headers exchange(头交换机)amq.match (and amq.headers in RabbitMQ)

更多AMQP介绍请查看

rabbitmq.mr-ping.com/AMQP/AMQP_0…

4 rabbit中灵活使用交换机

4-1 扇形交换机(发布订阅模式)

producer.py

import pika  
  
from rabbitmq.config import MQ_USER, MQ_PWD, MQ_HOST, MQ_PORT  
  
# 创建链接  
  
credentials = pika.PlainCredentials(MQ_USER, MQ_PWD)  
connection = pika.BlockingConnection(pika.ConnectionParameters(MQ_HOST, MQ_PORT, '/', credentials=credentials))  
channel = connection.channel()  
  
# 创建一个扇形交换机  
channel.exchange_declare(exchange='logs', exchange_type='fanout')  
  
channel.basic_publish(exchange='logs', routing_key='', body='Hello World!')  
  
print('[x]---')  
connection.close()

consumer

# 消费者  
import pika  
  
from rabbitmq.config import MQ_USER, MQ_PWD, MQ_HOST, MQ_PORT  
  
# 创建链接  
  
credentials = pika.PlainCredentials(MQ_USER, MQ_PWD)  
connection = pika.BlockingConnection(pika.ConnectionParameters(MQ_HOST, MQ_PORT, '/', credentials=credentials))  
channel = connection.channel()  
  
# 声明一个与生产者名称类型相同的交换机,避免先启动消费者-队列找不到交换机的情况  
channel.exchange_declare(exchange='logs',  
exchange_type='fanout') # fanout:发布订阅模式  
  
# 创建队列 exclusive:系统会创建一个随机唯一的队列名  
result = channel.queue_declare(queue='', exclusive=True)  
queue_name = result.method.queue  
print(queue_name)  
  
# 将指定队列绑定到交换机上  
channel.queue_bind(exchange='logs',  
queue=queue_name)  
  
# 确定回调函数  
def callback(ch, method, properties, body):  
print("[x]:", body)  
  
  
# 确定监听队列 [auto_ack:默认应答]  
channel.basic_consume(queue=queue_name,  
auto_ack=True,  
on_message_callback=callback)  
print('[*]:---')  
# 正式监听  
channel.start_consuming()

4-2 直连交换机

producer

# 生产者  
import pika  
  
from rabbitmq.config import MQ_USER, MQ_PWD, MQ_HOST, MQ_PORT  
  
# 链接rabbitmq  
credentials = pika.PlainCredentials(MQ_USER, MQ_PWD)  
connection = pika.BlockingConnection(pika.ConnectionParameters(MQ_HOST, MQ_PORT, '/', credentials=credentials))  
channel = connection.channel()  
  
# 声明一个名为logs类型为direct的交换机  
channel.exchange_declare(exchange='fanout_queen',  
exchange_type='direct') # direct:关键字模式  
  
# 向logs交换机插入数据 hello world! routing_key为关键字  
channel.basic_publish(exchange='fanout_queen',  
routing_key='info',  
body='hello world!'.encode())  
  
print('[x]---')  
connection.close()

consumer

# 生产者  
import pika  
  
from rabbitmq.config import MQ_USER, MQ_PWD, MQ_HOST, MQ_PORT  
  
# 链接rabbitmq  
credentials = pika.PlainCredentials(MQ_USER, MQ_PWD)  
connection = pika.BlockingConnection(pika.ConnectionParameters(MQ_HOST, MQ_PORT, '/', credentials=credentials))  
channel = connection.channel()  
  
# 声明一个与生产者名称类型相同的交换机,避免先启动消费者-队列找不到交换机的情况  
channel.exchange_declare(exchange='fanout_queen',  
exchange_type='direct') # direct:关键字模式  
  
# 创建队列 exclusive:系统会创建一个随机唯一的队列名  
result = channel.queue_declare(queue='', exclusive=True)  
queue_name = result.method.queue  
print(queue_name)  
  
# 将指定队列绑定到交换机上,routing_key:关键字,多个关键字绑定多个  
channel.queue_bind(exchange='fanout_queen',  
queue=queue_name,  
routing_key='info')  
  
channel.queue_bind(exchange='fanout_queen',  
queue=queue_name,  
routing_key='error')  
  
  
# 确定回调函数  
def callback(ch, method, properties, body):  
if isinstance(body, bytes):  
body = body.decode()  
print("[x]:", body)  
  
  
# 确定监听队列 [auto_ack:默认应答]  
channel.basic_consume(queue=queue_name,  
auto_ack=True,  
on_message_callback=callback)  
print('[*]:---')  
# 正式监听  
channel.start_consuming()

4-3 主题交换机(通配符模式)

producer

# 生产者  
import pika  
  
from rabbitmq.config import MQ_USER, MQ_PWD, MQ_HOST, MQ_PORT  
  
# 链接rabbitmq  
credentials = pika.PlainCredentials(MQ_USER, MQ_PWD)  
connection = pika.BlockingConnection(pika.ConnectionParameters(MQ_HOST, MQ_PORT, '/', credentials=credentials))  
channel = connection.channel()  
  
# 声明一个名为logs类型为direct的交换机  
channel.exchange_declare(exchange='topic_queen',  
exchange_type='topic') # direct:关键字模式  
  
# 向logs交换机插入数据 hello world! routing_key为关键字  
channel.basic_publish(exchange='topic_queen',  
routing_key='andy.yongge',  
body='大家好啊'.encode())  
  
print('[x]---')  
connection.close()

consumer

# 生产者  
import pika  
  
from rabbitmq.config import MQ_USER, MQ_PWD, MQ_HOST, MQ_PORT  
  
# 链接rabbitmq  
credentials = pika.PlainCredentials(MQ_USER, MQ_PWD)  
connection = pika.BlockingConnection(pika.ConnectionParameters(MQ_HOST, MQ_PORT, '/', credentials=credentials))  
channel = connection.channel()  
  
# 声明一个与生产者名称类型相同的交换机,避免先启动消费者-队列找不到交换机的情况  
channel.exchange_declare(exchange='topic_queen',  
exchange_type='topic') # direct:关键字模式  
  
# 创建队列 exclusive:系统会创建一个随机唯一的队列名  
result = channel.queue_declare(queue='', exclusive=True)  
queue_name = result.method.queue  
print(queue_name)  
  
# 将指定队列绑定到交换机上,routing_key:关键字,多个关键字绑定多个  
channel.queue_bind(exchange='topic_queen',  
queue=queue_name,  
routing_key='#.yongge')  
  
  
# 确定回调函数  
def callback(ch, method, properties, body):  
if isinstance(body, bytes):  
body = body.decode()  
print("[x]:", body)  
  
  
# 确定监听队列 [auto_ack:默认应答]  
channel.basic_consume(queue=queue_name,  
auto_ack=True,  
on_message_callback=callback)  
print('[*]:---')  
# 正式监听  
channel.start_consuming()