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项目使用消息 ...
随机推荐
- QFramework 使用指南 2020(三):脚本生成(1)基本使用
在上一篇,我们对 QFramework 的两个主要版本提供了介绍,并且写下了第一个 QFramework 脚本. 在这一篇,我们学习 QFramework 中几乎每个项目都要用到并且从中受益的功能:自 ...
- mac系统chrome浏览器快捷键
开发中谷歌浏览器常用快捷键: 开发者工具台快捷键:option+command+iconsole控制台快捷键:option+command+j 或者 option+command+c 1. 标签页和窗 ...
- JavaScript Array 数组方法汇总
JavaScript Array 数组方法汇总 1. arr.push() 从后面添加元素,返回值为添加完后的数组的长度 var arr = [1,2,3,4,5] console.log(arr.p ...
- 利用python定位网页上的元素
1. 想对网页上的元素进行操作,首先需要定位到元素. 以百度首页为例: 输入以下代码,打开百度首页: # coding = gbk from selenium import webdriver chr ...
- zstu19一月月赛 duxing201606的原味鸡树
duxing201606的原味鸡树 题意: 给定一颗有n(n<=1e9)个节点的完全二叉树,1e5次询问,问某个节点有几个子节点. 思路: 自己在月赛上没有思路,问了zfq才知道. 设两个指标, ...
- 2019年牛客多校第一场 B题 Integration 数学
题目链接 传送门 思路 首先我们对\(\int_{0}^{\infty}\frac{1}{\prod\limits_{i=1}^{n}(a_i^2+x^2)}dx\)进行裂项相消: \[ \begin ...
- Spreading the Wealth uva 11300
A Communist regime is trying to redistribute wealth in a village. They have have decided to sit ever ...
- php preg_match pcre回溯绕过
原理需要知识:正则NFA回溯原理,php的pcre.backtrack_limit设置. 正则NFA回溯原理正则表达式是一个可以被"有限状态自动机"接受的语言类."有限状 ...
- 通过Service访问应用 (1)
目录 通过Service访问应用 通过Pod IP访问应用 通过ClusterIP Service在集群内部访问 通过Service访问应用 通过之前的操作,应用部署完成了,我们的Demo网站已 ...
- css 实现居中的五种方式
一 .文字居中 1 固定容器高度的单行文本垂直居中:height=line-height 2 未知容器高度的文本垂直居中:设定padding,使上下的padding值相同即可 ...