SpringBoot使用消息队列RabbitMQ
RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲、消息分发的作用。RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,AMQP,即Advanced Message Queuing Protocol, 高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。在项目中,将一些无需即时返回且耗时的操作提取出来,进行了异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间, 提高了系统的吞吐量。
简单概念
几个名词术语:
Broker - 简单来说就是消息队列服务器的实体。
Exchange - 消息路由器,转发消息到绑定的队列上,指定消息按什么规则,路由到哪个队列。
Queue - 消息队列,用来存储消息,每个消息都会被投入到一个或多个队列。
Binding - 绑定,它的作用就是把 Exchange 和 Queue 按照路由规则绑定起来。
RoutingKey - 路由关键字,Exchange 根据这个关键字进行消息投递。
Producter - 消息生产者,产生消息的程序。
Consumer - 消息消费者,接收消息的程序。
Channel - 消息通道,在客户端的每个连接里可建立多个Channel,每个channel代表一个会话。
一般消息队列都是生产者(Producter)将消息发送到队列(Queue),消费者(Consumer)监听队列进行消费。rabbitmq中一个虚拟主机(默认 /)持有一个或者多个交换机(Exchange)。 用户只能在虚拟主机的粒度进行权限控制,交换机根据一定的策略(RoutingKey)绑定(Binding)到队列(Queue)上, 这样生产者和队列就没有直接联系,而是将消息发送的交换机,交换机再把消息转发到对应绑定的队列上。
交换机(Exchange)作为RabbitMQ的一个重要概念,最常用的有四种类型:
Direct: 先匹配, 再投送。即在绑定时设定一个routing_key, 消息的routing_key匹配时, 才会被交换器投送到绑定的队列中去. 交换机跟队列必须是精确的对应关 系,这种最为简单。
Topic: 转发消息主要是根据通配符。在这种交换机下,队列和交换机的绑定会定义一种路由模式,那么,通配符就要在这种路由模式和路由键之间匹配后交换 机才能转发消息,这种可以认为是Direct 的灵活版
Headers: 也是根据规则匹配, 相较于 direct 和 topic 固定地使用 routingkey , headers则是一个自定义匹配规则的类型, 在队列与交换器绑定时会设定一组键值 对规则,消息中也包括一组键值对( headers属性),当这些键值对有一对或全部匹配时,消息被投送到对应队列。
Fanout : 消息广播模式,不管路由键或者是路由模式,会把消息发给绑定给它的全部队列,如果配置了routingkey会被忽略
Ack机制
在RabbitMQ的消息队列编程中,有个非常重要的概念叫消息确认机制,也就是Ack机制。每个Consumer可能需要一段时间才能处理完收到的数据。如果在这个过程中,Consumer出错了或者异常退出了,而数据还没有处理完成,那么非常不幸,这段数据就丢失了。因为我们采用no-ack的方式进行确认,也就是说,每次Consumer接到数据后,而不管是否处理完成,RabbitMQ Server会立即把这个Message标记为完成,然后从queue中删除了。 如果一个Consumer异常退出了,它处理的数据能够被另外的Consumer处理,这样数据在这种情况下就不会丢失了。
为了保证数据不被丢失,RabbitMQ支持消息确认机制,即acknowledgments。为了保证数据能被正确处理而不仅仅是被Consumer收到,那么我们不能采用no-ack。而应该是在处理完数据后发送ack。 在处理数据后发送的ack,就是告诉RabbitMQ数据已经被接收,处理完成,RabbitMQ可以去安全的删除它了。
如果Consumer退出了但是没有发送ack,那么RabbitMQ就会把这个Message发送到下一个Consumer。这样就保证了在Consumer异常退出的情况下数据也不会丢失。
这里并没有用到超时机制。RabbitMQ仅仅通过Consumer的连接中断来确认该Message并没有被正确处理。也就是说,RabbitMQ给了Consumer足够长的时间来做数据处理。 这样即使你通过Ctr-C中断了Recieve.cs,那么Message也不会丢失了,它会被分发到下一个Consumer。如果忘记了ack,那么后果很严重。当Consumer退出时,Message会重新分发。然后RabbitMQ会占用越来越多的内存,由于RabbitMQ会长时间运行,因此这个“内存泄漏”是致命的。
下面的例子使用手动Ack模式
1.环境准备:
RabbitMQ需要安装erlang和RabbitMQ Server
erlang是一种通用的面向并发的编程语言 ,RabbitMQ则是由其编写,下载地址:http://www.erlang.org/downloads
RabbitMQ Server 下载地址 :http://www.rabbitmq.com/install-windows.html
安装完成后,会发现:

此时,RabbitMQ并未启动,可通过手动点击 rabbitmq_server-3.7.9\sbin 目录下的 rabbitmq-plugins.bat 来启动 ;
也可以通过命令行来添加可视化启动界面:


创建用户名密码和分配权限:
①查看用户

②添加用户名和密码
       
  
③添加角色和权限

最后 ,访问http://localhost:15672/ ,输入用户密码

2.SpringBoot中集成
maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<exclusions>
<exclusion>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
配置文件
##########################################################
################## 所有profile共有的配置 #################
########################################################## ################### spring配置 ###################
spring:
profiles:
active: dev --- #####################################################################
######################## 开发环境profile ##########################
#####################################################################
spring:
profiles: dev
rabbitmq:
host: 127.0.0.1
port: 5672
username: spring
password: 123456
publisher-confirms: true #支持发布确认
publisher-returns: true #支持发布返回
listener:
simple:
acknowledge-mode: manual #采用手动应答
concurrency: 1 #指定最小的消费者数量
max-concurrency: 1 #指定最大的消费者数量
retry:
enabled: true #是否支持重试 logging:
level:
ROOT: INFO
com:
xncoding: DEBUG
file: D:/logs/app.log #日志路径
配置类
最核心的类就是RabbitMQ的配置类,在里面定制模版类、声明交换机、队列、绑定交换机到队列。这里声明了一个Direct类型的交换机,并通过路由键绑定到一个队列中来测试Direct模式, 另外还声明了Fanout类型的交换机,并绑定到2个队列来测试广播模式;
监听器
编写消息队列的监听器类,监听队列消息并做相应的处理,并通过Ack机制确认处理完成:
@Component
public class Receiver {
private static final Logger log = LoggerFactory.getLogger(Receiver.class); /**
* FANOUT广播队列监听一.
*
* @param message the message
* @param channel the channel
* @throws IOException the io exception 这里异常需要处理
*/
@RabbitListener(queues = {"FANOUT_QUEUE_A"})
public void on(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
log.debug("FANOUT_QUEUE_A " + new String(message.getBody()));
} /**
* FANOUT广播队列监听二.
*
* @param message the message
* @param channel the channel
* @throws IOException the io exception 这里异常需要处理
*/
@RabbitListener(queues = {"FANOUT_QUEUE_B"})
public void t(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
log.debug("FANOUT_QUEUE_B " + new String(message.getBody()));
} /**
* DIRECT模式.
*
* @param message the message
* @param channel the channel
* @throws IOException the io exception 这里异常需要处理
*/
@RabbitListener(queues = {"DIRECT_QUEUE"})
public void message(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
log.debug("DIRECT " + new String(message.getBody()));
}
}
消息发送者
再写一个消息发送服务,用来向交换机发送消息:
/**
* 消息发送服务
*/
@Service
public class SenderService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private RabbitTemplate rabbitTemplate; /**
* 测试广播模式.
*
* @param p the p
* @return the response entity
*/
public void broadcast(String p) {
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend("FANOUT_EXCHANGE", "", p, correlationData);
} /**
* 测试Direct模式.
*
* @param p the p
* @return the response entity
*/
public void direct(String p) {
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend("DIRECT_EXCHANGE", "DIRECT_ROUTING_KEY", p, correlationData);
} }
运行测试
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class SenderServiceTest {
@Autowired
private SenderService senderService; @Test
public void testCache() {
// 测试广播模式
senderService.broadcast("同学们集合啦!");
// 测试Direct模式
senderService.direct("定点消息"); }
}
测试结果


代码下载地址:https://pan.baidu.com/s/1fAvUM3XKIqvFAZwk7c1YvA
SpringBoot使用消息队列RabbitMQ的更多相关文章
- C#中使用消息队列RabbitMQ
		
在C#中使用消息队列RabbitMQ 2014-10-27 14:41 by qy1141, 745 阅读, 2 评论, 收藏, 编辑 1.什么是RabbitMQ.详见 http://www.rabb ...
 - node使用消息队列RabbitMQ一
		
基础发布和订阅 消息队列RabbitMQ使用 1 安装RabbitMQ服务器 安装erlang服务 下载地址 http://www.erlang.org/downloads 安装RabbitMQ 下载 ...
 - 消息队列--RabbitMQ(一)
		
1.消息队列概述 可以理解为保存消息的一个媒介/或者是个容器,与之相关有两个概念(即生产者(Publish)与消费者(Consumer)).所谓生产者,就是生产创造消息的一方,那么,消费者便是从队列中 ...
 - (二)RabbitMQ消息队列-RabbitMQ消息队列架构与基本概念
		
原文:(二)RabbitMQ消息队列-RabbitMQ消息队列架构与基本概念 没错我还是没有讲怎么安装和写一个HelloWord,不过快了,这一章我们先了解下RabbitMQ的基本概念. Rabbit ...
 - (一)RabbitMQ消息队列-RabbitMQ的优劣势及产生背景
		
原文:(一)RabbitMQ消息队列-RabbitMQ的优劣势及产生背景 本篇并没有直接讲到技术,例如没有先写个Helloword.我想在选择了解或者学习一门技术之前先要明白为什么要现在这个技术而不是 ...
 - ASP.NET Core消息队列RabbitMQ基础入门实战演练
		
一.课程介绍 人生苦短,我用.NET Core!消息队列RabbitMQ大家相比都不陌生,本次分享课程阿笨将给大家分享一下在一般项目中99%都会用到的消息队列MQ的一个实战业务运用场景.本次分享课程不 ...
 - 消息队列rabbitmq/kafka
		
12.1 rabbitMQ 1. 你了解的消息队列 rabbitmq是一个消息代理,它接收和转发消息,可以理解为是生活的邮局.你可以将邮件放在邮箱里,你可以确定有邮递员会发送邮件给收件人.概括:rab ...
 - nodejs操作消息队列RabbitMQ
		
一. 什么是消息队列 消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已.其主要用途:不同进程Process/ ...
 - 消息队列rabbitmq  rabbitMQ安装
		
消息队列rabbitmq 12.1 rabbitMQ 1. 你了解的消息队列 生活里的消息队列,如同邮局的邮箱, 如果没邮箱的话, 邮件必须找到邮件那个人,递给他,才玩完成,那这个任务会处理的很麻 ...
 
随机推荐
- Linux 下必备的性能检测工具 合集
			
有些工具,值得学习学习: 网络 iftop IO iotop 系统 top htop 保持更新,转载请注明出处. https://www.cnblogs.com/xuyaowen/p/linux- ...
 - 自动化测试之路2---python安装
			
借鉴这位老哥的文章http://www.cnblogs.com/shabbylee/p/6792555.html
 - 两种动态SQL
			
参考:http://www.cnblogs.com/wanyuan8/archive/2011/11/09/2243483.htmlhttp://www.cnblogs.com/xbf321/arch ...
 - GitHub下载克隆clone指定的分支tag代码
			
需求描述: 这边有很多tag分支版本的代码,我想克隆下载指定版本到我服务器上面 例如:我想下载tag:1.4.1的代码 解决方法: 命令:git clone --branch [tags标签] [gi ...
 - XML详解一XML语法
			
XML指可扩展标记语言很类似 HTML,被设计用来传输和存储数据而非显示数据,XML标签没有被预定义需要自行定义标签,标签具有自我描述性,同时XML也是 W3C 的推荐标准. 先来写一个XML脚本de ...
 - SQLServer之触发器简介
			
触发器定义 触发器是数据库服务器中发生事件时自动执行的一种特殊存储过程.SQLServer允许为任何特定语句创建多个触发器.它的执行不是由程序调用,也不是手工启动,而是由事件来触发,当对数据库进行操作 ...
 - 浅析String类
			
这是对于String类的一些总结,我将会从几个方面并且结合着字符串池等相关知识进行总结 进程如下: 1.对于String类基本知识的一些总结 2.简要介绍字符串池 3.分 ...
 - web框架开发-Django用户认证组件
			
可以用认证组件做什么 针对session的缺陷, 跟新数据时,不跟新key键, 用户认证组件是删除后再重建 用户认证组件很多功能可以直接使用 利用用户认证表(auth_user,通过Django自己创 ...
 - python之json模块
			
#!/usr/bin/python # -*- coding: UTF- -*- ''' )序列化相关 json pickle (序列化是指将) 序列化是指将一个字符串转换成基础数据类型或者基础数据类 ...
 - postman的使用大全
			
转载 https://blog.csdn.net/fxbin123/article/details/80428216