教你如何认识RabbitMQ

RabbitMQ

什么是消息队列

消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。下面通过一篇故事来了解消息列表

故事

有一天,产品跑来说:“我们要做一个用户注册功能,需要在用户注册成功后给用户发一封成功邮件。”

小明(攻城狮):“好,需求很明确了。” 不就提供一个注册接口,保存用户信息,同时发起邮件调用,待邮件发送成功后,返回用户操作成功。没一会功夫,代码就写完了。验证功能没问题后,就发布上线了。

线上正常运行了一段时间,产品匆匆地跑来说:“你做的功能不行啊,运营反馈注册操作响应太慢,已经有好多用户流失了。”

小明听得一身冷汗,赶紧回去改。他发现,原先的以单线程同步阻塞的方式进行邮件发送,确实存在问题。这次,他利用了 JAVA 多线程的特性,另起线程进行邮件发送,主线程直接返回保存结果。测试通过后,赶紧发布上线。小明心想,这下总没问题了吧。

没过多久,产品又跑来了,他说:“现在,注册操作响应是快多了。但是又有新的问题了,有用户反应,邮件收不到。能否在发送邮件时,保存一下发送的结果,对于发送失败的,进行补发。”

小明一听,哎,又得熬夜加班了。产品看他一脸苦逼的样子,忙说:“邮件服务这块,别的团队都已经做好了,你不用再自己搞了,直接用他们的服务。”

小明赶紧去和邮件团队沟通,谁知他们的服务根本就不对外开放。这下小明可开始犯愁了,明知道有这么一个服务,可是偏偏又调用不了。

邮件团队的人说,“看你愁的,我给你提供了一个类似邮局信箱的东西,你往这信箱里写上你要发送的消息,以及我们约定的地址。之后你就不用再操心了,我们自然能从约定的地址中取得消息,进行邮件的相应操作。”

后来,小明才知道,这就是外界广为流传的消息队列。你不用知道具体的服务在哪,如何调用。你要做的只是将该发送的消息,向你们约定好的地址进行发送,你的任务就完成了。对应的服务自然能监听到你发送的消息,进行后续的操作。这就是消息队列最大的特点,将同步操作转为异步处理,将多服务共同操作转为职责单一的单服务操作,做到了服务间的解耦

消息队列的作用

消息队列就是用来解耦的?如果你只是这么认为,那就大错特错了,消息队列的功能有很多,解耦只是它的功能之一,总结来说,消息队列有如下作用:

1、解耦

对于生产者和消费者而言,他们不必关注对方是谁,是什么语言,是什么平台,只用关注MQ本身即可,如果消费者发生改变,生产者也无需做出改变。

2、削峰

流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛

应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。

  • 可以控制活动的人数
  • 可以缓解短时间内高流量压垮应用

在这里插入图片描述

  • 用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面
  • 秒杀业务根据消息队列中的请求信息,再做后续处理

为啥秒杀时总是失败,就是因为别人用了消息队列。

3、异步

场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种 1.串行的方式;2.并行方式

(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端

在这里插入图片描述
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间

在这里插入图片描述

假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。

因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)

小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?

引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:

在这里插入图片描述

按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍

RabbitMQ简介

RabbitMQ是一个开源的,在AMQP基础上完成的,可复用的企业消息系统。它支持主流操作系统,Linux、Windows、MaxOX等。同时它也支持多语言开发,Java、Python、Ruby、.net、PHP、C/C++、node.js等。

(注):AMQP是消息队列的一个协议。

在这里插入图片描述

其它MQ产品

ActiveMQ、Kafka

RabbitMQ的安装

1.下载

下载地址:http://www.rabbitmq.com/download.html

2.windows下安装

安装Erlang
下载:http://www.erlang.org/download/otp_win64_17.3.exe

安装完成。

开始菜单里出现如下选项:

在这里插入图片描述
启动、停止、重新安装等。

启用管理工具

在这里插入图片描述

1、双击
2、进入C:\Program Files (x86)\RabbitMQ Server\rabbitmq_server-3.4.1\sbin输入命令:

rabbitmq-plugins enable rabbitmq_management

这样就启动了管理工具,可以试一下命令:
停止:net stop RabbitMQ
启动:net start RabbitMQ

3、在浏览器中输入地址查看:http://127.0.0.1:15672/

在这里插入图片描述

4、使用默认账号登录:guest/ guest

3.Linux下安装

1、安装Erlang
2、添加yum支持

cd /usr/local/src/
mkdir rabbitmq
cd rabbitmq
wget http://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm
rpm -Uvh erlang-solutions-1.0-1.noarch.rpm
rpm --import http://packages.erlang-solutions.com/rpm/erlang_solutions.asc

使用yum安装:

sudo yum install erlang

在这里插入图片描述

3.安装RabbitMQ
上传rabbitmq-server-3.4.1-1.noarch.rpm文件到/usr/local/src/rabbitmq/
安装:

rpm -ivh rabbitmq-server-3.4.1-1.noarch.rpm

4、启动、停止

service rabbitmq-server start
service rabbitmq-server stop
service rabbitmq-server restart

5.设置开机启动

chkconfig rabbitmq-server on

6.设置配置文件

cd /etc/rabbitmq
cp /usr/share/doc/rabbitmq-server-3.4.1/rabbitmq.config.example /etc/rabbitmq/

mv rabbitmq.config.example rabbitmq.config

7.开启用户远程访问

vi /etc/rabbitmq/rabbitmq.config

在这里插入图片描述

注意要去掉后面的逗号。
8.开启web界面管理工具

rabbitmq-plugins enable rabbitmq_management
service rabbitmq-server restart

9.防火墙开放15672端口

/sbin/iptables -I INPUT -p tcp --dport 15672 -j ACCEPT
/etc/rc.d/init.d/iptables save

添加用户

1.添加admin用户

[

2.用户角色

1、超级管理员(administrator)

可登陆管理控制台,可查看所有的信息,并且可以对用户,策略(policy)进行操作。

2、监控者(monitoring)

可登陆管理控制台,同时可以查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)

3、策略制定者(policymaker)

可登陆管理控制台, 同时可以对policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。

4、普通管理者(management)

仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。

5、其他

无法登陆管理控制台,通常就是普通的生产者和消费者。

五种队列

简单队列

在这里插入图片描述

P:消息的生产者
C:消息的消费者
红色:队列

生产者将消息发送到队列,消费者从队列中获取消息。

Work模式

在这里插入图片描述

在这里插入图片描述

一个生产者、2个消费者。

一个消息只能被一个消费者获取。

测试结果:

1、消费者1和消费者2获取到的消息内容是不同的,同一个消息只能被一个消费者获取。

2、消费者1和消费者2获取到的消息的数量是相同的,一个是消费奇数号消息,一个是偶数。

  • 其实,这样是不合理的,因为消费者1线程停顿的时间短。应该是消费者1要比消费者2获取到的消息多才对。
    RabbitMQ 默认将消息顺序发送给下一个消费者,这样,每个消费者会得到相同数量的消息。即轮询(round-robin)分发消息。
  • 怎样才能做到按照每个消费者的能力分配消息呢?联合使用 Qos 和 Acknowledge 就可以做到。
    basicQos 方法设置了当前信道最大预获取(prefetch)消息数量为1。消息从队列异步推送给消费者,消费者的 ack 也是异步发送给队列,从队列的视角去看,总是会有一批消息已推送但尚未获得 ack 确认,Qos 的 prefetchCount 参数就是用来限制这批未确认消息数量的。设为1时,队列只有在收到消费者发回的上一条消息 ack 确认后,才会向该消费者发送下一条消息。prefetchCount 的默认值为0,即没有限制,队列会将所有消息尽快发给消费者。

2个概念

  • 轮询分发 :使用任务队列的优点之一就是可以轻易的并行工作。如果我们积压了好多工作,我们可以通过增加工作者(消费者)来解决这一问题,使得系统的伸缩性更加容易。在默认情况下,RabbitMQ将逐个发送消息到在序列中的下一个消费者(而不考虑每个任务的时长等等,且是提前一次性分配,并非一个一个分配)。平均每个消费者获得相同数量的消息。这种方式分发消息机制称为Round-Robin(轮询)。
  • 公平分发 :虽然上面的分配法方式也还行,但是有个问题就是:比如:现在有2个消费者,所有的奇数的消息都是繁忙的,而偶数则是轻松的。按照轮询的方式,奇数的任务交给了第一个消费者,所以一直在忙个不停。偶数的任务交给另一个消费者,则立即完成任务,然后闲得不行。而RabbitMQ则是不了解这些的。这是因为当消息进入队列,RabbitMQ就会分派消息。它不看消费者为应答的数目,只是盲目的将消息发给轮询指定的消费者。

为了解决这个问题,我们使用basicQos( prefetchCount = 1)方法,来限制RabbitMQ只发不超过1条的消息给同一个消费者。当消息处理完毕后,有了反馈,才会进行第二次发送。

还有一点需要注意,使用公平分发,必须关闭自动应答,改为手动应答。

生产者

String msg = "work消息";
for (int i = 0; i < 10; i++) {
    rabbitTemplate.convertAndSend("DirectQueue", msg);
}
Thread.sleep(2000);

消费者

@RabbitListener(queuesToDeclare = @Queue("DirectQueue"))
public void listener1(String msg) throws InterruptedException {
    System.out.println("消息者1接收的消息:" + msg);
}

@RabbitListener(queuesToDeclare = @Queue("DirectQueue"))
public void listener2(String msg) throws InterruptedException {
    System.out.println("消息者2接收的消息:" + msg);
}

订阅模式

在这里插入图片描述

在这里插入图片描述

1、1个生产者,多个消费者
2、每一个消费者都有自己的一个队列
3、生产者没有将消息直接发送到队列,而是发送到了交换机
4、每个队列都要绑定到交换机
5、生产者发送的消息,经过交换机,到达队列,实现,一个消息被多个消费者获取的目的
注意:一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费。

测试结果:
同一个消息被多个消费者获取。一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费到消息。

生产者

String msg = "订阅消息";
for (int i = 0; i < 10; i++) {
    rabbitTemplate.convertAndSend("DirectQueue", "", msg + i);
}
Thread.sleep(2000);

订阅者

/**
     * 队列1
     * @param msg
     * @throws InterruptedException
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "DirectQueue", durable = "true"),
            exchange = @Exchange(
                    value = "DirectQueue",
                    type = ExchangeTypes.FANOUT
            )
    ))
    public void listener1(String msg) throws InterruptedException {
        System.out.println("订阅1接收的消息:" + msg);
    }

    /**
     * 队列2
     * @param msg
     * @throws InterruptedException
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "DirectQueue2", durable = "true"),
            exchange = @Exchange(
                    value = "DirectQueue",
                    type = ExchangeTypes.FANOUT
            )
    ))
    public void listener2(String msg) throws InterruptedException {
        System.out.println("订阅2接收的消息:" + msg);
    }

路由模式

在这里插入图片描述

在这里插入图片描述

主题模式(通配符模式)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

同一个消息被多个消费者获取。一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费到消息。

SpringBoot集成RabbitMQ

1、依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2、配置rabbitmq的安装地址、端口以及账户信息

spring.application.name=spirng-boot-rabbitmq
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin

3、配置队列

package com.zpc.rabbitmq;

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitConfig {
    @Bean
    public Queue queue() {
        return new Queue("q_hello");
    }
}

练习

使用RabbitMQ完成用户注册、登录时的邮件功能。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值