??欢迎来到我的CSDN主页!??
?我是Java方文山,一个在CSDN分享笔记的博主。??
?推荐给大家我的专栏《RabbitMQ实战》。??
?点击这里,就可以查看我的主页啦!??
Java方文山的个人主页
?如果感觉还不错的话请给我点赞吧!??
?期待你的加入,一起学习,一起进步!??
一、RabbitMQ的简介
RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。 AMQP :Advanced Message Queue,高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。 RabbitMQ 最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
具体特点包括:
1.可靠性(Reliability)
RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认、发布确认。
2.灵活的路由(Flexible Routing)
在消息进入队列之前,通过 Exchange 来路由消息的。对于典型的路由功能,RabbitMQ已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能,可以将多个Exchange 绑定在一起,也通过插件机制实现自己的 Exchange 。
3.消息集群(Clustering)
多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker
4.高可用(Highly Available Queues)
队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。
5.多种协议(Multi-protocol)
RabbitMQ 支持多种消息队列协议,比如 STOMP、MQTT 等等。
6.多语言客户端(Many Clients)
RabbitMQ 几乎支持所有常用语言,比如 Java、.NET、Ruby 等等。
7.管理界面(Management UI)
RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker 的许多方面。
8.跟踪机制(Tracing)
如果消息异常,RabbitMQ 提供了消息跟踪机制,使用者可以找出发生了什么。
9.插件机制(Plugin System)
RabbitMQ 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。
二、RabbitMQ使用场景
服务解耦
假设有这样一个场景, 服务A产生数据, 而服务B,C,D需要这些数据, 那么我们可以在A服务中直接调用B,C,D服务,把数据传递到下游服务即可
但是,随着我们的应用规模不断扩大,会有更多的服务需要A的数据,如果有几十甚至几百个下游服务,而且会不断变更,再加上还要考虑下游服务出错的情况,那么A服务中调用代码的维护会极为困难
这是由于服务之间耦合度过于紧密
再来考虑用RabbitMQ解耦的情况
A服务只需要向消息服务器发送消息,而不用考虑谁需要这些数据;下游服务如果需要数据,自行从消息服务器订阅消息,不再需要数据时则取消订阅即可
流量削峰
假设我们有一个应用,平时访问量是每秒300请求,我们用一台服务器即可轻松应对
而在高峰期,访问量瞬间翻了十倍,达到每秒3000次请求,那么单台服务器肯定无法应对,这时我们可以考虑增加到10台服务器,来分散访问压力
但如果这种瞬时高峰的情况每天只出现一次,每次只有半小时,那么我们10台服务器在多数时间都只分担每秒几十次请求,这样就有点浪费资源了
这种情况,我们就可以使用RabbitMQ来进行流量削峰,高峰情况下,瞬间出现的大量请求数据,先发送到消息队列服务器,排队等待被处理,而我们的应用,可以慢慢的从消息队列接收请求数据进行处理,这样把数据处理时间拉长,以减轻瞬时压力
这是消息队列服务器非常典型的应用场景
异步调用
考虑定外卖支付成功的情况
支付后要发送支付成功的通知,再寻找外卖小哥来进行配送,而寻找外卖小哥的过程非常耗时,尤其是高峰期,可能要等待几十秒甚至更长
这样就造成整条调用链路响应非常缓慢
而如果我们引入RabbitMQ消息队列,订单数据可以发送到消息队列服务器,那么调用链路也就可以到此结束,订单系统则可以立即得到响应,整条链路的响应时间只有200毫秒左右
寻找外卖小哥的应用可以以异步的方式从消息队列接收订单消息,再执行耗时的寻找操作
案例分析
小红希望小明多读书 , 常寻找好书给小明看 , 之前的方式是这样:小红问小明什么时候有空 , 把书给小明送去 , 并亲眼监督小明读完书才走 . 久而久之 , 两人都觉得麻烦 . 后来的方式改成了:小红对小明说「我放到书架上的书你都要看」 , 然后小红每次发现不错的书都放到书架上 , 小明则看到书架上有书就拿下来看 . 书架就是一个消息队列 , 小红是生产者 , 小明是消费者 . 带来的好处 1. 小红想给小明书的时候 , 不必问小明什么时候有空 , 亲手把书交给他了 , 小红只把书放到书架上就行了 . 这样小红小明的时间都更自由 . 2. 小红相信小明的读书自觉和读书能力 , 不必亲眼观察小明的读书过程 , 小红只要做一个放书的动作 , 很节省时间 . 3. 当明天有另一个爱读书的小伙伴小强加入 , 小红仍旧只需要把书放到书架上 , 小明和小强从书架上取书即可 4. 书架上的书放在那里 , 小明阅读速度快就早点看完 , 阅读速度慢就晚点看完 , 没关系 , 比起小红把书递给小明并监督小明读完的方式 , 小明的压力会小一些 . 消息队列特点 1. 解耦 : 每个成员不必受其他成员影响 , 可以更独立自主 , 只通过一个简单的容器来联系 . 2. 提速 : 小红选只要做一个放书的动作 , 为自己节省了大量时间 . 3. 广播 : 小红只需要劳动一次 , 就可以让多个小伙伴有书可读 , 这大大地节省了她的时间 , 也让新的小伙伴的加入成本很低 . 4. 错峰与流控 : 小红给书的频率不稳定 , 如果今明两天连给了五本 , 之后隔三个月才又给一本 , 那小明只要在三个月内从书架上陆续取走五本书读完就行了 , 压力就不那么大了三、RabbitMQ基本概念
RabbitMQ是一种消息中间件,用于处理来自客户端的异步消息。服务端将要发送的消息放入到队列池中。接收端可以根据RabbitMQ配置的转发机制接收服务端发来的消息。RabbitMQ依据指定的转发规则进行消息的转发、缓冲和持久化操作,主要用在多服务器间或单服务器的子系统间进行通信,是分布式系统标准的配置。
Exchange
接受生产者发送的消息,并根据Binding规则将消息路由给服务器中的队列。ExchangeType决定了Exchange路由消息的行为。在RabbitMQ中,ExchangeType常用的有direct、Fanout和Topic三种。
Message Queue
消息队列。我们发送给RabbitMQ的消息最后都会到达各种queue,并且存储在其中(如果路由找不到相应的queue则数据会丢失),等待消费者来取。
Binding Key
它表示的是Exchange与Message Queue是通过binding key进行联系的,这个关系是固定。
Routing Key
生产者在将消息发送给Exchange的时候,一般会指定一个routing key,来指定这个消息的路由规则。这个routing key需要与Exchange Type及binding key联合使用才能生,我们的生产者只需要通过指定routing key来决定消息流向哪里。
消息队列相关
AMQP 一个提供统一消息服务的应用层标准高级消息队列协议 , 是一个通用的应用层协议 消息发送与接受的双方遵守这个协议可以实现异步通讯 . 这个协议约定了消息的格式和工作方式 . 除了我们的RabbitMQ还有以下几种消息中间件供大家参考
四、Docker安装部署RabbitMQ
安装RabbitMQ
注意获取镜像的时候要获取management版本的,不要获取last版本的,management版本的才带有管理界面
docker pull rabbitmq:management
创建并启动RabbitMQ
docker run -d \--name my-rabbitmq \-p 5672:5672 -p 15672:15672 \--hostname my-rabbitmq-host \-e RABBITMQ_DEFAULT_VHOST=my_vhost \-e RABBITMQ_DEFAULT_USER=admin \-e RABBITMQ_DEFAULT_PASS=admin \--restart=always \rabbitmq:management
--hostname:主机名(RabbitMQ的一个重要注意事项是它根据所谓的 “节点名称” 存储数据,默认为主机名) -e:指定环境变量: RABBITMQ_DEFAULT_VHOST:默认虚拟机名 RABBITMQ_DEFAULT_USER:默认的用户名 RABBITMQ_DEFAULT_PASS:默认用户名的密码 端口5672:这是RabbitMQ的AMQP(高级消息队列协议)端口。它用于客户端与RabbitMQ服务器之间的通信。生产者和消费者通过5672端口发送和接收消息。端口15672:这是RabbitMQ的管理界面端口,也称为RabbitMQ Management Plugin端口。通过该端口,可以使用Web浏览器访问RabbitMQ的管理界面,查看和管理队列、交换机、绑定等信息。管理界面提供了可视化的方式来监控和管理RabbitMQ服务器。 开放端口
firewall-cmd --zone=public --add-port=5672/tcp --add-port=15672/tcp --permanent
刷新防火墙
firewall-cmd --reload
访问管理控制台 http://虚拟机IP:15672
用户名密码是 admin
Overview(概览):提供了关于 RabbitMQ 服务器的总体信息,包括节点的状态、内存和磁盘使用情况、连接数等。
Connections(连接):显示当前与 RabbitMQ 服务器建立的所有连接。您可以查看每个连接的详细信息,例如连接的协议、通信状态和客户端地址等。
Channels(通道):显示当前在 RabbitMQ 中打开的所有通道。通道是连接上的虚拟连接,用于在客户端和服务器之间传输消息。
Exchanges(交换机):列出了在 RabbitMQ 中定义的所有交换机。交换机是消息的接收和路由中心,它将消息从发送者传递给一个或多个队列。
Queues and Streams(队列和流):显示 RabbitMQ 中定义的所有队列和流。队列是消息的临时存储区域,消息在发送到消费者之前将被存储在队列中。流是一种新的数据结构,用于持久化和流式传输消息。
Admin(管理员):提供了一些管理工具和操作,如添加用户、设置权限、查看日志等。管理员可以使用这些功能来管理 RabbitMQ 服务器的配置和运行状态。
添加RabbitMQ用户
我们使用admin进行登录是不太合适的,所以添加一个用户赋予admin权限
用户是添加好了,但是现在没有访问虚拟机的权限
点击weiwei找到Set permission赋权
五、Spring集成
1.环境搭建
这里我们创建一个SpringCloud项目,首先使用Maven项目作为我们的父项目
再创建子项目这里有两个身份项目,一个是生产者另一个是消费者,扮演不同的模块或不同服务
勾选我们所需依赖
现在我们的子项目创建出来了,但是它和父项目还没有任何关系,所以我们还需要让它继承父项目,在子项目中添加下面依赖
<parent> <groupId>org.example</groupId> <artifactId>Rabbit</artifactId> <version>0.0.1-SNAPSHOT</version> </parent>
不但子项目要继承父项目,父项目那边也需要定义子项目才能双向奔赴
<!--定义子项目--> <modules> <module>produce</module> </modules>
另项目直接复制生产者项目即可,随后将“produce”改为“consume”
2.配置RabbitMQ文件
将下列配置修改成自己的配置即可
server: port: 9999spring: application: name: consumerabbitmq: host: 118.178.124.148 username: weiwei password: 123456 port: 5672 virtual-host: my_vhost
3.代码编写
生产者produce
通过在配置类中定义队列,可以使得该队列在应用启动时自动创建,并且在需要时可以被其他组件注入和使用。
package org.example.produce.config;import org.springframework.amqp.core.Queue;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration@SuppressWarnings("all")public class RabbitConfig { @Bean public Queue firstQueue() { return new Queue("firstQueue"); }}
通过将 Sender
类注册为 Spring 容器中的一个组件,并注入 AmqpTemplate
对象,我们可以在需要时通过调用 send01
方法发送消息到指定的 RabbitMQ 队列中
package org.example.produce.controller;import org.springframework.amqp.core.AmqpTemplate;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.RequestMapping;@RestControllerpublic class Sender { @Autowired private AmqpTemplate rabbitTemplate; @RequestMapping("send01") public void send01() { rabbitTemplate.convertAndSend("firstQueue", "Hello World"); }}
消费者consume 通过将 Receiver
类注册为 Spring 容器中的一个组件,并使用 @RabbitListener
和 @RabbitHandler
注解,当有消息到达名为 "firstQueue" 的队列时,该类会自动触发 process
方法对消息进行处理。
package org.example.produce.controller;import lombok.extern.slf4j.Slf4j;import org.springframework.amqp.rabbit.annotation.RabbitHandler;import org.springframework.amqp.rabbit.annotation.RabbitListener;import org.springframework.stereotype.Component;@Component@SuppressWarnings("all")@Slf4j@RabbitListener(queues = "firstQueue")public class Receiver { @RabbitHandler public void process(String msg) { log.warn("接收到:" + msg); }}
现在我们在网页上访问一下
我们现在可以看到RabbitMQ的管理页面有七条队列
我们现在开启消费者看看能否接收到这些请求
开启的瞬间我们的消息就被读取了
五、注意事项
如果在spring中集成RabbitMQ出现下列错误可能是以下情况
没有同时开放15672和5672端口如果使用的是云服务器,需要开放云服务的安全组pom.xml文件中的spring.rabbitmq.host所需要填写的IP是配置 RabbitMQ 主机地址,我这里用的是虚拟机就填写虚拟机的地址,诸如此类层级关系没有对齐或是IP\端口\账户\密码\主机地址输入错误
到这里我的分享就结束了,欢迎到评论区探讨交流!!
?如果觉得有用的话还请点个赞吧 ?