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 ...
随机推荐
- WebSocket对象的创建及其与WebSocket服务器的连接(5)
WebSocket接口的使用非常简单,要连接通信端点,只需要创建一个新的WebSocket实例,并提供希望连接URL. //ws://和wss://前缀分别表示WebSocket连接和安全的WebSo ...
- C#怎么调用方法
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Exep ...
- [BZOJ4212]神牛的养成计划
[BZOJ4212]神牛的养成计划 试题描述 Hzwer 成功培育出神牛细胞,可最终培育出的生物体却让他大失所望...... 后来,他从某同校女神 牛处知道,原来他培育的细胞发生了基因突变,原先决定神 ...
- [NOIP2018 TG D2T1]旅行
题目大意:$NOIP\;TG\;D2T1$ 题解:一棵树的很简单,第一个点一定是$1$,只需要对每个节点,找最小的没有访问过的节点访问即可,我写的是$O(n\log_2n)$. 考虑基环树的部分,一个 ...
- Unable to start activity ComponentInfo{com.example.administrator.myapplication/com.example.administrator.myapplication.MainActivity}: android.view.InflateException: Binary XML file line #0: Binary XM
本来就是把fragment写死在activity的xml模板里面,结果报了这个错误, Unable to start activity ComponentInfo{com.example.admini ...
- bzoj 5092 [Lydsy1711月赛]分割序列 贪心高维前缀和
[Lydsy1711月赛]分割序列 Time Limit: 5 Sec Memory Limit: 256 MBSubmit: 213 Solved: 97[Submit][Status][Dis ...
- CSS选择器及CSS3新增选择器
转自:http://www.cnblogs.com/libingql/p/4375354.html 1. CSS1定义的选择器 选择器 类型 说明 E 类型选择器 选择指定类型的元素 E#id ID选 ...
- ssh中的相对路径与绝对路径的问题
一:前言:自己在学习ssh的时候常常被路径给迷惑,就比如在刚刚学习jsp的servlet时,绝对路径和相对路径我就弄混了,所以专门写了一篇博客来记载.而现在自己是在学ssh的时候在此遇到路径问题,本来 ...
- sql数据库的链接方式
今天看见了一个数据库的链接方法,给转载了,记得我刚刚学DAO的时候老是要记载这些东西,所以就上博客园上面看了看,就转过来了... MySQL: String Driver="com.mysq ...
- [BZOJ1441&BZOJ2257&BZOJ2299]裴蜀定理
裴蜀定理 对于整系数方程ax+by=m,设d =(a,b) 方程有整数解当且仅当d|m 这个定理实际上在之前学习拓展欧几里得解不定方程的时候就已经运用到 拓展到多元的方程一样适用 BZOJ1441 给 ...