Python操作rabbitMQ的示例代码


Posted in Python onMarch 19, 2019

引入

RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。

rabbitMQ是一款基于AMQP协议的消息中间件,它能够在应用之间提供可靠的消息传输。在易用性,扩展性,高可用性上表现优秀。使用消息中间件利于应用之间的解耦,生产者(客户端)无需知道消费者(服务端)的存在。而且两端可以使用不同的语言编写,大大提供了灵活性。

Python操作rabbitMQ的示例代码

中文文档

安装

# 安装配置epel源
  rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
 
# 安装erlang
  yum -y install erlang
 
# 安装RabbitMQ
  yum -y install rabbitmq-server

# 启动/停止
  service rabbitmq-server start/stop

rabbitMQ工作模型

简单模式

生产者

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

channel = connection.channel()

channel.queue_declare(queue='hello')

channel.basic_publish(exchange='',
           routing_key='hello',
           body='Hello World!')

print(" [x] Sent 'Hello World!'")
connection.close()

消费者

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
 
channel.queue_declare(queue='hello')
 
def callback(ch, method, properties, body):
  print(" [x] Received %r" % body)
 
channel.basic_consume( callback,
            queue='hello',
            no_ack=True)
 
print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()

相关参数

1,no-ack = False

如果消费者遇到情况(its channel is closed, connection is closed, or TCP connection is lost)挂掉了,那么,RabbitMQ会重新将该任务添加到队列中。

  • 回调函数中的 ch.basic_ack(delivery_tag=method.delivery_tag)
  • basic_comsume中的no_ack=False

接收消息端应该这么写:

?
import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(
    host='10.211.55.4'))
channel = connection.channel()

channel.queue_declare(queue='hello')

def callback(ch, method, properties, body):
  print(" [x] Received %r" % body)
  import time
  time.sleep(10)
  print 'ok'
  ch.basic_ack(delivery_tag = method.delivery_tag)

channel.basic_consume(callback,
           queue='hello',
           no_ack=False)

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

2,durable :消息不丢失

生产者

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4'))
channel = connection.channel()

# make message persistent
channel.queue_declare(queue='hello', durable=True)

channel.basic_publish(exchange='',
           routing_key='hello',
           body='Hello World!',
           properties=pika.BasicProperties(
             delivery_mode=2, # make message persistent
           ))
print(" [x] Sent 'Hello World!'")
connection.close()

3,消息获取顺序

默认消息队列里的数据是按照顺序被消费者拿走,例如:消费者1 去队列中获取 奇数 序列的任务,消费者1去队列中获取 偶数 序列的任务。

channel.basic_qos(prefetch_count=1) 表示谁来谁取,不再按照奇偶数排列

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters(host='10.211.55.4'))
channel = connection.channel()

# make message persistent
channel.queue_declare(queue='hello')


def callback(ch, method, properties, body):
  print(" [x] Received %r" % body)
  import time
  time.sleep(10)
  print 'ok'
  ch.basic_ack(delivery_tag = method.delivery_tag)

channel.basic_qos(prefetch_count=1)

channel.basic_consume(callback,
           queue='hello',
           no_ack=False)

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

exchange模型

1,发布订阅

Python操作rabbitMQ的示例代码

发布订阅和简单的消息队列区别在于,发布订阅会将消息发送给所有的订阅者,而消息队列中的数据被消费一次便消失。所以,RabbitMQ实现发布和订阅时,会为每一个订阅者创建一个队列,而发布者发布消息时,会将消息放置在所有相关队列中。

exchange type = fanout

生产者

import pika
import sys

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

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

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')

result = channel.queue_declare(exclusive=True)
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()

2,关键字发送

Python操作rabbitMQ的示例代码

之前事例,发送消息时明确指定某个队列并向其中发送消息,RabbitMQ还支持根据关键字发送,即:队列绑定关键字,发送者将数据根据关键字发送到消息exchange,exchange根据 关键字 判定应该将数据发送至指定队列。

exchange type = direct

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_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()

3,模糊匹配

Python操作rabbitMQ的示例代码

exchange type = topic

发送者路由值 队列中
old.boy.python old.* -- 不匹配
old.boy.python old.# -- 匹配

在topic类型下,可以让队列绑定几个模糊的关键字,之后发送者将数据发送到exchange,exchange将传入”路由值“和 ”关键字“进行匹配,匹配成功,则将数据发送到指定队列。

  • # 表示可以匹配 0 个 或 多个 单词
  • *  表示只能匹配 一个 单词
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_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()

基于rabbitMQ的RPC

 Callback queue 回调队列

一个客户端向服务器发送请求,服务器端处理请求后,将其处理结果保存在一个存储体中。而客户端为了获得处理结果,那么客户在向服务器发送请求时,同时发送一个回调队列地址 reply_to

Correlation id 关联标识

一个客户端可能会发送多个请求给服务器,当服务器处理完后,客户端无法辨别在回调队列中的响应具体和那个请求时对应的。为了处理这种情况,客户端在发送每个请求时,同时会附带一个独有 correlation_id 属性,这样客户端在回调队列中根据 correlation_id 字段的值就可以分辨此响应属于哪个请求。

客户端发送请求:

某个应用将请求信息交给客户端,然后客户端发送RPC请求,在发送RPC请求到RPC请求队列时,客户端至少发送带有reply_to以及correlation_id两个属性的信息

服务端工作流:

等待接受客户端发来RPC请求,当请求出现的时候,服务器从RPC请求队列中取出请求,然后处理后,将响应发送到reply_to指定的回调队列中

客户端接受处理结果:

客户端等待回调队列中出现响应,当响应出现时,它会根据响应中correlation_id字段的值,将其返回给对应的应用

服务者

import pika

# 建立连接,服务器地址为localhost,可指定ip地址
connection = pika.BlockingConnection(pika.ConnectionParameters(
    host='localhost'))

# 建立会话
channel = connection.channel()

# 声明RPC请求队列
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)

# 对RPC请求队列中的请求进行处理
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()

客户端

import pika
import uuid

class FibonacciRpcClient(object):
  def __init__(self):
    """
    客户端启动时,创建回调队列,会开启会话用于发送RPC请求以及接受响应
    """
    # 建立连接,指定服务器的ip地址
    self.connection = pika.BlockingConnection(pika.ConnectionParameters(
        host='localhost'))
        
    # 建立一个会话,每个channel代表一个会话任务
    self.channel = self.connection.channel()
    
    # 声明回调队列,再次声明的原因是,服务器和客户端可能先后开启,该声明是幂等的,多次声明,但只生效一次
    result = self.channel.queue_declare(exclusive=True)
    # 将次队列指定为当前客户端的回调队列
    self.callback_queue = result.method.queue
    
    # 客户端订阅回调队列,当回调队列中有响应时,调用`on_response`方法对响应进行处理; 
    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


  # 发出RPC请求
  def call(self, n):
  
    # 初始化 response
    self.response = None
    
    #生成correlation_id 
    self.corr_id = str(uuid.uuid4())
    
    # 发送RPC请求内容到RPC请求队列`rpc_queue`,同时发送的还有`reply_to`和`correlation_id`
    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()

# 发送RPC请求
print(" [x] Requesting fib(30)")
response = fibonacci_rpc.call(30)
print(" [.] Got %r" % response)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
pytyon 带有重复的全排列
Aug 13 Python
python从ftp下载数据保存实例
Nov 20 Python
python判断一个集合是否包含了另外一个集合中所有项的方法
Jun 30 Python
Python将图片批量从png格式转换至WebP格式
Aug 22 Python
python 实现交换两个列表元素的位置示例
Jun 26 Python
python 中如何获取列表的索引
Jul 02 Python
python并发编程多进程 互斥锁原理解析
Aug 20 Python
python实现静态web服务器
Sep 03 Python
win10下opencv-python特定版本手动安装与pip自动安装教程
Mar 05 Python
浅谈keras使用预训练模型vgg16分类,损失和准确度不变
Jul 02 Python
python代码能做成软件吗
Jul 24 Python
查找适用于matplotlib的中文字体名称与实际文件名对应关系的方法
Jan 05 Python
Python Matplotlib实现三维数据的散点图绘制
Mar 19 #Python
浅谈python中get pass用法
Mar 19 #Python
使用matplotlib中scatter方法画散点图
Mar 19 #Python
详解django+django-celery+celery的整合实战
Mar 19 #Python
详解Python正则表达式re模块
Mar 19 #Python
python matplotlib画图库学习绘制常用的图
Mar 19 #Python
详解python的四种内置数据结构
Mar 19 #Python
You might like
php目录遍历函数opendir用法实例
2014/11/20 PHP
PHP四舍五入、取整、round函数使用示例
2015/02/06 PHP
PHP的Yii框架中行为的定义与绑定方法讲解
2016/03/18 PHP
php中文乱码问题的终极解决方案汇总
2017/08/01 PHP
fancybox1.3.1 基于Jquery的插件在IE中图片显示问题
2010/10/01 Javascript
jQuery之end()和pushStack()使用介绍
2012/02/07 Javascript
js 判断文件类型并控制表单提交示例代码
2013/11/14 Javascript
JavaScript的strict模式与with关键字介绍
2014/02/08 Javascript
js图片自动轮播代码分享(js图片轮播)
2014/05/06 Javascript
Extjs grid panel自带滚动条失效的解决方法
2014/09/11 Javascript
javascript实现全角转半角的方法
2016/01/23 Javascript
创建一个类Person的简单实例
2016/05/17 Javascript
JavaScript实现经典排序算法之插入排序
2016/12/28 Javascript
微信小程序 自定义对话框实例详解
2017/01/20 Javascript
jQuery+C#实现参数RSA加密传输功能【附jsencrypt.js下载】
2017/06/26 jQuery
JavaScript生成图形验证码
2020/08/24 Javascript
nodejs结合socket.io实现websocket通信功能的方法
2018/01/12 NodeJs
Vue学习笔记之计算属性与侦听器用法
2019/12/07 Javascript
python实现ftp客户端示例分享
2014/02/17 Python
python动态加载变量示例分享
2014/02/17 Python
深入解析Python中的__builtins__内建对象
2016/06/21 Python
python并发编程之多进程、多线程、异步和协程详解
2016/10/28 Python
python实现控制台打印的方法
2019/01/12 Python
pyqt5实现绘制ui,列表窗口,滚动窗口显示图片的方法
2019/06/20 Python
Python3实现汉语转换为汉语拼音
2019/07/08 Python
PyTorch如何搭建一个简单的网络
2020/08/24 Python
Liu Jo西班牙官网:意大利服装品牌
2019/09/11 全球购物
院药学专业个人求职信
2013/09/21 职场文书
结婚保证书范文
2014/04/29 职场文书
社区工作者个人总结
2015/02/28 职场文书
小学教师节活动总结
2015/03/20 职场文书
2015年人事工作总结范文
2015/04/09 职场文书
社区挂职锻炼个人工作总结
2015/10/23 职场文书
2016入党心得体会范文
2016/01/06 职场文书
市直属机关2016年主题党日活动总结
2016/04/05 职场文书
python3 实现mysql数据库连接池的示例代码
2021/04/17 Python