python 消息队列_Python- rabbitMQ消息队列的使用

rabbitMQ消息队列

消息(Message)是指在应用间传输的数据

但是,如果你没有时间看英文文档,或者想看到一些总结性的内容,还是可以继续读下去的。

python 消息队列_Python- rabbitMQ消息队列的使用_第1张图片

首先,来看一下整体的架构图,并介绍一些基本概念:

channel:信道是生产者,消费者和 RabbitMQ 通信的渠道,是建立在 TCP 连接上的虚拟连接。一个 TCP 连接上可以建立成百上千个信道,通过这种方式,可以减少系统开销,提高性能。

Broker:接收客户端连接,实现 AMQP 协议的消息队列和路由功能的进程。

Virtual Host:虚拟主机的概念,类似权限控制组,一个 Virtual Host 里可以有多个 Exchange 和 Queue,权限控制的最小粒度是 Virtual Host。

Exchange:交换机,接收生产者发送的消息,并根据 Routing Key 将消息路由到服务器中的队列 Queue。

ExchangeType:交换机类型决定了路由消息的行为,RabbitMQ 中有三种 Exchange 类型,分别是 direct、fanout、topic。

Message Queue:消息队列,用于存储还未被消费者消费的消息,由 Header 和 body 组成。Header 是由生产者添加的各种属性的集合,包括 Message 是否被持久化、优先级是多少、由哪个 Message Queue 接收等,body 是真正需要发送的数据内容。

BindingKey:绑定关键字,将一个特定的 Exchange 和一个特定的 Queue 绑定起来。

了解了基本概念之后,就开始写代码吧。本文使用 Python 开发,需要先安装 Pika,版本信息如下:

安装准备工具

1.下载Eralng,底下连接已提供otp_win64_20.2.exe链接:.提取码:3gpa

2.下载rabbitmq,底下链接已提供rabbitmq-server-3.7.4.exe链接:.提取码:vzar

安装步骤(图文)

1.第一步:安装otp_win64_20.2.exe右键以管理员身份运行

python 消息队列_Python- rabbitMQ消息队列的使用_第2张图片接着一直点击下一步傻瓜式安装2.第二步:安装rabbitmq-server-3.7.4.exe双击文件rabbitmq-server-3.7.4.exe,傻瓜式安装,(注意不要安装在包含中文和空格的目录下!安装后window服务中就存在rabbitMQ了,并且是启动状态。 )接着安装管理界面(插件)

进入rabbitMQ安装目录的sbin目录

点击上方的路径框输入cmd,按下回车键python 消息队列_Python- rabbitMQ消息队列的使用_第3张图片python 消息队列_Python- rabbitMQ消息队列的使用_第4张图片python 消息队列_Python- rabbitMQ消息队列的使用_第5张图片

输入命令点击回车

rabbitmq-plugins enable rabbitmq_management

python 消息队列_Python- rabbitMQ消息队列的使用_第6张图片第三步:1.重启服务,双击rabbitmq-server.bat(双击后可能需要等待一会)python 消息队列_Python- rabbitMQ消息队列的使用_第7张图片2.打开浏览器,地址栏输入,即可看到管理界面的登陆页python 消息队列_Python- rabbitMQ消息队列的使用_第8张图片输入用户名和密码,都为guest 进入主界面:python 消息队列_Python- rabbitMQ消息队列的使用_第9张图片最上侧的导航以此是:概览、连接、信道、交换器、队列、用户管理

安装链接python的驱动

pip install pika

消费者生产者:

python 消息队列_Python- rabbitMQ消息队列的使用_第10张图片

基本使用

说明:一对多模式,一个生产者,多个消费者,一个队列,每个消费者从队列中获取唯一的消息。

简单模式

生产者:

importpika

​#1.连接rabbitmq服务器

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))#2.创建队列

channel =connection.channel()

​#3.声明一个名为'hello'的队列

channel.queue_declare(queue='hello', durable=True)

​#4.如果exchange为空,则是简单模式:向hello队列中插入字符串hello world!

channel.basic_publish(exchange='',

routing_key='hello',

body='hello world!',#消息持久化

properties=pika.BasicProperties(

delivery_mode=2,

))

​print('send hello world!')#5.关闭连接

channel.close()

消费者:

importpika

​#1.连接rabbitmq服务器

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))#2.创建队列

channel =connection.channel()

​#3.声明一个名为'hello'的队列 谁先发的请求谁就先创建队列,队列不会重复创建

channel.queue_declare(queue='hello', durable=True)

​#4.构建回调函数

defcallback(ch, method, properties, body):

​print("[x] Received %r" %body)

​#发送应答信号,表示数据已经处理完毕,可以删除

ch.basic_ack(delivery_tag=method.delivery_tag)

​#5.确认监听队列queue:hello,一旦有值出现,则出发回调函数,callback#在订阅消息的时候可以指定应答模式,当auto_ack等于true的时候,表示当消费者一收到消息就表示消费者收到了消息,#消费者收到了消息就会立即从队列中删除。

channel.basic_consume(queue='hello',

auto_ack=False,

on_message_callback=callback)#6. 持续监听

print('[*] Waiting for messages. To exit press CTRL+C')

channel.start_consuming()

描述:将交换器、队列和消息都设置持久化之后就能保证数据不会被丢失吗?当然不是,多个方面:

消费者端: 消费者订阅队列将autoAck设置为true,虽然消费者接收到了消息,但是没有来得及处理就宕机了,那数据也会丢失,解决方案就是以为手动确认接收消息,待处理完消息之后,手动删除消息

解决方案:应答模式

自动ACK:消息一旦被接收,消费者自动发送ACK

手动ACK:消息接收后,不会发送ACK,需要手动调用

- 如果消息不太重要,丢失也没有影响,那么自动ACK会比较方便

- 如果消息非常重要,不容丢失。那么最好在消费完成后手动ACK,否则接收消息后就自动ACK,RabbitMQ就会把消息从队列中删除。如果此时消费者宕机,那么消息就丢失了。

#发送应答信号,表示数据已经处理完毕,可以删除#构建回调函数

defcallback(ch, method, properties, body):

​print("[x] Received %r" %body)

​#发送应答信号,表示数据已经处理完毕,可以删除

ch.basic_ack(delivery_tag=method.delivery_tag)#在订阅消息的时候可以指定应答模式,当auto_ack等于True的时候,表示自动,False表示手动,

channel.basic_consume(queue='hello',

auto_ack=False,

on_message_callback=callback)

在rabbitmq服务端,如果消息正确被发送,但是rabbitmq未来得及持久化,没有将数据写入磁盘,服务异常而导致数据丢失,解决方案,可以通过rabbitmq集群的方式实现消息中间件的高可用性

持久化

#持久化是为提高rabbitmq消息的可靠性,防止在异常情况(重启,关闭,宕机)下数据的丢失#durable:设置是否执行持久化。如果设置为true,即durable=true,持久化实现的重要参数

channel.queue_declare(queue='hello', durable=True)#消息持久化delivery_mode=2 2是存在在硬盘 1是默认存储在内存

properties=pika.BasicProperties(

delivery_mode=2,

)

发布/订阅模式

说明:生产者将消息发送给 broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息。消费者监听自己的队列并进行消费。

发布订阅模式图解:

python 消息队列_Python- rabbitMQ消息队列的使用_第11张图片

代码:

发布者:

importpikaimportsys'''什么是发布/订阅:

在上一个实例中,我们搭建了一个工作队列,每个任务只分发给一个工作者(worker)。

在本实例中,我们要做的跟之前完全不一样 即:分发一个消息给多个消费者(consumers)。这种模式被称为“发布/订阅”。'''

#发布订阅--发布者#建立一个链接

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))#链接rabbitmq

channel =connection.channel()

​#在调用queue_declare方法的时候,不提供queue参数就可以创建一个随机名的队列#result = channel.queue_declare()

​#使用交换机,这里并没有使用指定的一个队列,而是使用交换机,这里使用的交换机是fanout交换机,并且指定交换机名为logs

channel.exchange_declare(exchange='logs',exchange_type='fanout')

​#获取消息发布

message = "info: Hello World!"

#开始发布消息#你会注意到routing_key为空,这是因为,它的值会被扇型交换机(fanout exchange)忽略。

channel.basic_publish(exchange='logs',

routing_key='',

body=message)

​print("[x] Sent %r"%message)#关闭链接

connection.close()

订阅者:

importpikaimporttime

connection= pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))

channel=connection.channel()

channel.exchange_declare(exchange='logs', exchange_type='fanout')

​#因为发布订阅用的是随机名队列,所以当与消费者断开连接的时候,这个队列应当被立即删除。exclusive标识符即可达到此目的。

result = channel.queue_declare('', exclusive=True)

​#在之前的实例中我们都要事先知道,队列的名字,这里采用的就是默认获取

queue_name =result.method.queue

​#开始绑定:将消费者绑定到和交换机同一个频道,这样logs交换机发来的消息,消费者就可以收到了

channel.queue_bind(exchange='logs', queue=queue_name)

​print('[*] Waiting for logs. To exit press CTRL+C')

​defcallback(ch, method, properties, body):print("[x] Received %r" %body.decode())

time.sleep(body.decode().count('.'))print("[x] Done")

channel.basic_consume(queue=queue_name,

on_message_callback=callback)

channel.start_consuming()#rabbitmqctl list_bindings 列出所有现存的绑定。#绑定的另外一种理解:#绑定(binding)是指交换机(exchange)和队列(queue)的关系。#可以简单理解为:这个队列(queue)对这个交换机(exchange)的消息感兴趣。

路由模式 Routing

说明:生产者将消息发送给 broker,由交换机根据routing_key分发到不同的消息队列,然后消费者同样根据routing_key来消费对应队列上的消息。

图解:

python 消息队列_Python- rabbitMQ消息队列的使用_第12张图片

代码

生产者:

#!/usr/bin/env python

importpikaimportsys#连接rabbitmq

connection =pika.BlockingConnection(

pika.ConnectionParameters(host='localhost'))#创建关系

channel =connection.channel()

​#声明交换机模式

channel.exchange_declare(exchange='direct_logs', exchange_type='direct')

​#判断是那个关键字

severity = sys.argv[1] if len(sys.argv) > 1 else 'info'message= ' '.join(sys.argv[2:]) or 'Hello World!'​#向交换机发数据 routing_key交换机向那个关键字队列发送数据

channel.basic_publish(

exchange='direct_logs', routing_key=severity, body=message)print("[x] Sent %r:%r" %(severity, message))

connection.close()

消费者:

#!/usr/bin/env python

importpikaimportsys#连接rabbitmq

connection =pika.BlockingConnection(

pika.ConnectionParameters(host='localhost'))#创建关系

channel =connection.channel()

​#声明交换机模式

channel.exchange_declare(exchange='direct_logs', exchange_type='direct')

​#创建队列

result = channel.queue_declare(queue='', exclusive=True)

queue_name=result.method.queue

​#设置关键字

severities = sys.argv[1:]if notseverities:

sys.stderr.write("Usage: %s [info] [warning] [error]\n" %sys.argv[0])

sys.exit(1)

​#routing_key = [info] [warning] [error] 其中一个#进行队列的绑定

for severity inseverities:

channel.queue_bind(

exchange='direct_logs', queue=queue_name, routing_key=severity)

​print('[*] Waiting for logs. To exit press CTRL+C')

​#回调函数

defcallback(ch, method, properties, body):print("[x] %r:%r" %(method.routing_key, body))

​#开始发布消息

channel.basic_consume(

queue=queue_name, on_message_callback=callback, auto_ack=True)

channel.start_consuming()

主题模式 Topics

图解

python 消息队列_Python- rabbitMQ消息队列的使用_第13张图片

说明:其实,主题模式应该算是路由模式的一种,也是通过routing_key来分发,只不过是routing_key支持了正则表达式,更加灵活。

# 表示可以匹配0个或多个单词

* 表示只能匹配一个单词

代码:

生产者:

#!/usr/bin/env python

importpikaimportsys#连接

connection =pika.BlockingConnection(

pika.ConnectionParameters(host='localhost'))

channel=connection.channel()

​#声明交换机

channel.exchange_declare(exchange='topic_logs', exchange_type='topic')

routing_key= sys.argv[1] if len(sys.argv) > 2 else 'anonymous.info'message= ' '.join(sys.argv[2:]) or 'Hello World!'​#发布

channel.basic_publish(

exchange='topic_logs', routing_key=routing_key, body=message)print("[x] Sent %r:%r" %(routing_key, message))

connection.close()

消费者:

#!/usr/bin/env python

importpikaimportsys

connection=pika.BlockingConnection(

pika.ConnectionParameters(host='localhost'))

channel=connection.channel()

channel.exchange_declare(exchange='topic_logs', exchange_type='topic')

result= channel.queue_declare('', exclusive=True)

queue_name=result.method.queue

binding_keys= sys.argv[1:]if notbinding_keys:

sys.stderr.write("Usage: %s [binding_key]...\n" %sys.argv[0])

sys.exit(1)

​for binding_key inbinding_keys:

channel.queue_bind(

exchange='topic_logs', queue=queue_name, routing_key=binding_key)

​print('[*] Waiting for logs. To exit press CTRL+C')

​defcallback(ch, method, properties, body):print("[x] %r:%r" %(method.routing_key, body))

channel.basic_consume(

queue=queue_name, on_message_callback=callback, auto_ack=True)

channel.start_consuming()

RPC 模式 RPC

图解

python 消息队列_Python- rabbitMQ消息队列的使用_第14张图片

说明:通过消息队列来实现 RPC 功能,客户端发送消息到消费队列,消息内容其实就是服务端执行需要的参数,服务端消费消息内容,执行程序,然后将结果返回给客户端。

代码

生产者代码:

#!/usr/bin/env python

importpikaimportuuid

​classFibonacciRpcClient(object):

​def __init__(self):

self.connection=pika.BlockingConnection(

pika.ConnectionParameters(host='localhost'))

self.channel=self.connection.channel()

result= self.channel.queue_declare(queue='', exclusive=True)

self.callback_queue=result.method.queue

self.channel.basic_consume(

queue=self.callback_queue,

on_message_callback=self.on_response,

auto_ack=True)

​defon_response(self, ch, method, props, body):if self.corr_id ==props.correlation_id:

self.response=body

​defcall(self, n):

self.response=None

self.corr_id=str(uuid.uuid4())

self.channel.basic_publish(

exchange='',

routing_key='rpc_queue',

properties=pika.BasicProperties(

reply_to=self.callback_queue,

correlation_id=self.corr_id,

),

body=str(n))while self.response isNone:

self.connection.process_data_events()returnint(self.response)

fibonacci_rpc=FibonacciRpcClient()

​print("[x] Requesting fib(30)")

response= fibonacci_rpc.call(30)print("[.] Got %r" % response)

消费者代码:

#!/usr/bin/env python

importpika

connection=pika.BlockingConnection(

pika.ConnectionParameters(host='localhost'))

channel=connection.channel()

channel.queue_declare(queue='rpc_queue')

​deffib(n):if n ==0:return0elif n == 1:return 1

else:return fib(n - 1) + fib(n - 2)

​defon_request(ch, method, props, body):

n=int(body)

​print("[.] fib(%s)" %n)

response=fib(n)

ch.basic_publish(exchange='',

routing_key=props.reply_to,

properties=pika.BasicProperties(correlation_id=props.correlation_id),

body=str(response))

ch.basic_ack(delivery_tag=method.delivery_tag)

channel.basic_qos(prefetch_count=1)

channel.basic_consume(queue='rpc_queue', on_message_callback=on_request)

​print("[x] Awaiting RPC requests")

channel.start_consuming()

总结

以上就是本文的全部内容,其中 Publish/Subscribe,Routing,Topics 三种模式可以统一归为 Exchange 模式,只是创建时交换机的类型不一样,分别是 fanout、direct、topic。

一般我们只会使用简单模式、发布订阅模式、路由模式,而主题模式和RPC模式很少使用了解即可。

你可能感兴趣的:(python,消息队列)