springboot之rabbitmq
一、RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
二、目录结构
  
三、是使用springboot搭建rabbitmq我们需要基本的依赖包
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
四、这里我们主要介绍6中模式的配置和使用
1)默认的模式(这种方式不是没有exchange,而是使用默认的exchange。默认为Direct)
  
            
声明方式:
/**
* 第一种:使用默认的交换机(direct模式)
*/
@Configuration
public class QueueConfiguration { /**
* 声明队列:队列有五个参数(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
* name:队列名称
* durable:持久性
* exclusive:排他性(独立性)
* autoDelete:自动删除
* arguments:其他相关参数
* @return
*/
@Bean
public Queue queue() {
return new Queue("queue", false);
}
}
(1)简单:只有一个listener在监听queue,这样消息只能传到这个队列
(2)进阶:如果存在多个listener监听这个queue,rabbitmq会优雅的平均分配给listener
(3)arguments(参数配置)
x-message-ttl(Time-To-Live):消息存活时间,单位毫秒
x-expires:队列没有访问超时时,自动删除(包含没有消费的消息),单位毫秒。
x-max-length:限制队列最大长度(新增后挤出最早的),单位个数。
x-max-length-bytes :限制队列最大容量
x-dead-letter-exchange:死信交换机,将删除/过期的数据,放入指定交换机。
x-dead-letter-routing-key:死信路由,将删除/过期的数据,放入指定routingKey
x-max-priority:队列优先级。
x-queue-mode:对列模式,默认lazy(将数据放入磁盘,消费时放入内存)。
x-queue-master-locator:镜像队列
2)主题模式/通配符模式(topicExchange)
  
声明方式:
/**
* 第二种:topic交换机模式(主题模式)
*/
@Configuration
public class TopicExchangeConfiguration { @Bean
public Queue queue1() {
return new Queue("queue1", false);
} @Bean
public Queue queue2() {
return new Queue("queue2", false);
} /**
* 声明交换机类型:存在4个参数(String name, boolean durable, boolean autoDelete, Map<String, Object> arguments)
* 这里的参数基本和queue一样的理解
* @return
*/
@Bean
public TopicExchange topicExchange() {
return new TopicExchange("topic", false, false);
} /**
* 绑定队列到交换机上面
* @return
*/
@Bean
public Binding binding1() {
return BindingBuilder.bind(queue1()).to(topicExchange()).with("*.topic");
} /**
* 这里存在两种匹配符
* *:代表一个单位的字符(1.topic)
* #:代表多个单位的字符(2.2.topic)
* @return
*/
@Bean
public Binding binding2() {
return BindingBuilder.bind(queue2()).to(topicExchange()).with("#.topic");
}
}
通配符:
*:代表一个单位的字符(1.topic)
#:代表多个单位的字符(2.2.topic)
3)直连模式(directExchange)
  
声明方式:
/**
* 第三种:Direct模式(直连模式,默认交换机也是这种类型)
*/
@Configuration
public class DirectExchangeConfiguration { @Bean
public Queue queue3() {
return new Queue("queue3", false);
} @Bean
public Queue queue4() {
return new Queue("queue4", false);
} /**
* 参数和topic的交换机类型一样
* @return
*/
@Bean
public DirectExchange directExchange() {
return new DirectExchange("direct", false, false);
} @Bean
public Binding binding3() {
return BindingBuilder.bind(queue3()).to(directExchange()).with("direct.3");
} @Bean
public Binding binding4() {
return BindingBuilder.bind(queue4()).to(directExchange()).with("direct.4");
}
}
4)发布/订阅模式(fanout模式)
  
声明方式:
/**
* 第四种:fanout模式(发布/订阅模式)
*/
@Configuration
public class FanoutExchangeConfiguration { @Bean
public Queue queue5() {
return new Queue("queue5", false);
} @Bean
public Queue queue6() {
return new Queue("queue6", false);
} @Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange("fanout", false, false);
} /**
* 这里的绑定不需要routingKey
* @return
*/
@Bean
public Binding binding5() {
return BindingBuilder.bind(queue5()).to(fanoutExchange());
} /**
* 相比于topic,fanout只能全部发送,topic可以更具匹配规则进行
* @return
*/
@Bean
public Binding binding6() {
return BindingBuilder.bind(queue6()).to(fanoutExchange());
}
}
说明:fanout模式是不需要绑定routingKey,这种方式也是广播形式的主要方式
5)消息头模式(headers模式)
/**
* 第五种:headers模式(消息头模式)
*/
@Configuration
public class HeadersExchangeConfiguration { @Bean
public Queue queue7() {
return new Queue("queue7", false);
} @Bean
public Queue queue8() {
return new Queue("queue8", false);
} @Bean
public HeadersExchange headersExchange() {
return new HeadersExchange("headers", false, false);
} /**
* 确认header是否存在
* @return
*/
@Bean
public Binding binding7() {
return BindingBuilder.bind(queue7()).to(headersExchange()).where("header").exists();
} @Bean
public Binding binding8() {
return BindingBuilder.bind(queue8()).to(headersExchange()).where("header").exists();
}
}
说明:这种方式主要是限定headers,方便通过其他方式携带数据。
6)rpc:
  
声明方式(大同小异):
@Configuration
public class RpcConfiguration { @Bean
public Queue rpc() {
return new Queue("rpc", false);
} @Bean
public DirectExchange rpcExchange() {
return new DirectExchange("rpcExchange", false, false);
} @Bean
public Binding rpcBinding() {
return BindingBuilder.bind(rpc()).to(rpcExchange()).with("rpcRoutingKey");
}
}
lisntener:
@Component
@RabbitListener(queues = "rpc")
public class RpcListener { @RabbitHandler
public String rpcListener(String text, Channel channel, Message message) throws IOException {
System.out.println("rpcServer:" + text);
MessageProperties messageProperties = message.getMessageProperties();
channel.basicAck(messageProperties.getDeliveryTag(), false);
return "success";
}
}
注意这里是有返回数据的。
客户端(publish)
这里推送存在两种方式,同步和异步
a、同步:主题这里默认超时是5秒,可以通过rabbitTemplate设置setReceiveTimeout超时时间。
     String message = (String) rabbitTemplate.convertSendAndReceive("rpcExchange", "rpcRoutingKey", time);
        System.out.println("rpcClient:" + message);
b、异步:
AsyncRabbitTemplate.RabbitConverterFuture<Object> future =
asyncRabbitTemplate.convertSendAndReceive("rpcExchange", "rpcRoutingKey", time);
System.out.println("rpcClient:" + future.get());
注意:AsyncRabbitTemplate是需要手动去配置的。并且需要配置AbstractMessageListenerContainer
如果没有配置AbstractMessageListenerContainer,则需要配置amq.rabbitmq.reply-to(amq.*需要权限才可以配置)
这里是spring对rabbitmq在源码部分对其进行的判断,如果不理解可以自己跟convertSendAndReceive函数
@Bean
public AsyncRabbitTemplate asyncRabbitTemplate(DirectMessageListenerContainer container) {
AsyncRabbitTemplate asyncRabbitTemplate = new AsyncRabbitTemplate(rabbitTemplate, container);
return asyncRabbitTemplate;
} @Bean
public DirectMessageListenerContainer directMessageListenerContainer(ConnectionFactory connectionFactory) {
DirectMessageListenerContainer container = new DirectMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames("rpc");
//这里我改成手动了,但是没有好的方式去获取channel,然后ack.所以我这里使用的自动。
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
//这里可以使用默认的执行器:SimpleAsyncTaskExecutor(但是,这里不是采用的线程池而是直接new Thread)
container.setTaskExecutor(new ThreadPoolExecutor(5, 60, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3000)));
return container;
}
五、消息发送者
1)yaml配置
server:
port: 9001
spring:
rabbitmq:
host: 192.168.5.100
port: 5672
username: guest
password: guest
publisher-confirms: true
publisher-returns: true
template:
#参数意义:true当没有合适的queue直接返回到ReturnCallback
# false没有合适的直接丢弃
mandatory: true
2)如果配置了publisher-confirms、publisher-returns为true.并且加入template.mandatory为true。可以配置如下
@Component
public class RabbitmqPublisherConfiguration { @Autowired
private RabbitTemplate rabbitTemplate; @PostConstruct
public RabbitTemplate rabbitTemplate() {
//1、设置publisher-confirms为true
//2、发布确认,只是在exchange范围
//3、如果没有exchange,则false.如果过为true,则说明发送到exchange成功
rabbitTemplate.setConfirmCallback((correlationData, ack, s) -> {
if (ack) {
System.out.println("send success");
} else {
System.out.println("send fail");
}
});
//1、设置publisher-returns为true
//2、如果没有发布成功,则将消息返回。当然这只是在接受消息层,不是exchange。
rabbitTemplate.setReturnCallback((message, id, reason, exchange, routingKey) -> {
StringBuffer buffer = new StringBuffer();
buffer.append("----------------------------------------\n");
buffer.append("接受消息: {0},失败!\n");
buffer.append("消息ID: {1}\n");
buffer.append("原因: {2}\n");
buffer.append("exchange: {3}\n");
buffer.append("routingKey: {4}\n");
buffer.append("----------------------------------------");
MessageFormat messageFormat = new MessageFormat(buffer.toString());
String text = messageFormat.format(new Object[]{new String(message.getBody()), id, reason, exchange, routingKey});
System.out.println(text); });
return rabbitTemplate;
}
}
a、ConfirmCallback:只是针对exchange,如果消息可以通过exchange,则发送成功。反之则失败
b、ReturnCallback:这个只是针对于routingKey,是否通过。如果这个routingKey不存在,则将消息返回。反之则发送。
3)消息发送
@Component
@EnableScheduling
public class RabbitmqPublisher { @Autowired
private RabbitTemplate rabbitTemplate; @Scheduled(cron = "0/15 * * * * ?")
public void execute() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String time = formatter.format(LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault()));
//默认
rabbitTemplate.convertAndSend("queue", time);
//主题模式
rabbitTemplate.convertAndSend("topic", "1.topic", time);
rabbitTemplate.convertAndSend("topic", "2.2.topic", time);
//直连模式
rabbitTemplate.convertAndSend("direct", "direct.3", time);
rabbitTemplate.convertAndSend("direct", "direct.4", time);
//广播模式
rabbitTemplate.convertAndSend("fanout", "", time);
//headers模式
MessageProperties messageProperties = new MessageProperties();
messageProperties.setHeader("header", "header");
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN);
Message message = MessageBuilder.withBody(time.getBytes()).andProperties(messageProperties).build();
rabbitTemplate.convertAndSend("headers", "", message);
}
}
六、消息监听者
1)yaml配置
server:
port: 9002
spring:
rabbitmq:
host: 192.168.5.100
port: 5672
username: guest
password: guest
listener:
direct:
acknowledge-mode: manual
simple:
acknowledge-mode: manual
说明:如果配置acknowledge-mode: manual(手动模式),则需要手动确认消息。如果没有则不需要手动确认,否则会报错。
需要在每个listener下面加上
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
listener的对手动对消息的处理方式有3种:Ack、Nack、Reject
Ack:确认收到消息
Nack:不确认收到消息
Reject:拒接消息
2)listener
@Component
public class RabbitmqListener { //1.默认队列
@RabbitListener(queues = "queue")
public void queueDouble1(String text, Channel channel, Message message) throws IOException {
System.out.println("queueDouble1:" + text);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} @RabbitListener(queues = "queue")
public void queueDouble2(String text, Channel channel, Message message) throws IOException {
System.out.println("queueDouble2:" + text);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} //2.主题队列
@RabbitListener(queues = "queue1")
public void queue1(String text, Channel channel, Message message) throws IOException {
System.out.println("queue1:" + text);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} @RabbitListener(queues = "queue2")
public void queue2(String text, Channel channel, Message message) throws IOException {
System.out.println("queue2:" + text);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} //3.直连队列
@RabbitListener(queues = "queue3")
public void queue3(String text, Channel channel, Message message) throws IOException {
System.out.println("queue3:" + text);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} @RabbitListener(queues = "queue4")
public void queue4(String text, Channel channel, Message message) throws IOException {
System.out.println("queue4:" + text);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} //4.广播队列
@RabbitListener(queues = "queue5")
public void queue5(String text, Channel channel, Message message) throws IOException {
System.out.println("queue5:" + text);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} @RabbitListener(queues = "queue6")
public void queue6(String text, Channel channel, Message message) throws IOException {
System.out.println("queue6:" + text);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} //5.消息头队列
@RabbitListener(queues = "queue7")
public void queue7(String text, Channel channel, Message message) throws IOException {
System.out.println("queue7:" + text);
System.out.println("header7:" + message.getMessageProperties().getHeaders().get("header"));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} @RabbitListener(queues = "queue8")
public void queue8(String text, Channel channel, Message message) throws IOException {
System.out.println("queue8:" + text);
System.out.println("header8:" + message.getMessageProperties().getHeaders().get("header"));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
2)也可以写成,另外一种方式
@Component
@RabbitListener(queues = "queue")
public class RabbitmqHandlerListener { @RabbitHandler
public void messageHandler(String text, Channel channel, Message message) throws IOException {
System.out.println("queueDouble3:" + text);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
七、测试
  
  
1)默认:
  
均匀的分配到每一个节点
2)主题(topic):
  
只要符合规则就接受
3)直连(direct)
  
和模式方式一样,一对一。多个均匀分布
4)广播(fanout)
  
5)消息头(headers)
  
八、当然例子也可以参考官网:https://www.rabbitmq.com/getstarted.html
九、源码:https://github.com/lilin409546297/springboot-rabbitmq
springboot之rabbitmq的更多相关文章
- SpringBoot集成rabbitmq(二)
		
前言 在使用rabbitmq时,我们可以通过消息持久化来解决服务器因异常崩溃而造成的消息丢失.除此之外,我们还会遇到一个问题,当消息生产者发消息发送出去后,消息到底有没有正确到达服务器呢?如果不进行特 ...
 - SpringBoot之RabbitMQ的使用
		
一 .RabbitMQ的介绍 RabbitMQ是消息中间件的一种,消息中间件即分布式系统中完成消息的发送和接收的基础软件,消息中间件的工作过程可以用生产者消费者模型来表示.即,生产者不断的向消息队列发 ...
 - SpringBoot集成RabbitMQ消息队列搭建与ACK消息确认入门
		
1.RabbitMQ介绍 RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性.扩展性.高可用性等方面表现不俗.Rabbi ...
 - Spring-boot之 rabbitmq
		
今天学习了下spring-boot接入rabbitmq. windows下的安装:https://www.cnblogs.com/ericli-ericli/p/5902270.html 使用博客:h ...
 - springboot学习笔记-6 springboot整合RabbitMQ
		
一 RabbitMQ的介绍 RabbitMQ是消息中间件的一种,消息中间件即分布式系统中完成消息的发送和接收的基础软件.这些软件有很多,包括ActiveMQ(apache公司的),RocketMQ(阿 ...
 - SpringBoot集成RabbitMQ
		
官方说明:http://www.rabbitmq.com/getstarted.html 什么是MQ? MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.MQ ...
 - 【SpringBoot系列5】SpringBoot整合RabbitMQ
		
前言: 因为项目需要用到RabbitMQ,前几天就看了看RabbitMQ的知识,记录下SpringBoot整合RabbitMQ的过程. 给出两个网址: RabbitMQ官方教程:http://www. ...
 - SpringBoot系列八:SpringBoot整合消息服务(SpringBoot 整合 ActiveMQ、SpringBoot 整合 RabbitMQ、SpringBoot 整合 Kafka)
		
声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 整合消息服务 2.具体内容 对于异步消息组件在实际的应用之中会有两类: · JMS:代表作就是 ...
 - rabbitmq学习(五):springboot整合rabbitmq
		
一.Springboot对rabbitmq的支持 springboot提供了对rabbitmq的支持,并且大大简化了rabbitmq的相关配置.在springboot中,框架帮我们将不同的交换机划分出 ...
 
随机推荐
- .Net WebApi 支持跨域访问使用 Microsoft.AspNet.WebApi.Cors
			
首先导入Cors库,通过程序包管理控制台导入 Install-Package Microsoft.AspNet.WebApi.Cors 引用库之后,我们需要进行简单的配置. 现在WebApiConfi ...
 - 通过Python实现一个文档的半自动录入工具
			
需求出现/使用场景: 因为公司需要将word办的接口文档在线化,看起来是个很好的事情,但是就是苦逼了我们这些干活的,其中工程量最大的就是参数的录入,要是参数少也罢,有的接口动辄三四十个参数,更甚八九十 ...
 - BZOJ4517:[SDOI2016]排列计数(组合数学,错排公式)
			
Description 求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的.序列恰好有 m 个数是 ...
 - [AHOI2009]飞行棋
			
嘟嘟嘟 刚开始想这道题的时候确实很蒙,只想到矩形对边做对应的弧长相等,然后想办法凑出相等的弧长.其实正解很简单,不要去想边,应该想对角线,因为根据初中园的知识,这个矩形的对角线是圆的直径,而直径所对的 ...
 - Codeforces 1106 E. Lunar New Year and Red Envelopes 优先队列+dp
			
题意大致是Bob新年拿红包,每个红包可以在s-t时间内取,但是取了之后得在d+1时间开始才能继续取红包. 同时他女儿能在m个时间点阻止他取红包,求女儿阻止后Bob取得的w总和最小值. Bob取红包的策 ...
 - lombok问题
			
今天研究了下以dubbo作为分布式的开源项目dubbo-app. 为了排除一些依赖的冲突和干扰,我另外开辟一个新的工作空间,同时我也将公司项目的依赖打个压缩包分类备份下. 这样一来,dubbo-app ...
 - Sublime Text 自动生成文件头部注释(版权信息):FileHeader 插件的使用
			
(一)安装步骤 1.先安装一个 Package Control 插件.相信大家使用 Sublime 的话都有安装这个了2.Preference -> Package Control -> ...
 - week3编程作业: Logistic Regression中一些难点的解读
			
%% ============ Part : Compute Cost and Gradient ============ % In this part of the exercise, you wi ...
 - postmark使用
			
一.Postmark原理 Postmark是由着名的NAS提供商NetApp开发,用来测试其产品的后端存储性能. Postmark主要用于测试文件系统在邮件系统或电子商务系统中性能,这类应用的特点是: ...
 - 学习JavaSE TCP/IP协议与搭建简易聊天室
			
一.TCP/IP协议 1.TCP/IP协议包括TCP.IP和UDP等 2.域名通过dns服务器转换为IP地址 3.局域网可以通过IP或者主机地址寻找到相应的主机 4.TCP是可靠的连接,效率低,且连接 ...