RabbitMQ

消息队列在软件中的应用场景

  1. 异步处理上(优于原先的方式)

为什么优于呢?

  1. 首先,通常情况下,如上图我们其实不用消息队列的情况下,其实也可以不用100ms,不用allof即可

  2. 那么优势在哪呢?在它消息队列的额外特点(可靠性削峰填谷),即可保证大多数消息也就是我们发送大多数的请求能达到准确达到同时它还会控制

  3. 应用解耦

  4. 流量控制

把对应的消息过多会存到队列中,之后依次发送到对应服务

大多数应用中,可通过消息服务中间件来提升系统异步通信,扩展解耦能力(解耦的相当大的优势在于我之后某个子服务更新换代后可以不用再改原代码,而可以直接通过消息队列来更改)

消息队列的重要概念

消息代理

即是消息队列的服务器,帮你代理一下

目的地

消息发送的终点或接收的位置(有别于接收方/消费者,不是接收方啊!是消息的暂存对象)

主要两种形式的目的地(一对一&一对多)

  1. 队列(点对点的通信模式):每个消息只由一个消费者读取
  2. 主题(topic):多个消费者可以订阅同一个主题,并且每个消费者都可以接收该主题上发送的消息

springboot的使用上

  1. @EnableRabbit开启支持
  2. 自动会导入RabbitAutoConfiguration

RabbitMq的核心概念

  1. message:

    就是我们发送的消息(要传给消费者的东西),包括消息头(类似http的消息头,放一些约定的东西:如routing-key(路由键),priority,delivery-mode(指出该消息可能需要持久性存储)),和消息体(放具体要传送的内容)

  2. Publisher:

    消息的生产者,向交换器(指定发送哪个)发布消息的客户端程序

  3. Exchange:

    交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列

    4类:direct(默认),fanout,topic,和headers,不同exchange转发消息策略有别

  4. Queue

    保存消息直到发送给消费者,消息的容器,也是消息的终点

  5. Binding

    绑定,用于消息队列和交换器之间的关联,一个绑定就是基于路由键将路由器和消息队列连接起来的路由规则,Exchange和Queue的绑定可以是多对多的关系

  6. Connection

    网络连接,如TCP

  7. Channel

    信道,AMQP命令都是通过信道发出去的,不管发布消息,订阅队列还是接收消息都是通过信道完成(对于操作系统来说建立和销毁TCP都是非常昂贵的开销,所以出了信道的概念,来复用一条TCP连接)

  8. Consumer(从消息队列中获取信息的客户端)

  9. Virtual Host:

    虚拟主机(也就是一台消息队列服务器分出一个虚拟消息队列,两者互不干扰)

  10. Broker:(消息队列服务器实体)

核心图

Binding是交换机与队列的粘黏剂

疑问?消息队列如何知道是哪个Consumer需要消息呢?

Consumer需要去订阅topic或者queue,类似订牛奶,之后有牛奶就给你家送

exchange的类型

  1. headers(性能方面过于差劲,基本不用)
  2. direct(精准匹配:消息的routekey必须和交换机所绑定的route精准匹配才会发送(无通配符))
  3. fanout(无匹配机制,发送到所有订阅的消费者)
  4. topic(有通配符:可以通过通配符来进行匹配)
    1. route的组成形式是:单词.单词.单词
    2. 其中.分隔的是单词,同时通配符必须整个代替单词,不存在以下情况:aaa#.444
    3. 通配符:(#:0个或多个字母)(*:一个字母)

springboot快速导入

  1. pom中添加spring-boot-starter-amqp
  2. 查看RabbitAutoConfiguration,看一下其中的RabbitProperties了解到我们需要使用什么前缀,同时需要配置哪些信息
  3. 还可以观察AutoConfiguration里面为我们添加了哪些类
  4. 添加注解@EnableRabbit

在spring中的运用

rabbitAdmin

用于创造exchange和queue以及binding中

  • exchange(学到的-->当发现通常的exchange是个接口时,可以进入然后通过ctrl+h来得到其实现类,然后使用其实现类)
 @Test
void createExchange(){
//String name, boolean durable, boolean autoDelete, Map<String, Object> arguments
Exchange exchange = new DirectExchange("dong",true,false,null);
rabbitAdmin.declareExchange(exchange);
log.info("exchange创建成功--->"+exchange.getName());
}
  • queue
@Test
void createQueue(){
//String name, boolean durable, boolean exclusive, boolean autoDelete, @Nullable Map<String, Object> arguments
//exclusive,一个连接成功后,其他的无法再次连接(不符合我们正常使用,正常下是多个连接上,然后让一个获得信息)
Queue queue = new Queue("dongQueue",true,false,false,null);
rabbitAdmin.declareQueue(queue);
log.info("exchange创建成功--->"+queue.getName());
}
  • Binding
 @Test
void createBinding(){
//@Nullable Queue lazyQueue(惰性队列), @Nullable String destination, Binding.DestinationType destinationType, String exchange, @Nullable String routingKey, @Nullable Map<String, Object> arguments
//就是把消息存储磁盘而非内存
//读消息慢(要看IO)
Binding binding = new Binding("dongQueue", Binding.DestinationType.QUEUE,"dong","dongQueue",null);
rabbitAdmin.declareBinding(binding);
log.info("binding创建成功");
}

rabbitTemplate

封装了常用的方法例如发送信息等

 @Test
void sendMessage(){
OrderItemEntity orderItemEntity = new OrderItemEntity();
orderItemEntity.setSkuName("娃哈哈h");
rabbitTemplate.convertAndSend("dongQueue22",orderItemEntity,new CorrelationData(UUID.randomUUID().toString()));
}

注意事项

  1. 可以直接传入任何java的对象
  2. 会默认使用Serializable的序列化方式

更改序列化方式为JSON

@Bean
@PostConstruct
public MessageConverter messageConverter(){
Jackson2JsonMessageConverter messageConverter = new Jackson2JsonMessageConverter();
return messageConverter;
}

在configuration中注入bean为 MessageConverter

之后会不会再去生成原先默认的Converter对象

原理如下:

在RabbitAutoConfiguration中有这么个方法,来生成rabbitTemplate的配置器

那么注意了!

像这样的ObjectProvider 的意思是,如果容器里有,那么就用容器里的,没有的话,再自己创建

于是我们正在容器中添加了对应的Json的MessageConverter

public RabbitTemplateConfigurer rabbitTemplateConfigurer(RabbitProperties properties, ObjectProvider<MessageConverter> messageConverter, ObjectProvider<RabbitRetryTemplateCustomizer> retryTemplateCustomizers)

RabbitListener以及RabbitHandler

主要负责接收消息,标志方法后,一旦监听到对应队列有消息则会自动调用

使用这两注解的前提是在配置类中添加注解@EnableRabbit

两者都是注解都可以实现接收消息的功能

区别:

  • @RabbitListener

    • 能标注在类和方法
    • 可以标识监听哪些队列,queues =
  • @RabbitHandler
    • 只能标注方法

最佳实践

通常是在类上标注@RabbitListener用来指明监听的队列

然后在需要接收消息的方法上,用@RabbitHandler

优点:

之后@RabbitHandler标注的方法,会根据参数自动选择哪个方法

使用实例:

  • 可以方法参数只有OrderItemEntity orderItemEntity
  • 会自动帮我们拆
  • channel最好还是使用一下,之后可靠传递有用
@RabbitHandler()
void getMessage(Message message, OrderItemEntity orderItemEntity, Channel channel){ //log.info("接收到消息,消息类型:{},消息头:{}",message,headers);
System.out.println(orderItemEntity.toString());
long deliveryTag = message.getMessageProperties().getDeliveryTag();
//参数1:deliveryTag(所有的消息会放到channel中,同时channel会按顺序(递增)生成一个id):
//参数2:boolean multiple 是否批处理(同一批在channel的消息都会同时确认或删除)
try {
channel.basicAck(deliveryTag,false);
} catch (IOException e) {
//long deliveryTag
// boolean multiple
// boolean requeue :是否接收完后还放回队列
try {
channel.basicNack(deliveryTag,false,false);
} catch (IOException ex) {
ex.printStackTrace();
}
//long deliveryTag, boolean requeue
//channel.basicReject(deliveryTag,false);
e.printStackTrace();
}
}

可靠投递

在各个阶段都要保证可靠

共有三个阶段

  1. Producer-->broker
  2. exchange-->queue
  3. queue-->consumer

解决confirmCallback

  • 消息只要被 broker 接收到就会执行 confirmCallback,如果是 cluster 模式,需要所有

broker 接收到才会调用 confirmCallback。

  • 被 broker 接收到只能表示 message 已经到达服务器,并不能保证消息一定会被投递

到目标 queue 里

两步:

  1. 改配置文件
# 开启发送端确认
spring.rabbitmq.publisher-confirm-type=correlated
  1. 改rabbitTemplate
@PostConstruct
public void initRabbit(){
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
*
* @param correlationData 当前消息的唯一关联数据(消息的唯一id)
* @param b 是否成功接收到消息
* @param s 失败的原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
System.out.println("correlationData"+correlationData+"----b="+b+"----s="+s);
}
});

解决returnCallback

两步:

  1. 改配置文件

其实只要第一行即可,第二行不是必须的,但提高性能

#开启发送端消息抵达队列的确认
spring.rabbitmq.publisher-returns=true
#只要抵达队列,以异步返送优先回调我们这个returnConfirm
spring.rabbitmq.template.mandatory=true
  1. 修改rabbitTemplate
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
/**
* 只要消息没有投递给指定的队列,就触发失败回调
* @param returnedMessage
*/
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("投递失败的消息"+returnedMessage.getMessage());
System.out.println("回复的状态码"+returnedMessage.getReplyCode());
System.out.println("回复的文本内容"+returnedMessage.getReplyText());
System.out.println("当时消息发送给哪个交换机:"+returnedMessage.getExchange());
System.out.println("当时用哪个路由键:"+returnedMessage.getRoutingKey());
}
});
}

解决ack

两步:

  1. 改配置
# 默认是自动签收,即只要发送到管道中即直接签收,要改成如下所示
spring.rabbitmq.listener.simple.acknowledge-mode=manual
  1. 在对应的接收信息的方法里修改逻辑

    1. 需要额外多两参数:(message,channel)
    2. 分别作为获取deliveryTag(代码中有解释)
    3. 传递该消息是否Ack或者reject or Nack
@RabbitHandler()
void getMessage(Message message, OrderItemEntity orderItemEntity, Channel channel){ //log.info("接收到消息,消息类型:{},消息头:{}",message,headers);
System.out.println(orderItemEntity.toString());
long deliveryTag = message.getMessageProperties().getDeliveryTag();
//参数1:deliveryTag(所有的消息会放到channel中,同时channel会按顺序(递增)生成一个id):
//参数2:boolean multiple 是否批处理(同一批在channel的消息都会同时确认或删除)
try {
channel.basicAck(deliveryTag,false);
} catch (IOException e) {
//long deliveryTag
// boolean multiple
// boolean requeue :是否接收完后还放回队列
try {
channel.basicNack(deliveryTag,false,false);
} catch (IOException ex) {
ex.printStackTrace();
}
//long deliveryTag, boolean requeue
//channel.basicReject(deliveryTag,false);
e.printStackTrace();
}
}

RabbitMQ基础学习Full版的更多相关文章

  1. C#RabbitMQ基础学习笔记

    RabbitMQ基础学习笔记(C#代码示例) 一.定义: MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开发).MQ是一种应用程序对应用程序的通信方法. ...

  2. rabbitmq基础学习+springboot结合rabbitmq实现回调确认confirm

    rabbitmq集群docker快速搭建 https://blog.csdn.net/u011058700/article/details/78708767 rabbitmq原理博客 https:// ...

  3. Mysql基础学习_Windows版(一)

    1.Mysql简介 Mysql是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性.所谓的关系型数据库,是建立在关系模型基础上的 ...

  4. RabbitMQ基础学习笔记(C#代码示例)

    一.定义: MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开发).MQ是一种应用程序对应用程序的通信方法.应用程序通过读写入队和出队的消息来通信,无需专 ...

  5. 狗屁不通的“视频专辑:零基础学习C语言(小甲鱼版)”(2)

    前文链接:狗屁不通的“视频专辑:零基础学习C语言(小甲鱼版)”(1) 小甲鱼在很多情况下是跟着谭浩强鹦鹉学舌,所以谭浩强书中的很多错误他又重复了一次.这样,加上他自己的错误,错谬之处难以胜数. 由于拙 ...

  6. 拒绝从入门到放弃_《鸟哥的 Linux 私房菜 — 基础学习篇(第三版)》必读目录

    目录 目录 前言 关于这本书 必看知识点 最后 前言 相信部分刚进入这个行业的新同学会对一个问题感到疑惑,为什么从培训学校出来的学员不被欢迎? 这里记录下一些我个人的看法(博主也曾有面试新员工的经历) ...

  7. SpringCloudStream学习(一)RabbitMQ基础

    应公司大佬要求,学习一下SpringCloudStream,作为技术储备.这几天也看了这方面的资料,现在写一篇笔记,以做总结.文章会从RabbitMQ基础讲起,到SpringCloudStream结束 ...

  8. RabbitMq基础教程之基本概念

    RabbitMq基础教程之基本概念 RabbitMQ是一个消息队列,和Kafka以及阿里的ActiveMQ从属性来讲,干的都是一回事.消息队列的主要目的实现消息的生产者和消费者之间的解耦,支持多应用之 ...

  9. 零基础学习openstack【完整中级篇】及openstack资源汇总

    1.你是如何学习openstack的?2.你对openstack的组件了解多少?3.你认为openstack该如何学习? 一直想写关于openstack的方面的内容,今天终于整理完成.算是完成一桩心事 ...

  10. RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙

    消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以 ...

随机推荐

  1. kafka搭建 一、单机版

    系列导航 一.kafka搭建-单机版 二.kafka搭建-集群搭建 三.kafka集群增加密码验证 四.kafka集群权限增加ACL 五.kafka集群__consumer_offsets副本数修改 ...

  2. P1228-递归【黄】

    这道大递归我一开始就找对了方向,不过了MLE,然后从网上搜索到了一个贼有用的概念--尾递归,即如果递归的下一句就是return且没有返回值或者返回值不含有递归函数则编译器会做优化,不会压入新的函数而是 ...

  3. 每天学五分钟 Liunx 110 | 存储篇:RAID

    RAID RAID 是廉价磁盘冗余阵列(Redundant Array of Inexpensive Disks)的意思.通过它可以将较小的磁盘组成较大的磁盘.   RAID 模式 RAID 有几种模 ...

  4. Rocketmq学习3——消息发送原理源码浅析

    一丶概述 RocketMQ 消息发送的原理流程可以分为以下几个步骤: 1. 创建生产者 在发送消息前,客户端首先需要创建一个消息生产者(Producer)实例,并设置必要的配置参数,如NameServ ...

  5. Linux系列之文件和目录权限

    前言 我们知道,root用户基本上可以在系统中做任何事.其他用户有更多的限制,并且通常被收集到组中.你把有类似需求的用户放入一个被授予相关权限的组,每个成员都继承组的权限. 让我们看一下: 查看权限( ...

  6. spring-transaction源码分析(3)Transactional事务失效原因

    问题概述 在Transactional方法中使用this方式调用另一个Transactional方法时,拦截器无法拦截到被调用方法,严重时会使事务失效. 类似以下代码: @Transactional ...

  7. HanLP — 感知机(Perceptron)

    感知机(Perceptron)是一个二类分类的线性分类模型,属于监督式学习算法.最终目的: 将不同的样本分本 感知机饮食了多个权重参数,输入的特征向量先是和对应的权重相乘,再加得到的积相加,然后将加权 ...

  8. 【rt-thread】构建自己的项目工程 -- 初始篇

    现以stm32f429igt6芯片的板子 & Keil5编译环境为例,记述构建适配自己板子的rt-thread工程的过程 1.拿到rt-thread源码,进入bsp/stm32/librari ...

  9. .Net 使用 MongoDB

    1.安装nuget包 MongoDB.Driver 2.简单代码 using MongoDB.Bson; using MongoDB.Driver; using System.Buffers; usi ...

  10. 使用Python+FFMPEG实现视频分割与合并

    前言 日常中偶尔会遇到需要简单剪辑处理视频的场景,以前我可能会拿出PR来剪辑一下,(别跟我说国产那些软件,剪辑完视频强制加上广告片头片尾恶心的一批),但是PR毕竟太重量级,剪个简单的视频都要花不少时间 ...