springboot与rabbitMQ实现延迟加载
来源:互联网 发布:视频加马赛克软件 编辑:程序博客网 时间:2024/06/03 05:41
参考:
http://blog.csdn.net/u014308482/article/details/53036770
http://blog.csdn.net/i_vic/article/details/72742277
里面的例子参考自这两篇博客,记录下使用过程。
为什么要延迟加载:
制定一项任务,在某个时间之后去执行,这种场景比较适合使用延迟加载的模式。
延迟队列存储的对象肯定是对应的延时消息,所谓”延时消息”是指当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费。
原理:
Time To Live(TTL)
RabbitMQ可以针对Queue和Message设置 x-message-tt,来控制消息的生存时间,如果超时,则消息变为dead letter
RabbitMQ针对队列中的消息过期时间有两种方法可以设置。
A: 通过队列属性设置,队列中所有消息都有相同的过期时间。
B: 对消息进行单独设置,每条消息TTL可以不同。
如果同时使用,则消息的过期时间以两者之间TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的TTL值,就成为dead letter
Dead Letter Exchanges(DLX)
RabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由。
x-dead-letter-exchange:出现dead letter之后将dead letter重新发送到指定exchange
x-dead-letter-routing-key:指定routing-key发送
队列出现dead letter的情况有:
消息或者队列的TTL过期
队列达到最大长度
消息被消费端拒绝(basic.reject or basic.nack)并且requeue=false
利用DLX,当消息在一个队列中变成死信后,它能被重新publish到另一个Exchange。这时候消息就可以重新被消费。
利用这两个特性,设置消息的过期时间,当生产的消息没有消费者去接收(这样的队列称为死信队列),消息在
rabbitServer的到达设置的过期时间时,就会将死信队列中的过期消息发送到DLX中设置的Exchange中,这样
就实现了延迟加载。
上图:
集成过程如下:
生产者端配置
1.引入依赖:
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.41</version></dependency></dependencies>
2.配置application.properties
server.port=10001spring.rabbitmq.host=localhostspring.rabbitmq.port=5672spring.rabbitmq.username=guestspring.rabbitmq.password=guestspring.rabbitmq.publisher-confirms=truespring.rabbitmq.virtual-host=/
3.配置AMQP
@Configurationpublic class AmqpConfig {@BeanRabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {return new RabbitAdmin(connectionFactory);}@Bean@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {RabbitTemplate template = new RabbitTemplate(connectionFactory);return template;}}
4.声明队列、交换机
@Configurationpublic class ExchangeConfig {/******************************************死信队列***************************************************/ //exchange name public static final String DEFAULT_EXCHANGE = "KSHOP"; //DLX QUEUE public static final String DEFAULT_DEAD_LETTER_QUEUE_NAME = "kshop.dead.letter.queue"; //DLX repeat QUEUE 死信转发队列 public static final String DEFAULT_REPEAT_TRADE_QUEUE_NAME = "kshop.repeat.trade.queue"; //信道配置 @Bean public DirectExchange defaultExchange() { return new DirectExchange(DEFAULT_EXCHANGE, true, false); } @Bean public Queue repeatTradeQueue() { Queue queue = new Queue(DEFAULT_REPEAT_TRADE_QUEUE_NAME,true,false,false); return queue; } @Bean public Binding drepeatTradeBinding() { return BindingBuilder.bind(repeatTradeQueue()).to(defaultExchange()).with(DEFAULT_REPEAT_TRADE_QUEUE_NAME); } @Bean public Queue deadLetterQueue() { Map<String, Object> arguments = new HashMap<>(); arguments.put("x-dead-letter-exchange", DEFAULT_EXCHANGE); arguments.put("x-dead-letter-routing-key", DEFAULT_REPEAT_TRADE_QUEUE_NAME); Queue queue = new Queue(DEFAULT_DEAD_LETTER_QUEUE_NAME,true,false,false,arguments); System.out.println("arguments :" + queue.getArguments()); return queue; } @Bean public Binding deadLetterBinding() { return BindingBuilder.bind(deadLetterQueue()).to(defaultExchange()).with(DEFAULT_DEAD_LETTER_QUEUE_NAME); } }
这里指定了声明了死信队列失效之后的发送的交换机和routing-key,其实这里可以指定两个交换机,一个是死信队列的交换机1绑定死信队列,一个是失效之后到达的交换机2绑定延迟队列,死信队列的交换机没有消费者去监听,而交换机2绑定的队列就是真正的延迟队列了,消费者去监听这个队列。
@Servicepublic class DeadLetterService {@Autowiredprivate RabbitTemplate rabbitTemplate;public void send(LogCarrier logCarrier) {MessagePostProcessor processor = new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {message.getMessageProperties().setExpiration(30000 + "");return message;}};rabbitTemplate.convertAndSend(ExchangeConfig.DEFAULT_EXCHANGE, ExchangeConfig.DEFAULT_DEAD_LETTER_QUEUE_NAME,JSON.toJSONString(logCarrier), processor);}}这里指定了超时时间为30秒
6.创建controller去调用
@RestControllerpublic class DeadLetterController {@Autowiredprivate DeadLetterService deadLetterService;@GetMapping("deadLetter")public void direct() throws InterruptedException {long i = 0;while(i<10) {LogCarrier contract = new LogCarrier();contract.setId(i++);contract.setType("direct");deadLetterService.send(contract);}System.out.println("消息发送时间:"+new Date());}}这样,生产者端的基本都配置完成。
消费者端配置,消费者端的配置很简单:
1.依赖
<dependencies><!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.41</version></dependency></dependencies>
2.application.properties
server.port=0spring.rabbitmq.host=localhostspring.rabbitmq.port=5672spring.rabbitmq.username=guestspring.rabbitmq.password=guestspring.rabbitmq.publisher-confirms=truespring.rabbitmq.virtual-host=/
@Configuration@EnableRabbitpublic class ConsumerConfig implements RabbitListenerConfigurer {@Beanpublic DefaultMessageHandlerMethodFactory myHandlerMethodFactory() {DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();factory.setMessageConverter(new MappingJackson2MessageConverter());return factory;}@Beanpublic SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();factory.setConnectionFactory(connectionFactory);factory.setPrefetchCount(1);//设置预读取数,可以进行有效的负载均衡。factory.setAcknowledgeMode(AcknowledgeMode.AUTO);//自动askreturn factory;}@Overridepublic void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {registrar.setMessageHandlerMethodFactory(myHandlerMethodFactory());}}
4.监听service
/** * 死信队列 * * @author cfyj 2017年11月24日 下午3:11:05 * */@Servicepublic class CustomService4 {private static int num = 0;@RabbitListener(queues = "kshop.repeat.trade.queue") @RabbitHandlerpublic void process(String obj) {LogCarrier logCarrier= JSON.parseObject(obj, LogCarrier.class);System.out.println(num+":------消息接收时间"+new Date()+logCarrier);}}
传输实体:
package com.cfyj.demo.domain;public class LogCarrier {private Long id;private String type;public String getType() {return type;}public void setType(String type) {this.type = type;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}@Overridepublic String toString() {return "LogCarrier [id=" + id + ", type=" + type + "]";}}
这样生产者和消费者都配置完成了。注意生产者和消费者端传输的对象实体类信息必须一致。
这样就开始测试吧,测试之前我们带着几个问题去测试:
只启动生产者,然后向死信队列发送信息,消息失效后会怎么样?
如果指定交换机的类型为fanout,没有消费者监听是否会将信息直接丢弃呢?
1.测试,启动生产者和消费者(先启动生产者来声明交换机和队列)
发送消息的时间
死信队列中的消息数
延迟队列的消息,这时因为过期时间还没到,所以死信队列中的信息还没有到达延迟队列中
消费者收到延迟队列的时间
接收-发送的时间正好为过期时间30s,这样就实现了消息的延迟消费,在到达过期时间后,死信队列的消息会发送到指定x-dead-letter-exchange的交换机中,由交换机发送到设置的延迟队列。
2.当我们只启动生产者时,发送消息,消息会怎么样?(topic类型和direct类型的测试结果一致)
发送请求时间:
死信队列中的消息:
当达到过期时间后,延迟队列的消息(注意两个队列收到消息的时间):
当只启动生产者服务然后发送消息到死信队列时,消息会先堆积到死信队列,然后到达过期时间后重发到延迟队列中。
3.如果指定交换机的类型为fanout,没有消费者监听是否会将信息直接丢弃呢?
发送信息后,消息会先进入死信队列中,并没有直接丢弃消息
死信队列,注意接收消息的时间:
死信队列中的消息到达过期时间后:
延迟队列:
测试结果与direct类型相同。
- springboot与rabbitMQ实现延迟加载
- rabbitmq实现延迟队列
- springboot实现rabbitmq
- 基于 rabbitmq 实现延迟队列
- RabbitMQ如何实现延迟队列?
- 基于 rabbitmq 实现延迟队列
- 使用rabbitmq 实现延迟消费
- Js实现延迟加载
- mybatis实现延迟加载
- rabbitmq 实现延迟队列(ttl+DLX)
- Spring集成RabbitMQ并实现延迟队列
- C#实现rabbitmq 延迟队列功能
- rabbitmq 延迟队列的实现(PHP)
- 使用RabbitMQ实现延迟任务(转载)
- Spring MVC整合RabbitMQ实现延迟队列
- MIPS 分支延迟与加载延迟
- Rabbitmq 整合Spring,SpringBoot与Docker
- 随便谈谈RabbitMQ与springBoot进行集成。
- 美国嘻哈歌手
- 大量并发修改实施分库
- GitHub应用(二)
- weblogic安装时提示不是有效的JDK Java主目录解决方案
- 你所不知道的Chrome调试技巧(下)
- springboot与rabbitMQ实现延迟加载
- StringUtils的isBlank与isEmply
- C#对数据库的操作
- java线程内存模型
- 基于gensim的Doc2Vec简析,以及用python 实现简要代码
- uva11512
- 别人总结的学习数据结构的经验
- SpirngMVC第一个程序
- 大数据从0到一(HDFS)