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项目使用消息 ...
随机推荐
- Leetcode之分治法专题-169. 求众数(Majority Element)
Leetcode之分治法专题-169. 求众数(Majority Element) 给定一个大小为 n 的数组,找到其中的众数.众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是 ...
- SpringMVC中的generator
引言 今天在做一个原生的spring项目的时候碰到一个非常好用的代码自动生成器,叫做generator,主要是运用于mybatis中的代码生成,它可以生成mapper的映射xml,model中的实体类 ...
- 导航控制器的根控制器 是滚动性&普通的frame区别
当一个控制器有navigationBar&tabBar: 1.当导航控制器根控制器是tableViewController时,tableView 的frame原点是屏幕左上角,当向tableV ...
- 爬虫——cookie模拟登陆
cookie适用于抓取需要登录才能访问的页面网站 cookie和session机制 http协议为无连接协议,cookie: 存放在客户端浏览器,session: 存放在Web服务器 人人网登录案例 ...
- CodeForces 1018B The hat
The hat 题解: 定义d[i]为第i个数和他对面的差值. 然后我们可以发现d[i]和d[i+1]的差值只会有3种情况2, -2, 0. 并且可以知道 d[i] = - d[i+n/2] 所以如果 ...
- codeforces 807 C. Success Rate(二分)
题目链接:http://codeforces.com/contest/807/problem/C 题意:记 AC 率为当前 AC 提交的数量 x / 总提交量 y .已知最喜欢的 AC 率为 p/q ...
- JS-DOM ~ 01. 了解DOM,动手做一下就明白了!/鼠标事件(好吧 其实我卡了三天
DOM概述 html加载完毕,渲染引擎会在内存中把html文档生成一个DOM树,getElementById是获取内DOM上的元素,然后操作的时候修改的是该元素的属性 体验事件/事件三要素1.事件源( ...
- Linux OOM-killer(内存不足时kill高内存进程的策略)
OOM_killer是Linux自我保护的方式,当内存不足时不至于出现太严重问题,有点壮士断腕的意味 在kernel 2.6,内存不足将唤醒oom_killer,挑出/proc/<pid> ...
- JSP官方文档(JavaServer Pages Specification)下载
找了好久,终于找到官网的下载地址了.这样,就可以用一手的文档资料了! 下载地址:http://download.oracle.com/otndocs/jcp/jsp-2_3-mrel2-spec/ s ...
- django学之路01--环境安装和pycharm运行django项目
1. 环境安装 1).virtualenv安装 C:\Users\Administrator>pip install virtualenv Collecting virtualenv Using ...