Python 之RabbitMQ使用
1. IO 多路复用
# select 模拟socket server
# server 端
import select
import socket
import sys
import queue
server = socket.socket()
server.setblocking(False)
server_addr = ('localhost', 10000)
print('starting up on %s port %s' % server_addr)
server.bind(server_addr)
server.listen(5)
inputs = [server, ]
outputs = []
message_queues = {}
while True:
print('waiting for next event...')
readable, writeable, exeptional = select.select(inputs, outputs, inputs)
for s in readable: # 每个 s 就是一个socket
if s in server:
conn, client_addr = s.accept()
print('new connection from', client_addr)
conn.setblocking(False)
inputs.append(conn)
message_queues[conn] = queue.Queue()
else:
data = s.recv(1024)
if data:
print('收到来自[%s]的数据:' % s.getpeername()[0], data)
message_queues[s].put[data]
if s not in outputs:
outputs.append(s)
else:
print('客户端断开了', s)
if s in outputs:
outputs.remove(s)
inputs.remove(s)
del message_queues[s]
for s in writeable:
try:
next_msg = message_queues[s].get_nowait()
except queue.Empty:
print('client [%s]' % s.getpeername()[0], 'queue is empty...')
outputs.remove(s)
else:
print('sending msg to [%s]' % s.getpeername()[0], next_msg)
s.send(next_msg.upper())
for s in exceptional:
print('handling exception for', s.getpeername())
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
del message_queues[s]
# client 端
import socket
import sys
messages = [b'This is the message ',
b'It will be sent',
b'in parts']
server_address = ('localhost', 10000)
socks = [socket.socket(socket.AF_INET, socket.SOCK_STREAM),
socket.socket(socket.AF_INET, socket.SOCK_STREAM),]
print('connecting to %s port %s' % server_address)
for s in socks:
s.connect(server_address)
for message in messages:
for s in socks:
print('%s: sending "%s"' % (s.getsockname(), message))
s.send(message)
for s in socks:
data = s.recv(1024)
print('%s: received "%s"' % (s.getsockname(), data))
if not data:
print(sys.stderr, 'closing socket', s.getsockname())
2. RabbitMQ
2.1 RabbitMQ 准备工作(以mac为例)
- 安装
RabbitMQ
:brew install rabbitmq
- 安装
pika
:pip3 install pika
- 启动
RabbitMQ
:/usr/local/Cellar/rabbitmq/3.7.4/sbin/rabbitmq-server
# 示例:
# 发送端
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel() # 声明一个管道
# 声明queue
channel.queue_declare(queue='hello')
# 需要 exchange 作为中转站
channel.basic_publish(exchange='',
routing_key='hello', # queue 名字
body='Hello World!')
print("' [x] Sent 'Hello World!'")
connection.close()
# 接收端
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello')
def callback(ch, method, properties, body):
print(" [x] Received %r" % body)
channel.basic_consume(callback, # 如果收到消息,就调用 CALLBACK 函数来处理消息
queue='hello',
no_ack=True) # no acknowledgement 不确认
print(' [*] Waiting fo messages. To exit press CTRL+C')
channel.start_consuming()
2.2 RabbitMQ 消息分发轮询
- 先启动消息生产者,然后再分别启动3个消费者,通过生产者多发送几条消息,这几条消息会依次分配到各个消费者身上;
2.3 RabbitMQ 消息持久化(发送端)
# 示例:
`channel.queue_declare(queue='hello', durable=True)`: 队列持久化;
# 示例二: 消息持久化
channel.basic_publish(exchange='',
routing_key='hello',
body='Hello World!',
properties=pika.BasicProperties(delivery_mode=2,) # 将消息持久化
)
2.4 RabbitMQ fanout广播模式
- 如果Rabbit只管按顺序把消息发到各个消费者身上,不考虑消费者负载的话,很可能出现,一个配置不高的机器的消费者那里
堆积了很多消息处理不完,同时配置高的消费者却一直很轻松; - 为解决上述问题,可以在各个消费者端,配置
perfetch=1
,意思是告诉RabbitMQ,消费者当前消息还没处理完的时候,就不
要再向该消费者发送新消息了。
# 示例:
# 发送端:
import pika
import sys
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello', durable=True)
message='Hello World!'
channel.basic_publish(exchange='',
routing_key='hello',
body=message,
properties=pika.BasicProperties(delivery_mode=2,))
print(' [x] Sent %r' % message)
connection.close()
# 消费者端
import pika
import time
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello', durable=True)
def callback(ch, method, properties, body):
print(' [x] Received %r' % body)
time.sleep(body.count(b'.'))
print(' [x] Done')
ch.basic_ack(delivery_tag = method.delivery_tag)
channel.basic_qos(prefetch_count=1)
channel.basic_consume(callback,
queue='hello')
channel.start_consuming()
2.5 RabbitMQ (发布/订阅模式)
- 发布/订阅模式,需要使用
Exchange
; Exchange
在定义的时候是有类型的,以决定到底是哪些Queue符合条件,可以接收消息:fanout
: 所有bind到此exchange的queue都可以接收消息;direct
: 通过routingKey和exchange决定的那个唯一的queue可以接收消息;topic
: 所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息;headers
: 通过headers来决定把消息发给哪些queue;
# 表达式符号说明: # 代表一个或多个字符, * 代表任何字符
# 例: #.a 会匹配 a.a, aa.a, aaa.a 等
# *.a 会匹配 a.a, b.a, c.a 等
# 示例: fanout模式,一方发送,多方同时接收
# 发送端
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs',
exchange_type='fanout')
message = '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',
exchange_type='fanout')
result = channel.queue_declare(exclusive=True) # 不指定queue名字,rabbit会随机分配一个名字, exclusive=True
# 会在使用此queue的消费者断开后,自动将queue删除
queue_name = result.method.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()
# 示例二: direct 模式(有选择的接收消息)
# 发送端(server.py)
import pika
import sys
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!'
channel.basic_publish(exchange='direct_logs',
routing_key=severity,
body=message)
print(' [x] Sent %r:%r' % (severity, message))
connection.close()
# 接收端(client.py)
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)
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()
# 说明:
# 以上操作在terminal启动: python3 client.py info (info级别的接收方)
# python3 server.py info (info级别的发送方)
# python3 client.py error (error级别的接收方)
# python3 server.py error (error级别的发送方)
# 示例三: 更细致的消息过滤(topic 模式)
# 划分为不同应用程序(例如mysql,python等),不同级别(error, info, debug)
# 发送端
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) > 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',
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()
# 说明:
# 以上操作在terminal启动: python3 client.py mysql.* (接收任何以mysql开头的消息)
# python3 server.py mysql.error (发送mysql的报错日志)
2.6 RabbitMQ rpc实现
# 示例:
# RPC server
import pika
import time
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()
# RPC client
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 # 生成随机Queue
self.channel.basic_consume(self.on_response, # 只要一收到消息,就调用 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() # 非阻塞版的start_consuming
print('no message')
return int(self.response)
fibonacci_rpc = FibonacciRpcClient()
print(' [x] Requesting fib(30)')
response = fibonacci_rpc.call(30)
print(' [.] Got %r' % response)
参考资料:
Python 之RabbitMQ使用的更多相关文章
- Python操作RabbitMQ
RabbitMQ介绍 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现的产品,RabbitMQ是一个消息代理,从“生产者”接收消息并传递消 ...
- 用 Python、 RabbitMQ 和 Nameko 实现微服务
用 Python. RabbitMQ 和 Nameko 实现微服务 原创 07-17 17:57 首页 Linux中国 "微服务是一股新浪潮" - 现如今,将项目拆分成多个独立的. ...
- python之RabbitMQ
一.安装RabbitMQ 1. 安装erlang 1 2 3 4 tar xf otp_src_18.3.tar.gz cd otp_src_18.3 ./configure --prefix=/ma ...
- Python之路【第九篇】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy
Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用 ...
- python - 操作RabbitMQ
python - 操作RabbitMQ 介绍 RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统.他遵循Mozilla Public License开源协议.MQ全称为Mess ...
- 文成小盆友python-num12 Redis发布与订阅补充,python操作rabbitMQ
本篇主要内容: redis发布与订阅补充 python操作rabbitMQ 一,redis 发布与订阅补充 如下一个简单的监控模型,通过这个模式所有的收听者都能收听到一份数据. 用代码来实现一个red ...
- Python之路第十二天,高级(4)-Python操作rabbitMQ
rabbitMQ RabbitMQ是一个在AMQP基础上完整的,可复用的企业消息系统.他遵循Mozilla Public License开源协议. MQ全称为Message Queue, 消息队列(M ...
- Python与RabbitMQ交互
RabbitMQ 消息队列 成熟的中间件RabbitMQ.ZeroMQ.ActiveMQ等等 RabbitMQ使用erlang语言开发,使用RabbitMQ前要安装erlang语言 RabbitMQ允 ...
- python中RabbitMQ的使用(安装和简单教程)
1,简介 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现的产品,RabbitMQ是一个消息代理,从"生产者"接收消息 ...
- Python之RabbitMQ的使用
今天总结一下Python关于Rabbitmq的使用 RabbitMQ官网说明,其实也是一种队列,那和前面说的线程queue和进程queue有什么区别呢? 线程queue只能在同一个进程下进行数据交互 ...
随机推荐
- 关于Context
Context字面意思是上下文,位于framework package的android.content.Context中,其实该类为LONG型,类似Win32中的Handle句柄.很多方法需要通过 C ...
- 阿里云高速maven库
<repository> <id>alimaven</id> <name>aliyun maven</name> <url>ht ...
- 一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
// test14.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> #include< ...
- 用c++封装linux系统调用
#include <pthread.h> #include <cstdlib> #include <ctime> #include <iostream> ...
- jquery checkbox选中
楼主写的在1.6之前是没有问题的,jquery 1.6后就要这样写了,<input type='checkbox' id='cb'/> <script> //获取是否选中 va ...
- 归并排序的C++实现
原创作品,转载请注明出处:点我 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序列 ...
- C/C++中printf和C++中cout的输出格式
一. Printf 输出格式 C中格式字符串的一般形式为: %[标志][输出最小宽度][.精度][长度]类型,其中方括号[]中的项为可选项.各项的意义介绍如下:1.类型类型字符用以表示输出数据的类型, ...
- mysql 存储过程调用
CALL 存储过程名('参数值1',‘参数值2',’参数值3')
- 求逆元 - HNU 13412 Cookie Counter
Cookie Counter Problem's Link: http://acm.hnu.cn/online/?action=problem&type=show&id=13412&a ...
- 关于Unity中的transform组件(二)
在Scene视图中的蓝色网格,每一格默认是1米 一.沿着Z轴每秒移动10米 Transform cube_trans; void start(){ this.cube_trans=this.trans ...