【281期】面试官问:淘宝七天自动确认收货,可以怎么实现?
点击上方“Java精选”,选择“设为星标”
别问别人为什么,多问自己凭什么!
下方有惊喜,留言必回,有问必答!
每一天进步一点点,是成功的开始...
目前常见的应用软件都有消息的延迟推送的影子,应用也极为广泛,例如:
淘宝七天自动确认收货。在我们签收商品后,物流系统会在七天后延时发送一个消息给支付系统,通知支付系统将款打给商家,这个过程持续七天,就是使用了消息中间件的延迟推送功能。
12306 购票支付确认页面。我们在选好票点击确定跳转的页面中往往都会有倒计时,代表着 30 分钟内订单不确认的话将会自动取消订单。其实在下订单那一刻开始购票业务系统就会发送一个延时消息给订单系统,延时30分钟,告诉订单系统订单未完成,如果我们在30分钟内完成了订单,则可以通过逻辑代码判断来忽略掉收到的消息。
在上面两种场景中,如果我们使用下面两种传统解决方案无疑大大降低了系统的整体性能和吞吐量:
使用 redis 给订单设置过期时间,最后通过判断 redis 中是否还有该订单来决定订单是否已经完成。这种解决方案相较于消息的延迟推送性能较低,因为我们知道 redis 都是存储于内存中,我们遇到恶意下单或者刷单的将会给内存带来巨大压力。
使用传统的数据库轮询来判断数据库表中订单的状态,这无疑增加了IO次数,性能极低。
使用 jvm 原生的 DelayQueue ,也是大量占用内存,而且没有持久化策略,系统宕机或者重启都会丢失订单信息。
1、消息延迟推送的实现
在 RabbitMQ 3.6.x 之前我们一般采用死信队列+TTL过期时间来实现延迟队列,我们这里不做过多介绍。
另外,推荐下 Spring boot 的实战开源项目:
https://gitee.com/yoodb/jing-xuan
在 RabbitMQ 3.6.x 开始,RabbitMQ 官方提供了延迟队列的插件,可以下载放置到 RabbitMQ 根目录下的 plugins 下。
首先我们创建交换机和消息队列,application.properties 中配置与上一篇文章相同。
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.HashMap;
import java.util.Map;@Configuration
public class MQConfig {public static final String LAZY_EXCHANGE = "Ex.LazyExchange";public static final String LAZY_QUEUE = "MQ.LazyQueue";public static final String LAZY_KEY = "lazy.#";@Beanpublic TopicExchange lazyExchange(){//Map<String, Object> pros = new HashMap<>();//设置交换机支持延迟消息推送//pros.put("x-delayed-message", "topic");TopicExchange exchange = new TopicExchange(LAZY_EXCHANGE, true, false, pros);exchange.setDelayed(true);return exchange;}@Beanpublic Queue lazyQueue(){return new Queue(LAZY_QUEUE, true);}@Beanpublic Binding lazyBinding(){return BindingBuilder.bind(lazyQueue()).to(lazyExchange()).with(LAZY_KEY);}
}
我们在 Exchange 的声明中可以设置exchange.setDelayed(true)
来开启延迟队列,也可以设置为以下内容传入交换机声明的方法中,因为第一种方式的底层就是通过这种方式来实现的。
另外,推荐公众号Java精选,回复java面试,获取最新面试题资料,内涵500+ PDF文件,支持在线随时随地刷题。
//Map<String, Object> pros = new HashMap<>();//设置交换机支持延迟消息推送,公众号Java精选//pros.put("x-delayed-message", "topic");TopicExchange exchange = new TopicExchange(LAZY_EXCHANGE, true, false, pros);
发送消息时我们需要指定延迟推送的时间,我们这里在发送消息的方法中传入参数 new MessagePostProcessor()
是为了获得 Message
对象,因为需要借助 Message
对象的api 来设置延迟时间。
import com.anqi.mq.config.MQConfig;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Date;@Component
public class MQSender {@Autowiredprivate RabbitTemplate rabbitTemplate;//confirmCallback returnCallback 代码省略,请参照上一篇public void sendLazy(Object message){rabbitTemplate.setMandatory(true);rabbitTemplate.setConfirmCallback(confirmCallback);rabbitTemplate.setReturnCallback(returnCallback);//id + 时间戳 全局唯一公众号Java精选CorrelationData correlationData = new CorrelationData("12345678909"+new Date());//发送消息时指定 header 延迟时间rabbitTemplate.convertAndSend(MQConfig.LAZY_EXCHANGE, "lazy.boot", message,new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {//设置消息持久化message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);//message.getMessageProperties().setHeader("x-delay", "6000");message.getMessageProperties().setDelay(6000);return message;}}, correlationData);}
}
我们可以观察 setDelay(Integer i)
底层代码,也是在 header 中设置 x-delay。等同于我们手动设置 header
推荐下几个月熬夜整理的近 10000+ 面试资料大全:https://gitee.com/yoodb/ebooks
message.getMessageProperties().setHeader("x-delay", "6000");
/*** Set the x-delay header.* @param delay the delay.* @since 1.6*/
public void setDelay(Integer delay) {if (delay == null || delay < 0) {this.headers.remove(X_DELAY);}else {this.headers.put(X_DELAY, delay);}
}
2、消费端进行消费
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.util.Map;@Component
public class MQReceiver {@RabbitListener(queues = "MQ.LazyQueue")@RabbitHandlerpublic void onLazyMessage(Message msg, Channel channel) throws IOException{long deliveryTag = msg.getMessageProperties().getDeliveryTag();channel.basicAck(deliveryTag, true);System.out.println("lazy receive " + new String(msg.getBody()));}```## 测试结果[#](https://www.cnblogs.com/haixiang/p/10966985.html#3724420099)```java
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;@SpringBootTest
@RunWith(SpringRunner.class)
public class MQSenderTest {@Autowiredprivate MQSender mqSender;@Testpublic void sendLazy() throws Exception {String msg = "hello spring boot";mqSender.sendLazy(msg + ":");}
}
果然在 6 秒后收到了消息 lazy receive hello spring boot:
作者:海向
https://cnblogs.com/haixiang/p/10966985.html
公众号“Java精选”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!
------ THE END ------
精品资料,超赞福利!
>Java精选面试题<
3000+ 道面试题在线刷,最新、最全 Java 面试题!
期往精选 点击标题可跳转
【273期】面试官问:为什么不推荐使用 BeanUtils 属性转换工具?
【274期】Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?
【275期】从一个消费慢的例子深入理解 kafka rebalance
【276期】面试官问:String长度有限制吗?是多少?
【277期】如何写好 Java 业务代码?这也是有很多规范的!
【278期】Spring Boot 巧用 @Async 提升接口并发能力
【279期】Spring Boot 服务监控机制,总算整明白了!
【280期】Spring Boot 利用 Redis 实现接口限流
技术交流群!
最近有很多人问,有没有读者交流群!想知道如何加入?方式很简单,兴趣相投的朋友,只需要点击下方卡片,回复“加群”,即可无套路入交流群!
文章有帮助的话,在看,转发吧!
【281期】面试官问:淘宝七天自动确认收货,可以怎么实现?相关推荐
- 从零开发短视频电商 30分钟未支付订单自动关闭、七天自动确认收货等延迟任务问题
文章目录 常见延迟任务 常见解决方案 主动形式 被动形式 基于Redis实现ZSet的方式.键空间通知的方式 ZSet的方式 键空间通知的方式 RocketMQ延迟消息 延迟消息级别配置 客户端发送延 ...
- 安卓开发学习笔记(1)使用Bundle在Activity之间交换数据(实例:模拟淘宝的填写并显示收货地址的功能)
运行结果: 代码截图: 不要忘记在manifest->AndroidManifest.XML中添加新的Activity,否则会闪退 MainActivity.java AddressActivi ...
- 【283期】面试官问:高并发场景下,如何保证全局唯一分布式 ID 生成?
点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每一天进步一点点,是成功的开始... 前言 系统 ...
- 【169期】面试官问:说说为什么要限流,有哪些解决方案?
点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜留言必回,有问必答! 每天 08:35 更新文章,每天进步一点点... ...
- 【059期】面试官问:序列化是什么,为什么要序列化,如何实现?
>>号外:关注"Java精选"公众号,回复"面试资料",免费领取资料!"Java精选面试题"小程序,3000+ 道面试题在线刷, ...
- 【154期】面试官问:请你说说 B 树、B+ 树的原理及区别?
点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方留言必回,有问必答! 每天 08:35 更新文章,每天进步一点点... 之前在 ...
- 【020期】面试官问:Java 遍历 Map 集合有几种方式?效率如何?
>>号外:关注"Java精选"公众号,回复"2021面试题",领取免费资料!"Java精选面试题"小程序,3000+ 道面试题在 ...
- 【146期】面试官问:说一说 RabbitMQ 的几种工作模式和优化建议?
点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方留言必回,有问必答! 每天 08:00 更新文章,每天进步一点点... 1.组 ...
- 【266期】面试官问:为什么 SQL 要尽量避免使用 IN 和 NOT IN?
点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每一天进步一点点,是成功的开始... IN 和 ...
- 【264期】面试官问:Spring Boot 启动时自动执行代码方式有哪几种?解释一二!...
点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每一天进步一点点,是成功的开始... 前言 目前 ...
最新文章
- PHP版UTF-8文件BOM自动检测移除程序
- JAVA web项目报错no sigar-x86-winnt.dll in java.library.path
- 微软ASP.NET站点部署指南(3):使用Web.Config文件的Transformations
- LINQ to SQL自定义映射表关系(1:N or 1:1)
- 巴洛克式和哥特式的区别
- 完整的Flex多文件上传实例
- 浅谈C++的智能指针
- JSP开发常用问题解决
- 如果Google统治世界[组图]
- The 7th Zhejiang Provincial Collegiate Programming Contest-Problem B:B - Somali Pirates
- ArrayList与普通数组的区别
- PHP获取每月第一天与最后一天
- 华为服务器linux光驱名称,华为服务器通过mgmt口挂载光盘装系统及Linux系统rescue模式下修复内核...
- JavaScript,css时间计时器
- 5java讲解(xy)
- iOS7适配问题 UITableView上方出现空白
- html语言如此简单,HTML lang 没你想的那么简单
- 聊聊让开发头疼的一句话需求那些事
- java中文句号转换英文句号_java实现中文或其他语言及标点符号等转换成unicode字符串,或unicode的16进制码转换回文字或符号等...
- 线性共轭梯度法python_Python中实现共轭梯度法的案例
热门文章
- 用于 Linux* 的英特尔® 图形驱动程序 以后买本本的时候,先注意一下
- 工业交换机与普通交换机区别
- Transition过渡动画
- LVM实现将2块磁盘总空间“合二为一”并挂载到同一目录/移除磁盘
- php英文星期中文星期,英文星期到星期天【星期一到星期天的英文用中文怎么说。发音标准的来。】...
- Piranha介绍:过期代码自动删除的开源工具
- 微信接口类php,【微信接口库】分享10个常用的php微信接口类
- 安利 : プログラミングで彼女をつくる 全攻略~
- 时间换算--C语言结构练习
- Google回归中国,你准备好成为Googler了吗?