Springboot2.x整合RabbitMQ
1、RabbitMQ介绍
2、接入配置
pom依赖
<!--amqp依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置文件
server.port=8080 spring.application.name=springboot-rabbitmq
spring.rabbitmq.host=192.168.242.131
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
# 开启发送确认
spring.rabbitmq.publisher-confirms=true
# 开启发送失败退回
spring.rabbitmq.publisher-returns=true
# 开启ACK
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.acknowledge-mode=manual
3、一对一模式
即一个生产者对一个消费者模式
配置类
@Configuration
public class RabbitMqConfig { @Bean
public Queue kinsonQueue() {
return new Queue("kinson");
} }
消费者
@Component
//监听队列kinson
@RabbitListener(queues = {"kinson"})
public class MyReceiver1 { @RabbitHandler
public void receiver(String msg) {
System.out.println("MyReceiver1 :" + msg);
}
}
消息生产者测试接口
/**
* 单条消息发送给单个队列,该队列只有一个消费者
*
* @return
*/
@GetMapping(value = "send")
public String send() {
String content = "Date:" + System.currentTimeMillis();
//发送默认交换机对应的的队列kinson
amqpTemplate.convertAndSend("kinson", content);
return content;
}
4、一对多模式
即一个生产者对多个消费者,该模式下可以是一个生产者将消息投递到一个队列,该队列对应多个消费者,此时每条消息只会被消费一次,多个消费者循环处理。另外也可以是一个生产者将消息投递到多个队列里,此时消息是被复制处理。
模式一:
配置类
@Configuration
public class RabbitMqConfig { @Bean
public Queue kinsonQueue() {
return new Queue("kinson");
} }
消费者1
@Component
//监听队列kinson
@RabbitListener(queues = {"kinson"})
public class MyReceiver1 { @RabbitHandler
public void receiver(String msg) {
System.out.println("MyReceiver1 :" + msg);
}
}
消费者2
@Component
//监听队列kinson
@RabbitListener(queues = {"kinson"})
public class MyReceiver2 { @RabbitHandler
public void receiver(String msg) {
System.out.println("MyReceiver2 :" + msg);
}
}
消息生产者测试接口
/**
* 发送多条消息到一个队列,该队列有多个消费者
*
* @return
*/
@GetMapping(value = "sendMore")
public String sendMore() {
List<String> result = new ArrayList<String>();
//发送10条数据
for (int i = 0; i < 10; i++) {
String content = "第" + (i + 1) + "次发送 Date:" + System.currentTimeMillis();
//发送默认交换机对应的的队列kinson,此时有两个消费者MyReceiver1和MyReceiver2,每条消息只会被消费一次
amqpTemplate.convertAndSend("kinson", content);
result.add(content);
}
return String.join("<br/>", result);
}
模式二:
配置类
@Configuration
public class RabbitMqConfig { @Bean
public Queue kinsonQueue() {
return new Queue("kinson");
} @Bean
public Queue kinsonQueue2() {
return new Queue("kinson2");
}
}
kinson队列消费者
@Component
//监听队列kinson
@RabbitListener(queues = {"kinson"})
public class MyReceiver1 { @RabbitHandler
public void receiver(String msg) {
System.out.println("MyReceiver1 :" + msg);
}
}
kinson2队列消费者
@Component
//监听队列kinson2
@RabbitListener(queues = {"kinson2"})
public class MyReceiver3 { @RabbitHandler
public void receiver(String msg) {
System.out.println("MyReceiver3 :" + msg);
}
}
消息生产者测试接口
/**
* 发送多条消息到多个队列
*
* @return
*/
@GetMapping(value = "sendMoreQueue")
public String sendMoreQueue() {
List<String> result = new ArrayList<String>();
//发送10条数据
for (int i = 0; i < 10; i++) {
String content = "第" + (i + 1) + "次发送 Date:" + System.currentTimeMillis();
//发送默认交换机对应的的队列kinson
amqpTemplate.convertAndSend("kinson", content);
//发送默认交换机对应的的队列kinson2
amqpTemplate.convertAndSend("kinson2", content);
result.add(content);
}
return String.join("<br/>", result);
}
相应测试结果请自测
5、ACK消息确认
配置文件加入相应配置
# 开启发送确认
spring.rabbitmq.publisher-confirms=true
# 开启发送失败退回
spring.rabbitmq.publisher-returns=true
# 开启ACK
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.simple.acknowledge-mode=manual
配置类,使用Fanout类型的Exchange,主要是设置队列,交换机及绑定
@Configuration
public class RabbitMqFanoutACKConfig { @Bean
public Queue ackQueue() {
return new Queue("ackQueue");
} @Bean
FanoutExchange fanoutExchange() {
return new FanoutExchange("fanoutExchange");
} @Bean
Binding bindingAckQueue2Exchange(Queue ackQueue, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(ackQueue).to(fanoutExchange);
} }
消息发送服务
@Service
public class AckSenderService implements RabbitTemplate.ReturnCallback { @Autowired
private RabbitTemplate rabbitTemplate; @Override
public void returnedMessage(Message message, int i, String s, String s1, String s2) {
System.out.println("AckSender returnedMessage " + message.toString() + " === " + i + " === " + s1 + " === " + s2);
} /**
* 消息发送
*/
public void send() {
final String content = "现在时间是" + LocalDateTime.now(ZoneId.systemDefault()); //设置返回回调
rabbitTemplate.setReturnCallback(this);
//设置确认回调
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
System.out.println("消息发送成功!");
}
else {
System.out.println("消息发送失败," + cause + correlationData.toString());
}
});
rabbitTemplate.convertAndSend("ackQueue", content);
}
}
消息消费者
@Component
@RabbitListener(queues = {"ackQueue"})
public class MyAckReceiver { @RabbitHandler
public void process(String sendMsg, Channel channel, Message message) { System.out.println("AckReceiver : 收到发送消息 " + sendMsg + ",收到消息时间"
+ LocalDateTime.now(ZoneId.systemDefault())); try {
//告诉服务器收到这条消息已经被当前消费者消费了,可以在队列安全删除,这样后面就不会再重发了,
//否则消息服务器以为这条消息没处理掉,后续还会再发
//第二个参数是消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("process success");
} catch (Exception e) {
System.out.println("process fail");
e.printStackTrace();
} }
}
测试访问接口
/**
* @return
*/
@GetMapping(value = "ackSend")
public String ackSend() {
senderService.send(); return "ok";
}
测试将Consumer确认代码注释掉,即
@Component
@RabbitListener(queues = {"ackQueue"})
public class MyAckReceiver { @RabbitHandler
public void process(String sendMsg, Channel channel, Message message) { System.out.println("AckReceiver : 收到发送消息 " + sendMsg + ",收到消息时间"
+ LocalDateTime.now(ZoneId.systemDefault())); try {
//告诉服务器收到这条消息已经被当前消费者消费了,可以在队列安全删除,这样后面就不会再重发了,
//否则消息服务器以为这条消息没处理掉,后续还会再发
//第二个参数是消息的标识,false只确认当前一个消息收到,true确认所有consumer获得的消息
//channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("process success");
} catch (Exception e) {
System.out.println("process fail");
e.printStackTrace();
} }
}
此时访问测试接口,可以看到当消息发送完被消费掉之后,队列的状态变为unacked。

当停掉服务时,unacked状态变为Ready

再重新启动服务时会重新发送消息

6、事务机制
事务的实现主要是对信道(Channel)的设置,主要的方法有三个:
//声明启动事务模式
channel.txSelect();
//提交事务
channel.txComment();
//回滚事务
channel.txRollback();
消息发送示例
public void publish()
throws KeyManagementException, NoSuchAlgorithmException, URISyntaxException, IOException, TimeoutException {
// 创建连接
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername(username);
factory.setPassword(password);
factory.setVirtualHost("/");
factory.setHost(host);
factory.setPort(port);
Connection conn = factory.newConnection();
// 创建信道
Channel channel = conn.createChannel();
// 声明队列
channel.queueDeclare(TX_QUEUE, true, false, false, null); try { long startTime = System.currentTimeMillis(); for (int i = 0; i < 10; i++) {
// 声明事务
channel.txSelect();
String message = String.format("时间 => %s", System.currentTimeMillis());
// 发送消息
channel.basicPublish("", TX_QUEUE, MessageProperties.PERSISTENT_TEXT_PLAIN,
message.getBytes("UTF-8"));
// 提交事务
channel.txCommit();
} long endTime = System.currentTimeMillis(); System.out.println("事务模式,发送10条数据,执行花费时间:" + (endTime - startTime) + "s"); } catch (Exception e) {
channel.txRollback();
} finally {
channel.close();
conn.close();
}
}
消息消费示例
public void consume() throws IOException, TimeoutException, InterruptedException {
Connection conn = RabbitMqConnFactoryUtil.getRabbitConn();
Channel channel = conn.createChannel();
channel.queueDeclare(TX_QUEUE, true, false, false, null);
// 声明事务
channel.txSelect();
try {
//单条消息获取进行消费
GetResponse resp = channel.basicGet(TX_QUEUE, false);
String message = new String(resp.getBody(), "UTF-8");
System.out.println("收到消息:" + message);
//消息拒绝
// channel.basicReject(resp.getEnvelope().getDeliveryTag(), true);
// 消息确认
channel.basicAck(resp.getEnvelope().getDeliveryTag(), false);
// 提交事务
channel.txCommit();
} catch (Exception e) {
// 回滚事务
channel.txRollback();
} finally {
//关闭通道、连接
channel.close();
conn.close();
}
}
7、Confirm消息确认
Confirm发送方确认模式使用和事务类似,也是通过设置Channel进行发送方确认的,Confirm的三种实现方式:
//方式一:普通发送方确认模式
channel.waitForConfirms();
//方式二:批量确认模式
channel.waitForConfirmsOrDie();
//方式三:异步监听发送方确认模式
channel.addConfirmListener();
消息发布示例
public void publish() throws IOException, TimeoutException, InterruptedException {
// 创建连接
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername(username);
factory.setPassword(password);
factory.setVirtualHost("/");
factory.setHost(host);
factory.setPort(port);
Connection conn = factory.newConnection();
// 创建信道
Channel channel = conn.createChannel();
// 声明队列
channel.queueDeclare(CONFIRM_QUEUE, false, false, false, null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
// 开启发送方确认模式
channel.confirmSelect();
String message = String.format("时间 => %s", System.currentTimeMillis());
channel.basicPublish("", CONFIRM_QUEUE, null, message.getBytes("UTF-8"));
}
//添加确认监听器
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("未确认消息,标识:" + deliveryTag);
}
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println(String.format("已确认消息,标识:%d,多个消息:%b", deliveryTag, multiple));
}
});
long endTime = System.currentTimeMillis();
System.out.println("执行花费时间:" + (endTime - startTime) + "s");
}
RabbitMQ简单示例源码参照Github
Springboot2.x整合RabbitMQ的更多相关文章
- SpringBoot2.0应用(三):SpringBoot2.0整合RabbitMQ
如何整合RabbitMQ 1.添加spring-boot-starter-amqp <dependency> <groupId>org.springframework.boot ...
- SpringBoot2.0源码分析(三):整合RabbitMQ分析
SpringBoot具体整合rabbitMQ可参考:SpringBoot2.0应用(三):SpringBoot2.0整合RabbitMQ RabbitMQ自动注入 当项目中存在org.springfr ...
- 【SpringBoot】息队列介绍和SpringBoot2.x整合RockketMQ、ActiveMQ
========================13.消息队列介绍和SpringBoot2.x整合RockketMQ.ActiveMQ ======================= 1.JMS介绍和 ...
- springboot 学习之路 20 (整合RabbitMQ)
整合RabbitMQ: 我的操作系统是window7 ,所以在整合ribbotMQ之前需要先安装rabbitMq服务:安装步骤请参考:window下安装RabbitMQ 这个详细介绍了安装步骤,请按 ...
- 消息队列介绍和SpringBoot2.x整合RockketMQ、ActiveMQ 9节课
1.JMS介绍和使用场景及基础编程模型 简介:讲解什么是小写队列,JMS的基础知识和使用场景 1.什么是JMS: Java消息服务(Java Message Service),Java ...
- spring boot实战(第十二篇)整合RabbitMQ
前言 最近几篇文章将围绕消息中间件RabbitMQ展开,对于RabbitMQ基本概念这里不阐述,主要讲解RabbitMQ的基本用法.Java客户端API介绍.spring Boot与RabbitMQ整 ...
- 【RabbitMQ系列】 Spring mvc整合RabbitMQ
一.linux下安装rabbitmq 1.安装erlang环境 wget http://erlang.org/download/otp_src_18.2.1.tar.gz tar xvfz otp_s ...
- Spring Boot 整合 rabbitmq
一.消息中间件的应用场景 异步处理 场景:用户注册,信息写入数据库后,需要给用户发送注册成功的邮件,再发送注册成功的邮件. 1.同步调用:注册成功后,顺序执行发送邮件方法,发送短信方法,最后响应用户 ...
- 每天学点SpringCloud(十三):SpringCloud-Stream整合RabbitMQ
我们知道,当微服务越来越来多的时候,仅仅是feign的http调用方式已经满足不了我们的使用场景了.这个时候系统就需要接入消息中间件了.相比较于传统的Spring项目.SpringBoot项目使用消息 ...
随机推荐
- Spring Boot 2.0 教程 | 快速集成整合消息中间件 Kafka
欢迎关注个人微信公众号: 小哈学Java, 每日推送 Java 领域干货文章,关注即免费无套路附送 100G 海量学习.面试资源哟!! 个人网站: https://www.exception.site ...
- Java-手动搭建SSM(Maven)
一.环境部署 操作系统:windows10专业版 jdk:1.8.0_144 IDE:eclipse-oxygen 服务器:tomcat 9.0 数据库:mysql 5.7.18 Maven:3.54 ...
- 2018CCPC 吉林现场赛 赛后总结
一直以来都没有比赛完写总结的习惯,导致前面几次比赛都没有写过总结. 这是我写的第一场总结把,有时间有想法还记得细节的话再把前面几次比赛的总结给补上把. 热身赛: 热身赛的时候,写的比较急想着快点做出题 ...
- CF - 1107 E Vasya and Binary String DP
题目传送门 题解: dp[ l ][ r ][ k ] 代表的是[l, r]这段区间内, 前面有k-1个连续的和s[l]相同且连续的字符传进来的最大值. solve( l, r, k) 代表的是处理 ...
- JOBDU 1193 矩阵转置
啊!!!这道题目今天竟然写错了!!!这道题目巨坑,说不能用数组,结果竟然是用数组做的,吐血!!! 看的所有有关博文,都是用数组做的,晕倒!真的出题人有毛病,出这种题,又不限制运行!!! 以后再遇到这种 ...
- 2017ACM总结
首先对自己表示强烈的谴责, 这个时间应该是打cf的时间, 没有想到我又秀到了自己, 放弃了CF, 23333, 菜鸡wxk. 高中刚毕业的时候,听大学生们跪求老师不要给挂科,当时想的是,哇上了大学就不 ...
- django中使用事务以及接入支付宝支付功能
之前一直想记录一下在项目中使用到的事务以及支付宝支付功能,自己一直犯懒没有完,趁今天有点兴致,在这记录一下. 商城项目必备的就是支付订单的功能,所以就会涉及到订单的保存以及支付接口的引入.先来看看订单 ...
- 【Nginx】实现负载均衡的几种方式
要理解负载均衡,必须先搞清楚正向代理和反向代理. 注: 正向代理,代理的是用户. 反向代理,代理的是服务器 什么是负载均衡 当一台服务器的单位时间内的访问量越大时,服务器压力就越大,大到超过自身承受能 ...
- tomcat,nginx日志定时清理
1. Crontab定时任务 Crontab 基本语法 t1 t2 t3 t4 t5 program 其中 t1 是表示分钟,t2 表示小时,t3 表示一个月份中的第几日,t4 表示月份,t5 表示一 ...
- jsp页面直接输出了html代码
可能出现的情况: 1.修改web.xml中springMVC的过滤器路径如下: "/"与"/*区别" 其实/和/*都可以匹配所有的请求资源,但其匹配的优先顺序是 ...