Rabbitmq 简单介绍

1. 消息队列介绍

消息队列就是基础数据结构中的“先进先出”的一种数据机构。例如生活中买东西,需要排队,先排的人先买消费,就是典型的“先进先出”

在这里插入图片描述

1.1 MQ解决什么问题

MQ是一直存在,不过随着微服务架构的流行,成了解决微服务之间问题的常用工具。

  1. 应用解耦

以电商应用为例,应用中有订单系统、库存系统、物流系统、支付系统。用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障,都会造成下单操作异常。

当转变成基于消息队列的方式后,系统间调用的问题会减少很多,比如物流系统因为发生故障,需要几分钟来修复。在这几分钟的时间里,物流系统要处理的内存被缓存在消息队列中,用户的下单操作可以正常完成。当物流系统恢复后,继续处理订单信息即可,中单用户感受不到物流系统的故障。提升系统的可用性

在这里插入图片描述

  1. 流量消峰

举个栗子,如果订单系统最多能处理一万次订单,这个处理能力应付正常时段的下单时绰绰有余,正常时段我们下单一秒后就能返回结果。但是在高峰期,如果有两万次下单操作系统是处理不了的,只能限制订单超过一万后不允许用户下单。

使用消息队列做缓冲,我们可以取消这个限制,把一秒内下的订单分散成一段时间来处理,这事有些用户可能在下单十几秒后才能收到下单成功的操作,但是比不能下单的体验要好。

  1. 消息分发

多个服务队数据感兴趣,只需要监听同一类消息即可处理。
在这里插入图片描述
例如A产生数据,B对数据感兴趣。如果没有消息的队列A每次处理完需要调用一下B服务。过了一段时间C对数据也感性,A就需要改代码,调用B服务,调用C服务。只要有服务需要,A服务都要改动代码。很不方便。

在这里插入图片描述
有了消息队列后,A只管发送一次消息,B对消息感兴趣,只需要监听消息。C感兴趣,C也去监听消息。A服务作为基础服务完全不需要有改动

  1. 异步消息

在这里插入图片描述
有些服务间调用是异步的,例如A调用B,B需要花费很长时间执行,但是A需要知道B什么时候可以执行完,以前一般有两种方式,A过一段时间去调用B的查询api查询。或者A提供一个callback api,B执行完之后调用api通知A服务。这两种方式都不是很优雅

在这里插入图片描述
使用消息总线,可以很方便解决这个问题,A调用B服务后,只需要监听B处理完成的消息,当B处理完成后,会发送一条消息给MQ,MQ会将此消息转发给A服务。

这样A服务既不用循环调用B的查询api,也不用提供callback api。同样B服务也不用做这些操作。A服务还能及时的得到异步处理成功的消息

1.2 常见消息队列及比较

在这里插入图片描述

Kafka在于分布式架构,RabbitMQ基于AMQP协议来实现,RocketMQ思路来源于kafka,改成了主从结构,在事务性可靠性方面做了优化。广泛来说,电商、金融等对事务性要求很高的,可以考虑RabbitMQ和RocketMQ,对性能要求高的可考虑Kafka

2. Rabbitmq 安装

官网:https://www.rabbitmq.com/getstarted.html

2.1 服务端原生安装

# 安装erlang
yum -y install erlang

# 安装 RabbitMQ
yum -y install rabbitmq-server

2.2 服务端 Docker 安装

docker pull rabbitmq:management
docker run -di --name Myrabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 rabbitmq:management

启动后访问 15672 端口,如下所示

在这里插入图片描述

输入账号密码后(账号密码为 docker 启动是配置的)如下所示

在这里插入图片描述

3. python 操作 rabbitmq

pip3 install pika

3.1 基本使用(生产者消费者模型)

生产者

import pika

# 无密码
# connection = pika.BlockingConnection(pika.ConnectionParameters('127.0.0.1'))

# 有密码
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.101', credentials=credentials))
channel = connection.channel()

# 声明一个队列(创建一个队列)
channel.queue_declare(queue='xwx')

# routing_key 必须等于队列的名字,body 是发送的消息
channel.basic_publish(exchange='',
                      routing_key='xwx',  # 消息队列名称
                      body='hello world')
connection.close()

运行之后效果

在这里插入图片描述

消费者

import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.101', credentials=credentials))
channel = connection.channel()

# 声明一个队列(创建一个队列)
channel.queue_declare(queue='xwx')


def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)


channel.basic_consume(queue='xwx', on_message_callback=callback, auto_ack=True)

channel.start_consuming()

消费之前的任务,结果如下所示

在这里插入图片描述

3.2 消息安全之 ack

消费者收到消息后,要回复给消息队列,消息队列收到回复,该消息就删除,如果收不到,就一直放在消息队列中,再起一个消费者,还会消费这个消息

import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.101', credentials=credentials))
channel = connection.channel()

# 声明一个队列(创建一个队列)
channel.queue_declare(queue='xwx')


def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)


# 将 auto_ack 修改为 False,消息队列就不会收到回复
channel.basic_consume(queue='xwx', on_message_callback=callback, auto_ack=False)

channel.start_consuming()

结果如下

在这里插入图片描述

可以使用其官方文档提供的解决方式,将如下代码添加至逻辑中

ch.basic_ack(delivery_tag=method.delivery_tag)
import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.101', credentials=credentials))
channel = connection.channel()

# 声明一个队列(创建一个队列)
channel.queue_declare(queue='xwx')


def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)
    ch.basic_ack(delivery_tag=method.delivery_tag)


channel.basic_consume(queue='xwx', on_message_callback=callback, auto_ack=False)

channel.start_consuming()

4. 持久化

消息丢失:原本放在内存中,如果 rabbimq 服务挂掉,消息就没了,通过持久化,保证队列和消息都不丢失
队列持久化就是一个新队列

生产者

import pika

# 无密码
# connection = pika.BlockingConnection(pika.ConnectionParameters('127.0.0.1'))

# 有密码
credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.101', credentials=credentials))
channel = connection.channel()

# 声明一个队列,durable=True 支持持久化,注意队列必须是新的才可以
channel.queue_declare(queue='hello', durable=True)

channel.basic_publish(exchange='',
                      routing_key='hello',  # 消息队列名称
                      body='Hello')
connection.close()

如下所示
在这里插入图片描述
消费者

import pika

credentials = pika.PlainCredentials("admin", "admin")
connection = pika.BlockingConnection(pika.ConnectionParameters('10.0.0.101', credentials=credentials))
channel = connection.channel()

# 声明一个队列(创建一个队列), 也需要设置 durable=True
channel.queue_declare(queue='hello', durable=True)


def callback(ch, method, properties, body):
    print("消费者接受到了任务: %r" % body)
    ch.basic_ack(delivery_tag=method.delivery_tag)


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

channel.start_consuming()

5. 闲置消费

正常情况如果有多个消费者,是按照顺序第一个消息给第一个消费者,第二个消息给第二个消费者

但是可能第一个消息的消费者处理消息很耗时,一直没结束,就可以让第二个消费者优先获得闲置的消息,需要添加如下语句

# 哪个消费者空闲,就会去消费消息
channel.basic_qos(prefetch_count=1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值