Python RabbitMQ基础用法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Dream_ya/article/details/84763891

上一篇文章> Linux中编译安装RabbitMQ

一、MQ介绍


MQ并不是python内置的模块,而是一个需要你额外安装(在上一篇博客有介绍)的程序,安装完毕后可通过python中内置的pika模块来调用MQ发送或接收队列请求,我们可以通过官方文档查看创建消息队列应用程序的基本知识。

二、hello消息队列


1、原理图

P:producer
hello:消息队列(MQ)
C:consumer

在这里插入图片描述

producer生产出把数据放入到队列中,consumer可以从里面取出数据!!!

2、代码

官网链接:http://www.rabbitmq.com/tutorials/tutorial-one-python.html

(1)send.py
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('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()
(2)receive.py
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

### 声明二次是为了程序能够正常运行,因为不知道那个程序会先启动
channel.queue_declare(queue='hello')

### ch:管道内存地址
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)

### callback:收到消息,调用callback函数处理消息
channel.basic_consume(callback,
                      queue='hello',
                      no_ack=True)

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

运行send.py后,在运行receive.py就会接收到send发送的数据,我们不知道数据接收后是否完成了处理(接收数据的机器是否出问题),因此我们可以加入确定信号!!!

3、优化代码

在callback中加入sleep,我们开启多个receive,发送消息后,关闭接收消息的那个,消息会自动发送到另外一个receive中,我们可以在shell中使用 rabbitmqctl list_queues,查看队列!!!

(1)send.py
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('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()
(2)receive.py
import pika,time

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

### 声明二次是为了程序能够正常运行,因为不知道那个程序会先启动
channel.queue_declare(queue='hello')

### ch:管道内存地址,ch.basic_ack这样才完成确定
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    time.sleep(10)
    ch.basic_ack(delivery_tag=method.delivery_tag)

### callback:收到消息,调用callback函数处理消息
### no_ack=True:不收到回信
channel.basic_consume(callback,
                      queue='hello',
                      # no_ack=True
                      )

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

结果:

[root@python ~]# rabbitmqctl list_queues
Listing queues
hello   1

[root@python ~]# rabbitmqctl list_queues
Listing queues
hello   0

这样虽然可以保证数据的完整性,但是MQ服务down了就不行了!!!

三、Work Queues


实现消息持久化!!!

1、原理图

在这里插入图片描述

2、代码

官网链接:http://www.rabbitmq.com/tutorials/tutorial-two-python.html

(1)send.py
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

### 声明为持久化消息队列
channel.queue_declare(queue='hello1',durable=True)

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

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

### 声明二次是为了程序能够正常运行,因为不知道那个程序会先启动
channel.queue_declare(queue='hello1',durable=True)

### ch:管道内存地址
def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)
    # time.sleep(10)
    ch.basic_ack(delivery_tag=method.delivery_tag)

### 最多只能收到一条
channel.basic_qos(prefetch_count=1)

### callback:收到消息,调用callback函数处理消息
channel.basic_consume(callback,
                      queue='hello1',
                      # no_ack=True
                      )

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

结果:

 ### 先运行send和receive,在过程中关闭掉MQ,可以发现端口都不存在了
[root@python ~]# netstat -lntup|grep -E "5672|25672|15672"          
[root@python ~]# rabbitmq-server -detached             ###开启MQ
[root@python ~]# netstat -lntup|grep -E "5672|25672|15672"
tcp        0      0 0.0.0.0:15672           0.0.0.0:*               LISTEN      7922/beam           
tcp        0      0 0.0.0.0:25672           0.0.0.0:*               LISTEN      7922/beam           
tcp6       0      0 :::5672                 :::*                    LISTEN      7922/beam           
[root@python ~]# rabbitmqctl list_queues
Listing queues
hello1  2

四、Publish/Subscribe


1、发布/订阅介绍

  • 消息发布:类似广播的效果,所有的消息队列都能搜索到!!!
  • Exchange类型:
    • fanout:所有bind到此exchange的queue都可以接受消息
    • direct:通过routingKey和exchange决定的那个唯一的queue可以接收消息
    • topic:所有符合routingKey(可以是一个表达式)的routingKey所bind的queue可以接收消息

2、fanout

官网链接:http://www.rabbitmq.com/tutorials/tutorial-three-python.html

(1)原理图

X:exchange中间件,通过exchange转发消息!!!

在这里插入图片描述

(2)fanout_send.py
import pika
import sys

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

channel.exchange_declare(exchange='logs',
                         exchange_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()
(3)fanout_receive.py
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)       ###exclusive:排他的,没有连接时,会自动删除掉
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()

结果:
进行连接的消息队列可以收到广播,消息是实时的(即如果之前发送的消息,consumer没有启动就接收不到)!!!

3、direct

官网链接:http://www.rabbitmq.com/tutorials/tutorial-four-python.html

(1)原理图

在这里插入图片描述

(2)direct_send.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" % message)
connection.close()
(3)direct_receive.py
import pika,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)       ###exclusive:排他的,没有连接时,会自动删除掉
queue_name = result.method.queue

severities = sys.argv[1:]
if not severities:
    ### sys.stderr用来重定向标准错误信息
    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" % body)

channel.basic_consume(callback,
                      queue=queue_name,
                      no_ack=True)

channel.start_consuming()

结果:

开多个shell执行
producer:python  direct_send.py error
consumer:python direct_receive.py info,python direct_receive.py warning,python direct_receive.py error
可以发现这样收取的话还是不够细分的!!!

在这里插入图片描述

4、topic

官网链接:http://www.rabbitmq.com/tutorials/tutorial-five-python.html

(1)原理图

实现更细致的消息过滤!!!

在这里插入图片描述

(2)topic_send.py
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()
(3)topic_receive.py
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()

结果:

consumer(receive)也可以后面接#实现收取所有消息:python topic_receive.py “#”!!!

在这里插入图片描述

可以发现通过不同的文件名进行消息队列消息收取!!!

五、RPC


1、原理图

在这里插入图片描述

简单来说就是实现了返回server端处理client后的结果,之前我们只是返回了状态罢了,通过uuid来确定唯一性,从而实现数据结果转发!!!

2、代码

官网链接:http://www.rabbitmq.com/tutorials/tutorial-six-python.html

(1)rpc-server.py
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)
### 收到了消息就会进行调用,props:返回给消费者的参数
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)
### 收到消息就调用on_request
channel.basic_consume(on_request, queue='rpc_queue')

print(" [x] Awaiting RPC requests")
channel.start_consuming()
(2)rpc-client.py
import pika
import uuid

class FibonacciRpcClient(object):
    def __init__(self):
        self.connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))

        self.channel = self.connection.channel()
        
        ### 生成独一的随机queue
        result = self.channel.queue_declare(exclusive=True)
        self.callback_queue = result.method.queue

        ### 收到消息就调用self.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

    def call(self, n):
        self.response = None

        ### 生成一个随机uuid
        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)

猜你喜欢

转载自blog.csdn.net/Dream_ya/article/details/84763891