RabbitMQ基础学习

目录

MQ

使用消息队列的原因

简单的rabbitMQ队列通信

消息持久化

RPC(Remote Procedure Call)


MQ

消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。其主要用途:不同进程Process/线程Thread之间通信

使用消息队列的原因

当不同进程(process)之间传递消息时,如果两个进程之间耦合程度过高,一个进程的改动就会引发另一个进程的改动,为了隔离这两个进程,在两进程间抽离出一层(一个模块),用来两个进程之间传递的消息,这样单独修改某一个进程不会影响另一个;

为了实现标准化,格式规范化,对过多的消息进行排队,因此诞生了消息队列。

MQ框架非常之多,比较流行的有RabbitMq、ActiveMq、ZeroMq、kafka,以及阿里开源的RocketMQ。

RabbitMq基于Erlang,所以需要安装。

简单的rabbitMQ队列通信

由上图可知,数据是先发给exchange交换器,exchage再发给相应队列。pika模块是python对rabbitMQ的API接口。接收端有一个回调函数,一接收到数据就调用该函数。一条消息被一个消费者接收后,该消息就从队列删除

import pika

# 连上rabbitMQ
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()  # 生成管道,在管道里跑不同的队列

# 声明queue
channel.queue_declare(queue='hello1')

# 向队列里发数据
channel.basic_publish(exchange='', routing_key ='hello1', body = 'HelloWorld!!')
print("Sent Message succeed")
connection.close()
import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello1')  # 声明队列,保证程序不出错

def callback(ch, method, properties, body):
    print("\033[0;31;40m\t-->ch\033[0m" ,ch)
    print("\033[0;32;40m\t-->method\033[0m", method)
    print("\033[0;33;40m\t-->properties\033[0m", properties)
    print("\033[0;34;40m\tReceived\033[0m", body)
    # 一条消息被一个消费者接收后,该消息就从队列删除

channel.basic_consume('hello1',callback, auto_ack=False)
print('[*] Waiting for messages.To exit press CTRL+C')
channel.start_consuming()

如果把发送端和接收端分别比作生产者消费者。生产者发送任务A,消费者接收任务A并处理,处理完后生产者将消息队列中的任务A删除。现在我们遇到了一个问题:如果消费者接收任务A,但在处理的过程中突然宕机了。而此时生产者将消息队列中的任务A删除。实际上任务A并未成功处理完,相当于丢失了任务/消息。为解决这个问题,应使消费者接收任务并成功处理完后发送一个ack到生产者!生产者收到ack后就明白任务A已被成功处理,这时才从消息队列中将任务A删除,如果没有收到ack,就需要把任务A发送给下一个消费者,直到任务A被成功处理。

消息持久化

消息持久化分为两步:

  • 持久化队列。通过代码实现持久化hello队列:channel.queue_declare(queue='hello', durable=True)

  • 持久化队列中的消息。通过代码实现:properties=pika.BasicProperties( delivery_mode = 2)

运行send创建一个消息

重启服务

运行receive可就收消息

import pika

# 连上rabbitMQ
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()  # 生成管道,在管道里跑不同的队列

# 声明queue
channel.queue_declare(queue='hello1',durable=True) #设置队列并且持久化

# 向队列里发数据                                                                  持久化第二步
channel.basic_publish(exchange='', routing_key ='hello1', body = 'HelloWorld!!',properties=pika.BasicProperties(delivery_mode=2))
print("Sent Message succeed")
connection.close()
import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.queue_declare(queue='hello1',durable=True)  # 声明队列,保证程序不出错

def callback(ch, method, properties, body):
    print("\033[0;31;40m\t-->ch\033[0m" ,ch)
    print("\033[0;32;40m\t-->method\033[0m", method)
    print("\033[0;33;40m\t-->properties\033[0m", properties)
    print("\033[0;34;40m\tReceived\033[0m", body)
    ch.basic_ack(delivery_tag=method.delivery_tag)
    # 一条消息被一个消费者接收后,该消息就从队列删除

channel.basic_qos(prefetch_count=1)   #一次处理一个队列
channel.basic_consume('hello1',callback, auto_ack=False)
print('Waiting for messages.To exit press CTRL+C')
channel.start_consuming()

RPC(Remote Procedure Call)

 

client端发的消息被server端接收后,server端会调用callback函数,执行任务后,还需要把相应的信息发送到client

import pika
import uuid

class FibonacciRpcClient(object):

    def __init__(self):

        self.connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
        self.channel = self.connection.channel()
        self.channel.queue_declare(queue='rpc_queue')
        # 随机建立一个queue,为了监听返回的结果
        result = self.channel.queue_declare('',exclusive=True)
        print('client:随机监听队列已建立')
        self.callback_queue = result.method.queue  ##队列名
        self.channel.basic_consume(self.callback_queue,self.on_response,  # 一接收客户端发来的指令就调用回调函数on_response
                                   auto_ack=True)

    def on_response(self, ch, method, props, body):  # 回调
        # 每条指令执行的速度可能不一样,指令1比指令2先发送,但可能指令2的执行结果比指令1先返回到客户端,
        # 此时如果没有下面的判断,客户端就会把指令2的结果误认为指令1执行的结果
        if self.corr_id == props.correlation_id:
            self.response = body
        print('client:收到server端处理结果')
    def call(self, n):

        self.response = None  ##指令执行后返回的消息
        self.corr_id = str(uuid.uuid4())  ##可用来标识指令(顺序)
        print('向client端发送消息'+str(n))
        self.channel.basic_publish(exchange='',
                                   routing_key='rpc_queue',  # client发送指令,发到rpc_queue
                                   properties=pika.BasicProperties(
                                       reply_to=self.callback_queue,  # 将指令执行结果返回到reply_to队列
                                       correlation_id=self.corr_id,
                                   ),
                                   body=str(n))
        while self.response is None:
            self.connection.process_data_events()  # 去queue接收数据(不阻塞)
        return int(self.response)

fibonacci_rpc = FibonacciRpcClient()
response = fibonacci_rpc.call(30)
print("client:Got %r" % response)
import pika
import time

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare(queue='rpc_queue')
print('server:管道-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("server:接受到"+str(n))
    ch.basic_publish(exchange='',  ##服务端发送返回的数据到props.reply_to队列(客户端发送指令时声明)
                     routing_key=props.reply_to,  # correlation_id (随机数)每条指令都有随机独立的标识符
                     properties=pika.BasicProperties(correlation_id=props.correlation_id),
                     body=str(fib(n)))
    print('server:回应client端')
    ch.basic_ack(delivery_tag=method.delivery_tag)  # 客户端持久化

channel.basic_qos(prefetch_count=1)  # 公平分发
channel.basic_consume('rpc_queue',on_request) # 一接收到消息就调用on_request
print("server:等待client端响应")
channel.start_consuming()

启动server

启动client

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海人001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值