RabbitMQ使用介绍(python)
在我们的项目开发过程中,我们有时会有时候有两个或者多个程序交互的情况,当然就会使用到这里的消息队列来实现。现在比较火的就是RabbitMQ,还有一些ZeroMQ ,ActiveMQ 等等,著名的openstack默认用的RabbitMQ来实现的。
python中我们使用pika模块来操作消息队列,当然Celery也是python中比较火的做分布式消息队列的模块。
1,RabbitMQ的安装
参考链接https://www.cnblogs.com/zzqit/p/10158923.html
2,RAbbitMQ常用命令
rabbitmqctl list_queues # 查看所有队列信息 rabbitmqctl list_exchanges # 查看所有交换器 rabbitmqctl list_bindings # 查看所有 rabbitmqctl reset # 清除所有队列 rabbitmqctl status # 查看运行信息 rabbitmqctl start_app # 启动应用 rabbitmqctl stop_app # 关闭应用
3,常用方法
exchange_delete # 删除交换机
queue_delete # 删除队列
queue_purge # 清除指定队列的所有的消息
queue_unbind # 队列和交换机解除绑定
basic.cancel # 清除消费者
4,最简单的发收事例
发送端(producer)
import pika # 建立一个实例
connection = pika.BlockingConnection(
pika.ConnectionParameters('localhost') # 默认端口5672,可不写
) # 声明一个管道,在管道里发消息
channel = connection.channel() # 在管道里声明queue名字为test
channel.queue_declare(queue='test') # 指明发送队列的名字跟内容
channel.basic_publish(exchange='',
routing_key='test', # queue名字
body='Hello World!' # 消息内容
) print(" [x] Sent 'Hello World!'") connection.close() # 队列关闭
消费端(consumer)
import pika # 建立实例
connection = pika.BlockingConnection(
pika.ConnectionParameters('localhost')
) # 声明管道
channel = connection.channel() # 这里声明queue的名字防止消费端先跑,服务端还没开启报错
channel.queue_declare(queue='test') # 消费端的回调函数
def callback(ch, method, properties, body):
print(" [x] Received %r" % body)
ch.basic_ack(delivery_tag = method.delivery_tag) # 告诉生成者,消息处理完成 channel.basic_consume( # 消费消息
callback, # 如果收到消息,就调用callback函数来处理消息
queue='hello', # 你要从那个队列里收消息
# no_ack=True # 为True不管消费者消费的时候是否处理完成,这条消息在队列中就没有了
) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming() # 开始消费消息
当然这里的连接方式是针对在本地连接,如果需要连接远程就采用下面的方式进行连接
credentials = pika.PlainCredentials('test', '123456') # rabbitmq登录账号 connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.12.12',5672,'/',credentials)) # 前面为ip地址,5672为端口号
3,消息持久化及公平分发
我们可能会遇到生产者服务器意外宕机了,这样我们生成的消息队列跟里面的消息就会全部没有,当然这样肯定是不行的,所以我们可以通过下面的设置消息持久化跟队列持久化来达到最终的目的。
上面的问题解决了以后,在我们实际的生产过程中,每个消费者服务器的性能是不均等的,所以我们需要根据不同服务器的性能做负载均衡,实现公平分发。当然下面已经解决了提到的这两个问题。
发送端(producer)
import pika
import sys connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')
) channel = connection.channel() channel.queue_declare(queue='test', durable=True) # 队列持久化 message = ' '.join(sys.argv[1:]) or "Hello World!" # sys.argv获取输入命令后面的参数 channel.basic_publish(exchange='',
routing_key='test', # 队列名称
body=message, # 消息体
properties=pika.BasicProperties(
delivery_mode=2, # 消息持久化
)) print(" [x] Sent %r" % message) connection.close()
消费端(consumer)
import pika connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')
) channel = connection.channel() channel.queue_declare(queue='test', durable=True) # 消费端也要写durable=True print(' [*] Waiting for messages. To exit press CTRL+C') def callback(ch, method, properties, body):
print(" [x] Received %r" % body)
ch.basic_ack(delivery_tag=method.delivery_tag) # 告诉生产者处理完成 channel.basic_qos(prefetch_count=1) # 做公平分发,如果有一个消息就不给你发了 channel.basic_consume(callback,
queue='test') channel.start_consuming()
5,广播模式
前面的效果都是生产者生产了消息以后把消息放入队列中然后等消费者来消费,当然也可以做成广播的形式,发送端发的同时,绑定的消费端就实时接受到数据,当然如果发消息的时候没绑定就不会把这个消息存下来。下面会简单介绍三种模式:fanout,direct,topic
(1)消息发布订阅(fanout)
发送端
import pika
import sys connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')
) channel = connection.channel() channel.exchange_declare(exchange='logs', # 声明广播管道
type='fanout') # 类型为fanout只要是绑定了exchange为logs的都可以接受 message = ' '.join(sys.argv[1:]) or "info: Hello World!" channel.basic_publish(exchange='logs', # 发送的广播管道
routing_key='', # 需要等消费者接入,为空,必须有
body=message) print(" [x] Sent %r" % message) connection.close()
接收端
import pika connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')
) channel = connection.channel() channel.exchange_declare(exchange='logs', # 声明和生产者一样
type='fanout') res = channel.queue_declare(exclusive=True) # 不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = res.method.queue # 分配的queue名字 channel.queue_bind(exchange='logs',
queue=queue_name) def callback(ch, method, properties, body):
print(" [x] %r" % body) channel.basic_consume(callback,
queue=queue_name,
no_ack=True) # 广播是实时的,消息不保存 channel.start_consuming()
(2)有选择的接收(direct)
前面的fanout消费者是默认接收所有的数据,但是有时候需要筛选一下,只接收自己想要的数据,就需要用到这里的direct了。
发送端
import pika
import sys connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')
) channel = connection.channel() channel.exchange_declare(exchange='direct_logs', # 跟前面的fanout差不多
type='direct') severity = sys.argv[1] if len(sys.argv) > 1 else 'info' # 发送消息级别,默认为info message = ' '.join(sys.argv[2:]) or 'Hello World!' # 发送消息内容 channel.basic_publish(exchange='direct_logs',
routing_key=severity, # 发送消息的级别
body=message) print(" [x] Sent %r:%r" % (severity, message)) connection.close()
接收端
import pika
import sys connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')
) channel = connection.channel() channel.exchange_declare(exchange='direct_logs',
type='direct') result = channel.queue_declare(exclusive=True) # #不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = result.method.queue severities = sys.argv[1:] # 获取运行脚本的参数
if not severities:
sys.stderr.write("Usage: %s [info] [warning] [error]\n" % sys.argv[0])
sys.exit(1) for severity in severities: # 循环绑定所有想接收的级别
channel.queue_bind(exchange='direct_logs',
queue=queue_name,
routing_key=severity) def callback(ch, method, properties, body):
print(" [x] %r:%r" % (method.routing_key, body)) channel.basic_consume(callback,
queue=queue_name,
no_ack=True) channel.start_consuming()
运行代码示例
python producer.py info hello # 运行发送端,级别为info,内容为hello python consumer.py info error # 接收端,接收级别为info跟error python consumer.py info warning # 接收端,接收级别为info跟warning python consumer.py error warning # 接收端,接收级别为error跟warning # 最终收到消息的有1,2两个,第三个收不到
(3)更加细致的过滤(topic)
发送端
import pika
import sys connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')
) channel = connection.channel() channel.exchange_declare(exchange='topic_logs',
type='topic') routing_key = sys.argv[1] if len(sys.argv) > 1 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()
接收端
import pika
import sys connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost')
) channel = connection.channel() channel.exchange_declare(exchange='topic_logs',
type='topic') result = channel.queue_declare(exclusive=True) # 不指定queue名字,rabbit会随机分配一个名字,exclusive=True会在使用此queue的消费者断开后,自动将queue删除
queue_name = result.method.queue binding_keys = sys.argv[1:]
if not binding_keys:
sys.stderr.write("Usage: %s [binding_key]...\n" % sys.argv[0])
sys.exit(1) for binding_key in binding_keys:
channel.queue_bind(exchange='topic_logs',
queue=queue_name,
routing_key=binding_key) def callback(ch, method, properties, body):
print(" [x] %r:%r" % (method.routing_key, body)) channel.basic_consume(callback,
queue=queue_name,
no_ack=True) channel.start_consuming()
在使用topic的情况下,接收端可以通过下面几种写法筛选自己想要接收的数据
python consumer.py *.info # 所有info结尾的
python consumer.py *.info nginx.* # 所有info结尾的和nginx开始的
python consumer.py '#' # 接收所有
6,RPC(远程过程调用)模式
在前面的所有模式中都是单向的,没有接收消费端发来的消息,如果消费端返回生产端接收就是RPC(远程过程调用)。在返回时,新建一个queue,放在里面,为了服务端返回的queue不写死,在客户端给服务端发指令的的时候,同时带一条消息说,你结果返回给哪个queue。
RPC server
import pika
import time def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n-1) + fib(n-2) def on_request(ch, method, props, body):
n = int(body)
print(" [.] fib(%s)" % n)
response = fib(n) ch.basic_publish(
exchange='', # 把执行结果发回给客户端
routing_key=props.reply_to, # 客户端要求返回想用的queue
# 返回客户端发过来的correction_id 为了让客户端验证消息一致性
properties=pika.BasicProperties(correlation_id = props.correlation_id),
body=str(response)
)
ch.basic_ack(delivery_tag = method.delivery_tag) # 任务完成,告诉客户端 if __name__ == '__main__':
connection = pika.BlockingConnection(pika.ConnectionParameters(
host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='rpc_queue') # 声明一个rpc_queue , channel.basic_qos(prefetch_count=1)
# 在rpc_queue里收消息,收到消息就调用on_request
channel.basic_consume(on_request, queue='rpc_queue')
print(" [x] Awaiting RPC requests")
channel.start_consuming()
RPC client
import pika
import uuid
import time class FibonacciRpcClient(object):
def __init__(self):
self.connection = pika.BlockingConnection(pika.ConnectionParameters(
host='localhost'))
self.channel = self.connection.channel()
result = self.channel.queue_declare(exclusive=True)
self.callback_queue = result.method.queue self.channel.basic_consume(self.on_response, # 只要一收到消息就调用on_response
no_ack=True,
queue=self.callback_queue) # 收这个queue的消息 def on_response(self, ch, method, props, body): # 必须四个参数
# 如果收到的ID和本机生成的相同,则返回的结果就是我想要的指令返回的结果
if self.corr_id == props.correlation_id:
self.response = body def call(self, n):
self.response = None # 初始self.response为None
self.corr_id = str(uuid.uuid4()) # 随机唯一字符串
self.channel.basic_publish(
exchange='',
routing_key='rpc_queue', # 发消息到rpc_queue
properties=pika.BasicProperties( # 消息持久化
reply_to = self.callback_queue, # 让服务端命令结果返回到callback_queue
correlation_id = self.corr_id, # 把随机uuid同时发给服务器
),
body=str(n)
)
while self.response is None: # 当没有数据,就一直循环
# 启动后,on_response函数接到消息,self.response 值就不为空了
self.connection.process_data_events() # 非阻塞版的start_consuming()
# print("no msg……")
# time.sleep(0.5)
# 收到消息就调用on_response
return int(self.response) if __name__ == '__main__':
fibonacci_rpc = FibonacciRpcClient()
print(" [x] Requesting fib(7)")
response = fibonacci_rpc.call(7)
print(" [.] Got %r" % response)
RabbitMQ使用介绍(python)的更多相关文章
- 介绍Python程序员常用的IDE和其它开发工具
概述 “工欲善其事,必先利其器”,如果说编程是程序员的手艺,那么IDE就是程序员的吃饭家伙了. IDE 的全称是Integration Development Environment(集成开发环境), ...
- RabbitMQ的介绍与spring整合
本文主要讲述的是个人参考官网及其他前辈博客,对RabbitMQ的一些理解与spring整个RabbitMQ. 一.RabbitMQ的介绍 1.1.什么是RabbitMQ RabbitMQ是一个由erl ...
- 介绍python由来, 安装python3.8.3 及其变量的定义, 小整数池
介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,Guido开始写能够解释Python语言语法的解释器.Python这个名字,来自Guido所挚爱 ...
- .NET 云原生架构师训练营(模块二 基础巩固 RabbitMQ Masstransit 介绍)--学习笔记
2.6.6 RabbitMQ -- Masstransit 介绍 Masstransit 是什么 Quickstart 消息 Message Masstransit 是什么 Masstransit 是 ...
- .Net RabbitMQ实战指南——RabbitMQ相关概念介绍
什么是消息中间件 消息(Message)是指在应用间传送的数据.消息可以非常简单,比如只包含文本字符串.JSON等,也可以很复杂,比如内嵌对象. 消息队列中间件(Message Queue Middl ...
- RabbitMQ学习: 介绍
1. 介绍 RabbitMQ是一个由erlang开发的基于AMQP(Advanced Message Queue )协议的开源实现.用于在分布式系统中存储转发消息,在易用性.扩展性.高可用性等方面都非 ...
- Rabbitmq -Publish_Subscribe模式- python编码实现
what is Exchanges ?? Let's quickly go over what we covered in the previous tutorials: A producer is ...
- 转载:简单介绍Python中的try和finally和with方法
用 Python 做一件很平常的事情: 打开文件, 逐行读入, 最后关掉文件; 进一步的需求是, 这也许是程序中一个可选的功能, 如果有任何问题, 比如文件无法打开, 或是读取出错, 那么在函数内需要 ...
- RabbitMQ的介绍及使用进阶(Docker+.Net Core)
目录: 一.什么是RabbitMQ 二.RabbitMQ运用场景 三.RabbitMQ优势及特点 四.Centos7中Docker安装RabbitMQ 五..Net Core 中使用RabbitMQ ...
随机推荐
- 计蒜客16495 Truefriend(fwt)
#include <iostream> #include <cstring> #include <cstdio> using namespace std; type ...
- [洛谷P3377]【模板】左偏树(可并堆)
题目大意:有$n$个数,$m$个操作: $1\;x\;y:$把第$x$个数和第$y$个数所在的小根堆合并 $2\;x:$输出第$x$个数所在的堆的最小值 题解:左偏树,保证每个的左儿子的距离大于右儿子 ...
- BZOJ2337: [HNOI2011]XOR和路径 期望概率dp 高斯
这个题让我认识到我以往对于图上期望概率的认识是不完整的,我之前只知道正着退还硬生生的AC做过的所有图,那么现在让我来说一下逆退,一般来说对于概率性的东西都只是正推,因为有了他爸爸才有了他,而对于期望性 ...
- Java实现十进制数转十六进制数
Now~Let's begin our second question~ 如何利用Java语言将十进制数字转换成十六进制数字呢? 我第一次编码出来的效果是酱紫的~ /** * */ package c ...
- gitlab迁移升级
一.迁移步骤 1.首先安装最新版本gitlab(gitlab7.2安装) 2.停止旧版本gitlab服务 3.将旧的项目文件完整导入新的gitlab bundle exec rake gitlab:i ...
- React.js基础知识
一. react.js的基本使用方法 (1)快速使用,hello world <div id="app"></div> <script src=&qu ...
- javascript学习教程
我来班门弄斧一下吧,把我JavaScript学习过程中常去的一些网站分享给大家: =========================增加================================ ...
- [USACO07FEB]新牛棚Building A New Barn
洛谷题目链接:[USACO07FEB]新牛棚Building A New Barn 题目描述 After scrimping and saving for years, Farmer John has ...
- Node.js 编码转换
Node.js自带的toString()方法不支持gbk,因此中文转换的时候需要加载第三方库,推荐以下两个编码转换库,iconv-lite和encoding. iconv, iconv-l ...
- 斜率优化DP讲解
对于斜率优化的DP转移方程,一般以w[i]=max(w[j]+(sum[i]-sum[j])*v)的1D1D形式为主,直观看来就是前j个为若干个阶段,第j+1到第i个为一个阶段,每个阶段有自己的代价或 ...