RabbitMQ基础学习Full版
RabbitMQ
消息队列在软件中的应用场景
- 异步处理上(优于原先的方式)

为什么优于呢?
- 首先,通常情况下,如上图我们其实不用消息队列的情况下,其实也可以不用100ms,不用allof即可 
- 那么优势在哪呢?在它消息队列的额外特点(可靠性和削峰填谷),即可保证大多数消息也就是我们发送大多数的请求能达到准确达到同时它还会控制 
- 应用解耦 
- 流量控制 

把对应的消息过多会存到队列中,之后依次发送到对应服务
大多数应用中,可通过消息服务中间件来提升系统异步通信,扩展解耦能力(解耦的相当大的优势在于我之后某个子服务更新换代后可以不用再改原代码,而可以直接通过消息队列来更改)
消息队列的重要概念
消息代理
即是消息队列的服务器,帮你代理一下
目的地
消息发送的终点或接收的位置(有别于接收方/消费者,不是接收方啊!是消息的暂存对象)
主要两种形式的目的地(一对一&一对多)
- 队列(点对点的通信模式):每个消息只由一个消费者读取
- 主题(topic):多个消费者可以订阅同一个主题,并且每个消费者都可以接收该主题上发送的消息
springboot的使用上
- @EnableRabbit开启支持
- 自动会导入RabbitAutoConfiguration
RabbitMq的核心概念
- message: 
 就是我们发送的消息(要传给消费者的东西),包括消息头(类似http的消息头,放一些约定的东西:如routing-key(路由键),priority,delivery-mode(指出该消息可能需要持久性存储)),和消息体(放具体要传送的内容)
- Publisher: - 消息的生产者,向交换器(指定发送哪个)发布消息的客户端程序 
- Exchange: - 交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列 
 4类:direct(默认),fanout,topic,和headers,不同exchange转发消息策略有别
- Queue - 保存消息直到发送给消费者,消息的容器,也是消息的终点 
- Binding - 绑定,用于消息队列和交换器之间的关联,一个绑定就是基于路由键将路由器和消息队列连接起来的路由规则,Exchange和Queue的绑定可以是多对多的关系 
- Connection - 网络连接,如TCP 
- Channel - 信道,AMQP命令都是通过信道发出去的,不管发布消息,订阅队列还是接收消息都是通过信道完成(对于操作系统来说建立和销毁TCP都是非常昂贵的开销,所以出了信道的概念,来复用一条TCP连接) 
- Consumer(从消息队列中获取信息的客户端) 
- Virtual Host: 
 虚拟主机(也就是一台消息队列服务器分出一个虚拟消息队列,两者互不干扰)
- Broker:(消息队列服务器实体) 
核心图

Binding是交换机与队列的粘黏剂
疑问?消息队列如何知道是哪个Consumer需要消息呢?
Consumer需要去订阅topic或者queue,类似订牛奶,之后有牛奶就给你家送
exchange的类型
- headers(性能方面过于差劲,基本不用)
- direct(精准匹配:消息的routekey必须和交换机所绑定的route精准匹配才会发送(无通配符))
- fanout(无匹配机制,发送到所有订阅的消费者)
- topic(有通配符:可以通过通配符来进行匹配)
- route的组成形式是:单词.单词.单词
- 其中.分隔的是单词,同时通配符必须整个代替单词,不存在以下情况:aaa#.444
- 通配符:(#:0个或多个字母)(*:一个字母)
 
- route的组成形式是:
springboot快速导入
- pom中添加spring-boot-starter-amqp
- 查看RabbitAutoConfiguration,看一下其中的RabbitProperties了解到我们需要使用什么前缀,同时需要配置哪些信息
- 还可以观察AutoConfiguration里面为我们添加了哪些类
- 添加注解@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()));
    }
注意事项
- 可以直接传入任何java的对象
- 会默认使用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();
    }
}
可靠投递
在各个阶段都要保证可靠
共有三个阶段
- Producer-->broker
- exchange-->queue
- queue-->consumer

解决confirmCallback
- 消息只要被 broker 接收到就会执行 confirmCallback,如果是 cluster 模式,需要所有
broker 接收到才会调用 confirmCallback。
- 被 broker 接收到只能表示 message 已经到达服务器,并不能保证消息一定会被投递
到目标 queue 里
两步:
- 改配置文件
# 开启发送端确认
spring.rabbitmq.publisher-confirm-type=correlated
- 改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
两步:
- 改配置文件
其实只要第一行即可,第二行不是必须的,但提高性能
#开启发送端消息抵达队列的确认
spring.rabbitmq.publisher-returns=true
#只要抵达队列,以异步返送优先回调我们这个returnConfirm
spring.rabbitmq.template.mandatory=true
- 修改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
两步:
- 改配置
# 默认是自动签收,即只要发送到管道中即直接签收,要改成如下所示
spring.rabbitmq.listener.simple.acknowledge-mode=manual
- 在对应的接收信息的方法里修改逻辑
- 需要额外多两参数:(message,channel)
- 分别作为获取deliveryTag(代码中有解释)
- 传递该消息是否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版的更多相关文章
- C#RabbitMQ基础学习笔记
		RabbitMQ基础学习笔记(C#代码示例) 一.定义: MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开发).MQ是一种应用程序对应用程序的通信方法. ... 
- rabbitmq基础学习+springboot结合rabbitmq实现回调确认confirm
		rabbitmq集群docker快速搭建 https://blog.csdn.net/u011058700/article/details/78708767 rabbitmq原理博客 https:// ... 
- Mysql基础学习_Windows版(一)
		1.Mysql简介 Mysql是一种关系数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性.所谓的关系型数据库,是建立在关系模型基础上的 ... 
- RabbitMQ基础学习笔记(C#代码示例)
		一.定义: MQ是MessageQueue,消息队列的简称(是流行的开源消息队列系统,利用erlang语言开发).MQ是一种应用程序对应用程序的通信方法.应用程序通过读写入队和出队的消息来通信,无需专 ... 
- 狗屁不通的“视频专辑:零基础学习C语言(小甲鱼版)”(2)
		前文链接:狗屁不通的“视频专辑:零基础学习C语言(小甲鱼版)”(1) 小甲鱼在很多情况下是跟着谭浩强鹦鹉学舌,所以谭浩强书中的很多错误他又重复了一次.这样,加上他自己的错误,错谬之处难以胜数. 由于拙 ... 
- 拒绝从入门到放弃_《鸟哥的 Linux 私房菜 — 基础学习篇(第三版)》必读目录
		目录 目录 前言 关于这本书 必看知识点 最后 前言 相信部分刚进入这个行业的新同学会对一个问题感到疑惑,为什么从培训学校出来的学员不被欢迎? 这里记录下一些我个人的看法(博主也曾有面试新员工的经历) ... 
- SpringCloudStream学习(一)RabbitMQ基础
		应公司大佬要求,学习一下SpringCloudStream,作为技术储备.这几天也看了这方面的资料,现在写一篇笔记,以做总结.文章会从RabbitMQ基础讲起,到SpringCloudStream结束 ... 
- RabbitMq基础教程之基本概念
		RabbitMq基础教程之基本概念 RabbitMQ是一个消息队列,和Kafka以及阿里的ActiveMQ从属性来讲,干的都是一回事.消息队列的主要目的实现消息的生产者和消费者之间的解耦,支持多应用之 ... 
- 零基础学习openstack【完整中级篇】及openstack资源汇总
		1.你是如何学习openstack的?2.你对openstack的组件了解多少?3.你认为openstack该如何学习? 一直想写关于openstack的方面的内容,今天终于整理完成.算是完成一桩心事 ... 
- RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙
		消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以 ... 
随机推荐
- Spring Boot 2.x :日志框架@Slf4j的使用和logback文件配置
			为什么是SLF4J? 默认情况下,Spring Boot会用SLF4J + Logback来记录日志,并用INFO级别输出到控制台. 怎么使用SLF4J? 如果我们在一个Spring Boot 的程序 ... 
- 换架 3D 飞机,继续飞呀飞
			相信大多数图扑 HT 用户都曾见过这个飞机的 Demo,在图扑发展的这十年,这个 Demo 是许多学习 HT 用户一定会参考的经典 Demo 之一. 这个 Demo 用简洁的代码生动地展示了 OBJ ... 
- 2D 可视赋能智慧水务绿色集约化发展
			前言 随着国家对环境保护治理程度的日益重视,各地政府积极响应国家政策,在共同聚焦生态文明建设下,急速催生了水务行业数字化转型.如今 "供排污"一体化管理系统成为行业发展的重要趋势, ... 
- 【驱动】ifconfig up后内核网络驱动做了什么.md
			背景 最近在排查一个网络问题,ifconfig eth0 up 后,网卡link up比较慢.因此,分析了下从ifconfig up 到网络驱动的调用流程.这里顺便作个记录. ifconfig eth ... 
- mysql too many connections 解决
			本文为博主原创,转载请注明出处: 由于在开发过程中,很多人连接共同一个数据库,在工具连接到mysql, 并执行sql时,提示 too many connections ,这是由于数据库连接太多,以致于 ... 
- LeetCode-Go:一个使用 Go 语言题解 LeetCode 的开源项目
			在中国的 IT 环境里,大多数场景下,学习算法的目的在于通过笔试算法题. 但算法书林林总总,有时候乱花渐欲迷人眼. 杜甫有诗云:读书破万卷,下笔如有神.不管选择哪本书,只要深入学习,分层次,逐层进阶, ... 
- 基于jquery开发的Windows 12网页版
			预览 https://win12.gitapp.cn 首页代码 <!DOCTYPE html> <html lang="en"> <head> ... 
- 【BAT】递归替换文件后缀
			@echo off set /p src_suffix=please input origin suffix: set /p des_suffix=please input target suffix ... 
- 浏览器兼容   :  IE 5 到 IE 9
			<!--[if IE]> <link href="ie.css" rel="stylesheet"> <![endif]--> ... 
- [转帖]深入浅出分析LSM树(日志结构合并树)
			https://zhuanlan.zhihu.com/p/415799237  目录 收起 零.前言 一.LSM树数据结构定义 二.插入操作 三.删除操作 四.修改操作 五.查询操作 六.合并操作 ... 
