简介

MessageQueue用于解决跨进程、跨线程、跨应用、跨网络的通信问题。

RabbitMQ使用erlang开发,在windows上使用时要先安装erlang。

官方的示例比较容易理解,可以点这里去看看。

结构

生产者 ---> exchange ---> queue ---> 消费者。

生产者负责提供消息,exchange负责分发消息到指定queue,queue存储消息(默认临时,可设置持久化),消费者接收处理消息。

基本模型

流程:

  1. 建立到rabbitmq的连接
  2. 建立通道
  3. 声明使用的队列(生产者和消费者都要声明,因为不能确定两者谁先运行)
  4. 生产/消费
  5. 持续监听/关闭连接

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这两个属性来控制的。直接用官网的源码来做注释。

流程:

  1. 发布者设置发布频道
  2. 收听者配置频道信息
  3. 收听者通过随机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增加路由。

流程:

  1. 生产者向指定路由发送消息
  2. 消费者绑定路由
  3. 根据路由接收到不同的消息
# 生产者
#!/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模型实现的就是单端收发功能。

主要是通过两个队列实现,一个发,一个收。

流程:

  1. 客户端发送消息到约定队列,并且附带返回队列的名称和验证id
  2. 服务器接到消息,将处理过的消息发送给指定队列并附带验证id
  3. 客户端接到消息先验证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模块的更多相关文章

  1. python的pika模块操作rabbitmq

    上一篇博文rabbitmq的构架和原理,了解了rabbitmq的使用原理,接下来使用python的pika模块实现使用rabbitmq. 环境搭建 安装python,不会的请参考Linux安装配置py ...

  2. 关于python中pika模块的问题

    工作中经常用到rabbitmq,而用的语言主要是python,所以也就经常会用到python中的pika模块,但是这个模块的使用,也给我带了很多问题,这里整理一下关于这个模块我在使用过程的改变历程已经 ...

  3. rabbitmq pika connection closed

    You are here: Home / rabbitmq pika connection closed rabbitmq pika connection closed By lijiejie on  ...

  4. springboot jpa 创建数据库以及rabbitMQ分模块扫描问题

    在使用jpa过程中,如果没有在配置中加入自动创建实体对于的sql,则需要提前创建建表语句 spring.jpa.properties.hibernate.show_sql=true spring.jp ...

  5. RabbitMQ(pika模块)

    RabbitMQ 基础 2 3 4 5 6 7 8 安装配置epel源    $ rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-r ...

  6. RabbitMQ之pika模块

    发布/订阅 系统 send.py import pika import time s_conn = pika.BlockingConnection(pika.ConnectionParameters( ...

  7. RabbitMQ python模块pika生产者消费者轮询模型。

    完整代码如下: 生产者,producer import pika connection = pika.BlockingConnection( pika.ConnectionParameters('lo ...

  8. python安装pika模块rabbitmq

    1.pip install pika 2.如找不到 拷贝 D:\python\testmq\venv\Lib\site-packages  \pika目录

  9. rabbitmq pika(python)订阅发布多客户端消费场景简单使用

    发布端: import pika import time credentials = pika.credentials.PlainCredentials('root', 'root',erase_on ...

随机推荐

  1. 在 Yii2 项目中使用 Composer 添加 FontAwesome 字体资源

    2014-06-21 19:05 原文 简体 繁體 2,123 次围观 前天帮同事改个十年前的网站 bug,页面上一堆 include require 不禁让人抱头痛哭.看到 V2EX 上的讨论说,写 ...

  2. Neural Networks and Deep Learning

    Neural Networks and Deep Learning This is the first course of the deep learning specialization at Co ...

  3. get 发送ajax请求

    上demo小案例 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  4. 【Luogu】P1879玉米田(状压DP)

    题目链接 数据范围这么小,难度又这么大,一般就是状态压缩DP了. 对输入进行处理,二进制表示每一行的草地状况.如111表示这一行草地肥沃,压缩成7. 所以f[i][j]表示第i行状态为j时的方案数 状 ...

  5. VM上完美运行macos

    VM上完美运行macos 作者:方辰昱 时间:十月三号 效果图 简要步骤 下载安装VM 下载镜像文件链接,darwin.iso,unlocker,beamoff.合集下载链接:https://pan. ...

  6. BZOJ2245 [SDOI2011]工作安排 【费用流】

    题目 你的公司接到了一批订单.订单要求你的公司提供n类产品,产品被编号为1~n,其中第i类产品共需要Ci件.公司共有m名员工,员工被编号为1~m员工能够制造的产品种类有所区别.一件产品必须完整地由一名 ...

  7. P1410 子序列 (动态规划)

    题目描述 给定一个长度为N(N为偶数)的序列,问能否将其划分为两个长度为N/2的严格递增子序列. 输入输出格式 输入格式: 若干行,每行表示一组数据.对于每组数据,首先输入一个整数N,表示序列的长度. ...

  8. 基于注解的 Spring MVC(上)

    什么是Spring MVC Spring MVC框架是一个MVC框架,通过实现Model-View-Controller模式来很好地将数据.业务与展现进行分离.从这样一个角度来说,Spring MVC ...

  9. cmd指令

    d:  进入D盘: cd job 进入d盘名为job的文件夹:cd显示当前路径: md test创建名为test的文件夹: rd test删除名为test的文件夹: cd.>test.json创 ...

  10. Day 12 shell语法及程序若干

    1. 现归纳一下shell中的运算符:       +:对两个变量做加法.    -:对两个变量做减法.    *:对两个变量做乘法.    /:对两个变量做除法.    **:对两个变量做幂运算.  ...