文档章节

Java秒杀系统实战系列~秒杀逻辑优化之RabbitMQ接口限流一

稳杰
 稳杰
发布于 2019/08/23 16:51
字数 1475
阅读 102
收藏 0

摘要:本篇博文是“Java秒杀系统实战系列文章”的第十七篇,我们将继续秒杀系统的优化之路。在本篇文章中我们将基于RabbitMQ异步通信、FIFO(先进先出)、接口限流的特性,在执行秒杀核心的处理逻辑之前架上一层“限流”的处理逻辑,从而让瞬时产生的,犹如波涛汹涌、潮水般的请求流量变得井井有条、有序性地到达后端的秒杀接口! 

内容:在前面的篇章中,我们主要是从秒杀的核心处理逻辑着手进行优化,先后从数据库级别Sql的优化、分布式锁的引入、分布式唯一ID算法的引入以及业务服务模块的异步通信、服务解耦等方式对秒杀核心业务逻辑的处理进行了大幅度的调整、优化,本文Debug将介绍一种独立于“秒杀核心业务逻辑”的方式对秒杀系统进行优化。 

首先,我们先来回顾一下秒杀系统整体的秒杀业务逻辑的处理:

当前端高并发产生多线程请求时,正常情况下,前端会在瞬时产生犹如波涛汹涌、潮水般的请求流量到达后端的秒杀接口,此时如果我们的代码处理逻辑并不那么迅速,那么很有可能将应对不了这股蜂拥而至的高并发流量,最终可能出现各种各样乱七八糟的问题,前面所讲的“库存超卖”,“重复秒杀”便是一种体现。

而且,在某种程度上,这些请求流量对于我们来讲有两点我们需要去注意的:

(1) 这些请求流量是透明的,我们后端接口压根不知道、也不需要知道请求对应的用户是哪位;

(2) 这些请求最终并非全部都是有效的,即如果待秒杀的商品数量为N,而请求的流量远远大于N,则很明显,在秒杀开始后将有很大一部分的请求流量对于我们后端接口而言是木有多大用处的,因为秒杀进行到一定时间时,N很有可能已经等于0了。

基于这两点设想,我们希望对这些请求流量进行“规范化”,当前端高并发产生多线程请求流量时,我们希望将这些请求压入“队列”,使得这些请求可以“乖乖的”等待被处理,即变得井井有条、有序性地到达后端的秒杀接口,而不是像无头苍蝇般、一窝蜂的插队处理! 

众所周知,RabbitMQ是一款MQ中间件,MQ正是Message Queue,即消息队列的简称,而我们都知道队列的特点是先进先出,即FIFO;它可以实现先进入队列的消息先被消费处理、后进入队列的消息后被消费处理,因此,RabbitMQ的这一特性将可以助我们实现“接口限流”的作用,其最终的效果如下图所示:   

下面我们就进入代码实战环节:

(1) 首先,我们需要在RabbitmqConfig配置类中创建限流用的队列、交换机和路由消息模型,其代码如下所示:

//TODO:RabbitMQ限流专用

@Bean
public Queue executeLimitQueue(){
    Map<String, Object> argsMap=Maps.newHashMap();
    //限制channel中队列同一时刻通过的消息数量
    argsMap.put("x-max-length", env.getProperty("spring.rabbitmq.listener.simple.prefetch",Integer.class));
    return new Queue(env.getProperty("mq.kill.item.execute.limit.queue.name"),true,false,false,argsMap);
}

@Bean
public TopicExchange executeLimitExchange(){
    return new TopicExchange(env.getProperty("mq.kill.item.execute.limit.queue.exchange"),true,false);
}

@Bean
public Binding executeLimitBinding(){
    return BindingBuilder.bind(executeLimitQueue()).to(executeLimitExchange()).with(env.getProperty("mq.kill.item.execute.limit.queue.routing.key"));
}

其中,读取环境变量的对象实例读取的配置参数是配置为配置文件application.properties中的,如下所示:   

#RabbitMQ限流专用
mq.kill.item.execute.limit.queue.name=${mq.env}.kill.item.execute.limit.queue
mq.kill.item.execute.limit.queue.exchange=${mq.env}.kill.item.execute.limit.exchange
mq.kill.item.execute.limit.queue.routing.key=${mq.env}.kill.item.execute.limit.routing.key

(2) 接着,我们需要在RabbitSenderService服务类中开发一个用于转移巨大用户流量的、将请求流量或者消息发送至RabbitMQ的功能方法,其完整源代码如下所示:

//秒杀时异步发送Mq消息
public void sendKillExecuteMqMsg(final KillDto killDto){
    try {
        if (killDto!=null){
            rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
            rabbitTemplate.setExchange(env.getProperty("mq.kill.item.execute.limit.queue.exchange"));
            rabbitTemplate.setRoutingKey(env.getProperty("mq.kill.item.execute.limit.queue.routing.key"));
            rabbitTemplate.convertAndSend(killDto, new MessagePostProcessor() {
                @Override
                public Message postProcessMessage(Message message) throws AmqpException {
                    MessageProperties mp=message.getMessageProperties();
                    mp.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                    mp.setHeader(AbstractJavaTypeMapper.DEFAULT_CONTENT_CLASSID_FIELD_NAME,KillDto.class);
                    return message;
                }
            });
        }
    }catch (Exception e){
        log.error("秒杀时异步发送Mq消息-发生异常,消息为:{}",killDto,e.fillInStackTrace());
    }
}

(3) 转移消息进入队列之后,消息将变得井井有序、规范化地等待被监听,消费处理,其处理逻辑即为“秒杀接口的核心处理逻辑”,完整源代码于RabbitReceiverService服务类中,如下所示:

//秒杀时异步接收Mq消息-监听者
@RabbitListener(queues = {"${mq.kill.item.execute.limit.queue.name}"},containerFactory = "multiListenerContainer")
public void consumeKillExecuteMqMsg(KillDto dto){
    try {
        if (dto!=null){
            //采用任何一种加分布锁的处理方法都是可行的 killItemV5也行
            killService.killItemV4(dto.getKillId(),dto.getUserId());
        }
    }catch (Exception e){
        log.error("用户秒杀成功后超时未支付-监听者-发生异常:",e.fillInStackTrace());
    }
}

RabbitMQ限流、转移以及在接收处理层面的代码开发已经完成了,下篇文章我们将将其整合至秒杀的业务逻辑当中!

补充:

1、目前,这一秒杀系统的整体构建与代码实战已经全部完成了,完整的源代码数据库地址可以来这里下载:https://gitee.com/steadyjack/SpringBoot-SecondKill 记得Fork跟Star啊!!

2、最后,不要忘记了关注一下Debug的技术微信公众号:

© 著作权归作者所有

稳杰
粉丝 4
博文 18
码字总数 31856
作品 0
广州
高级程序员
私信 提问
加载中

评论(0)

SpringBoot整合RabbitMQ之典型应用场景实战二

实战前言 RabbitMQ 作为目前应用相当广泛的消息中间件,在企业级应用、微服务应用中充当着重要的角色。特别是在一些典型的应用场景以及业务模块中具有重要的作用,比如业务服务模块解耦、异步...

小红牛
2018/11/21
0
0
爬虫架构 | 消息队列应用场景及ActiveMQ、RabbitMQ、RocketMQ、Kafka对比

前言:在之前的业务中,使用了Kafka和RabbitMQ两种消息队列,这篇文章来做一个总结。 消息队列中间件是分布式系统中重要的组件,主要实现异步消息,应用解耦,流量削峰及消息通讯等功能。 下...

小怪聊职场
2018/04/26
0
0
基于 Spring Boot 的电商秒杀系统 - jseckill

一个基于 Spring Boot 2.1 的电商秒杀系统。 让程序员能够轻松进阶 Java 高并发架构,很适合中小型互联网项目的电商秒杀,抢票等场景。采用最新的 Spring Boot, MyBatis 版本。 架构图 秒杀步...

刘少明Frank
2019/03/11
9.4K
10
高并发架构系列:Kafka、RocketMQ、RabbitMQ的优劣势比较

在高并发业务场景下,典型的阿里双11秒杀等业务,消息队列中间件在流量削峰、解耦上有不可替代的作用。 我之前介绍了【MQ消息队列的12点核心原理总结】,【如何从0到1设计一个MQ消息队列】,...

mikechen优知
2019/01/09
0
0
Rabbitmq---消息队列

一 . MQ:message queue   消息队列的作用:   1 通信解耦   2 高峰限流 原理分析: 一开始,认证系统是强耦合的,A系统传递认证系统消息接收计算结果的过程中   1 传给认证系统   2 认...

Ala6
2018/11/14
523
2

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周三乱弹 —— 提高不了工作效率和脸有关系

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @薛定谔的兄弟 :分享洛神有语创建的歌单「我喜欢的音乐」: 1 《夏令时记录(piano.ver)》- ゆめこ 手机党少年们想听歌,请使劲儿戳(这里) ...

小小编辑
今天
67
2
List的一波操作

public static void main(String[] args) { List<Entity> list = new ArrayList<>(); list.add(new Entity(1)); list.add(new Entity(2)); list.add(new Entity(3)); ......

那个猩猩很亮
今天
75
0
Spring基础

主要用于service层; 轻量级java开发框架; 各层 web层:struts,spring-MVC service层:spring dao层:hibernate,mybatis , jdbcTemplate --> spring-data Spring核心:控制反转IOC 切面编...

七宝1
今天
30
0
解决overflow+border-radius+transform圆角问题

网上还有其他版本,但是对我来说都不好使,下面是我在Chrome上的代码。overflow:hidden依然是不能正常使用,换成unset就可以,读者如果有更好的解决方案,请留言,谢谢。 <figure> <img...

hi懒喵
今天
53
0
《C语言》—— 数组

书籍使我变成了一个幸福的人,使我的生活变成轻松而舒适的诗。——高尔基 本文已经收录至我的GitHub,欢迎大家踊跃star 和 issues。 https://github.com/midou-tech/articles 点关注,不迷路!...

龙跃十二
今天
84
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部