RabbitMQ/pika模块
简介
MessageQueue用于解决跨进程、跨线程、跨应用、跨网络的通信问题。
RabbitMQ使用erlang开发,在windows上使用时要先安装erlang。
官方的示例比较容易理解,可以点这里去看看。
结构
生产者 ---> exchange ---> queue ---> 消费者。
生产者负责提供消息,exchange负责分发消息到指定queue,queue存储消息(默认临时,可设置持久化),消费者接收处理消息。
基本模型
流程:
- 建立到rabbitmq的连接
- 建立通道
- 声明使用的队列(生产者和消费者都要声明,因为不能确定两者谁先运行)
- 生产/消费
- 持续监听/关闭连接
python中使用pika模块来处理与rabbitmq的连接。
# 生产者
import pika
connection = pika.BlockingConnection(
pika.ConnectionParameters(
host='localhost'
)
)
channel = connection.channel()
r = channel.queue_declare(queue='name', exclusive=False, durable=False) # exclusive设置True是随机生成一个queue名字并返回,durable设置True是队列持久化
queue_name = r.method.queue
channel.basic_publish(
exchange = '', # 使用默认分发器
routing_key = queue_name,
properties = pika.BasicProperties(
delivery_mode = 2 # 这个参数用于设置消息持久化
),
body = [data]
)
connection.close()
# 消费者
import pika
connection = pika.BlockingConnection(
pika.ConnectionParameters(
host='localhost'
)
)
channel = connection.channel()
r = channel.queue_declare(queue='name', exclusive=False, durable=False)
queue_name = r.method.queue
def callback(channel, method, properties, body):
pass
# channel.basic_ack(delivery_tag = method.delivery_tag) 在回调函数最后调用手工应答,表示消息处理完毕,queue可以删除消息了
channel.basic_consume(
callback, # 这是个回调函数,接收生产者发来的body
queue = queue_name,
no_ack = True # 设置True表示消息一经获取就被从queue中删除,如果这时消费者崩溃,则这条消息将永久丢失,所以去掉这个属性,在回调函数中手工应答比较安全
)
channel.basic_qos(prefetch_count = [num]) # 设置消费者的消费能力,数字越大,则说明该消费者能力越强,往往与设备性能成正比
channel.start_consuming() # 阻塞模式获取消息
# connection.process_data_events() 非阻塞模式获取消息
发布订阅模型
类似收音机广播,订阅者只要打开收音机就能收听信息,但接收不到它打开之前的消息。
包括发布订阅模型以及接下来的一些模型,都是通过exchange和routing_key这两个属性来控制的。直接用官网的源码来做注释。
流程:
- 发布者设置发布频道
- 收听者配置频道信息
- 收听者通过随机queue绑定频道接收消息
# 发布者
#!/usr/bin/env python
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# 创建一个命名exchange,并设置其type为fanout,表示广播
channel.exchange_declare(exchange='logs',
exchange_type='fanout')
# 从命令行接收输入
message = ' '.join(sys.argv[1:]) or "info: Hello World!"
# 由于是广播模式,任意消费者只要设置同样的exchange,就能以任意queue来接收消息,所以这里routing_key置空
channel.basic_publish(exchange='logs',
routing_key='',
body=message)
print(" [x] Sent %r" % message)
connection.close()
# 收听者
#!/usr/bin/env python
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# 这里使用同样的exchange配置,就像调节收音机频道
channel.exchange_declare(exchange='logs',
exchange_type='fanout')
# 在基础模型中提到过,设置exclusive=True表示生成随机的queue
result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue
# 生成了queue,还要将它与exchange进行绑定,这样消息才能通过exchange进入queue
channel.queue_bind(exchange='logs',
queue=queue_name)
print(' [*] Waiting for logs. To exit press CTRL+C')
def callback(ch, method, properties, body):
print(" [x] %r" % body)
channel.basic_consume(callback,
queue=queue_name,
no_ack=True)
channel.start_consuming()
路由/级别模型
将消息发送到指定的路由处,类似于logging模块的分级日志消息。
主要利用channel.queue_bind(routing_key=[route])这个方法,来为queue增加路由。
流程:
- 生产者向指定路由发送消息
- 消费者绑定路由
- 根据路由接收到不同的消息
# 生产者
#!/usr/bin/env python
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
# 同样使用命名exchange,主要是type为direct
channel.exchange_declare(exchange='direct_logs',
exchange_type='direct')
# 将命令行输入的路由作为接收消息的queue的属性,只有匹配的才能接收到消息
severity = sys.argv[1] if len(sys.argv) > 2 else '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()
# 消费者
#!/usr/bin/env python
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='direct_logs',
exchange_type='direct')
result = channel.queue_declare(exclusive=True)
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)
# 对该消费者的queue绑定路由
for severity in severities:
channel.queue_bind(exchange='direct_logs',
queue=queue_name,
routing_key=severity)
print(' [*] Waiting for logs. To exit press CTRL+C')
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()
细目模型/更细致的划分
这个模型比前几种更强大,但是原理与路由模型是相同的。
如果routing_key='#',它就相当于发布订阅模式,向所有queue发送消息,如果routing_key值中不包含*,#,则相当于路由模型。
该模型主要是实现了模糊匹配。
# 生产者
#!/usr/bin/env python
import pika
import sys
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
import pika
import sys
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 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)
print(' [*] Waiting for logs. To exit press CTRL+C')
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()
RPC模型
前面的几种模型都只能是一端发消息,另一端接收,RPC模型实现的就是单端收发功能。
主要是通过两个队列实现,一个发,一个收。
流程:
- 客户端发送消息到约定队列,并且附带返回队列的名称和验证id
- 服务器接到消息,将处理过的消息发送给指定队列并附带验证id
- 客户端接到消息先验证id,通过则处理消息
# 服务器
#!/usr/bin/env python
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='rpc_queue')
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,
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(on_request, queue='rpc_queue')
print(" [x] Awaiting RPC requests")
channel.start_consuming()
# 客户端
#!/usr/bin/env python
import pika
import uuid
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, no_ack=True,
queue=self.callback_queue)
def on_response(self, ch, method, props, body):
if self.corr_id == props.correlation_id:
self.response = body
def call(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 is None:
self.connection.process_data_events()
return int(self.response)
fibonacci_rpc = FibonacciRpcClient()
print(" [x] Requesting fib(30)")
response = fibonacci_rpc.call(30)
print(" [.] Got %r" % response)
RabbitMQ/pika模块的更多相关文章
- python的pika模块操作rabbitmq
上一篇博文rabbitmq的构架和原理,了解了rabbitmq的使用原理,接下来使用python的pika模块实现使用rabbitmq. 环境搭建 安装python,不会的请参考Linux安装配置py ...
- 关于python中pika模块的问题
工作中经常用到rabbitmq,而用的语言主要是python,所以也就经常会用到python中的pika模块,但是这个模块的使用,也给我带了很多问题,这里整理一下关于这个模块我在使用过程的改变历程已经 ...
- rabbitmq pika connection closed
You are here: Home / rabbitmq pika connection closed rabbitmq pika connection closed By lijiejie on ...
- springboot jpa 创建数据库以及rabbitMQ分模块扫描问题
在使用jpa过程中,如果没有在配置中加入自动创建实体对于的sql,则需要提前创建建表语句 spring.jpa.properties.hibernate.show_sql=true spring.jp ...
- RabbitMQ(pika模块)
RabbitMQ 基础 2 3 4 5 6 7 8 安装配置epel源 $ rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-r ...
- RabbitMQ之pika模块
发布/订阅 系统 send.py import pika import time s_conn = pika.BlockingConnection(pika.ConnectionParameters( ...
- RabbitMQ python模块pika生产者消费者轮询模型。
完整代码如下: 生产者,producer import pika connection = pika.BlockingConnection( pika.ConnectionParameters('lo ...
- python安装pika模块rabbitmq
1.pip install pika 2.如找不到 拷贝 D:\python\testmq\venv\Lib\site-packages \pika目录
- rabbitmq pika(python)订阅发布多客户端消费场景简单使用
发布端: import pika import time credentials = pika.credentials.PlainCredentials('root', 'root',erase_on ...
随机推荐
- unittest的discover方法使用
使用unittest进行测试,如果是需要实现上百个测试用例,把它们全部写在一个test.py文件中,文件会越来越臃肿,后期维护页麻烦.此时可以将这些用例按照测试功能进行拆分,分散到不同的测试文件中. ...
- 图论trainning-part-1 E. Invitation Cards
E. Invitation Cards Time Limit: 8000ms Memory Limit: 262144KB 64-bit integer IO format: %lld Ja ...
- 九度oj 题目1533:最长上升子序列
题目描述: 给定一个整型数组, 求这个数组的最长严格递增子序列的长度. 譬如序列1 2 2 4 3 的最长严格递增子序列为1,2,4或1,2,3.他们的长度为3. 输入: 输入可能包含多个测试案例. ...
- 九度oj 题目1254:N皇后问题
题目描述: N皇后问题,即在N*N的方格棋盘内放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在同一斜线上.因为皇后可以直走,横走和斜走如下图). 你的任务是,对 ...
- BestCoder Round #32
问题描述 目前,我们用PM2.5的含量来描述空气质量的好坏.一个城市的PM2.5含量越低,它的空气质量就越好.所以我们经常按照PM2.5的含量从小到大对城市排序.一些时候某个城市的排名可能上升,但是他 ...
- POJ 1067: Wythoff Game【博弈】
经典的威佐夫博奕把黄金分割常数乘以k(k=m-n)即为奇异点,此时奇异点是用小数据观察出来的,具体的数学证明,观察到黄金分割常数是无理数,再加上高斯函数[kφ]的形势将自然数分割成两个等价类很容易想到 ...
- 【单调队列+二分查找】bzoj 1012: [JSOI2008]最大数maxnumber
[题意] 维护一个单调递减的q数组,用id数组记录q数组的每个下标对应在原数组的位置,那么id数组一定有单调性(q数组中越靠后,原数组中也靠后),然后二分查找这个数 [AC] #include< ...
- cf701E Connecting Universities
Treeland is a country in which there are n towns connected by n - 1 two-way road such that it's poss ...
- 小Z的袜子(bzoj 2038)
Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……具体来说,小Z把这N只袜 ...
- android开发里遇到的坑——eclipse项目导入android studio以后Run按钮灰色
android studio编译有错误但是没有提示给用户,关闭android studio重新启动项目后,会显示错误,修复以后即可!