Java秒殺系統(tǒng)(十七):秒殺邏輯優(yōu)化之RabbitMQ接口限流一
作者:
修羅debug
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 by-sa 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。
摘要:本篇博文是“Java秒殺系統(tǒng)實戰(zhàn)系列文章”的第十七篇,我們將繼續(xù)秒殺系統(tǒng)的優(yōu)化之路。在本篇文章中我們將基于RabbitMQ異步通信、FIFO(先進先出)、接口限流的特性,在執(zhí)行秒殺核心的處理邏輯之前架上一層“限流”的處理邏輯,從而讓瞬時產(chǎn)生的,猶如波濤洶涌、潮水般的請求流量變得井井有條、有序性地到達后端的秒殺接口!
內(nèi)容:在前面的篇章中,我們主要是從秒殺的核心處理邏輯著手進行優(yōu)化,先后從數(shù)據(jù)庫級別Sql的優(yōu)化、分布式鎖的引入、分布式唯一ID算法的引入以及業(yè)務服務模塊的異步通信、服務解耦等方式對秒殺核心業(yè)務邏輯的處理進行了大幅度的調(diào)整、優(yōu)化,本文Debug將介紹一種獨立于“秒殺核心業(yè)務邏輯”的方式對秒殺系統(tǒng)進行優(yōu)化。
首先,我們先來回顧一下秒殺系統(tǒng)整體的秒殺業(yè)務邏輯的處理:
當前端高并發(fā)產(chǎn)生多線程請求時,正常情況下,前端會在瞬時產(chǎn)生猶如波濤洶涌、潮水般的請求流量到達后端的秒殺接口,此時如果我們的代碼處理邏輯并不那么迅速,那么很有可能將應對不了這股蜂擁而至的高并發(fā)流量,最終可能出現(xiàn)各種各樣亂七八糟的問題,前面所講的“庫存超賣”,“重復秒殺”便是一種體現(xiàn)。
而且,在某種程度上,這些請求流量對于我們來講有兩點我們需要去注意的:
(1) 這些請求流量是透明的,我們后端接口壓根不知道、也不需要知道請求對應的用戶是哪位;
(2) 這些請求最終并非全部都是有效的,即如果待秒殺的商品數(shù)量為N,而請求的流量遠遠大于N,則很明顯,在秒殺開始后將有很大一部分的請求流量對于我們后端接口而言是木有多大用處的,因為秒殺進行到一定時間時,N很有可能已經(jīng)等于0了。
基于這兩點設想,我們希望對這些請求流量進行“規(guī)范化”,當前端高并發(fā)產(chǎn)生多線程請求流量時,我們希望將這些請求壓入“隊列”,使得這些請求可以“乖乖的”等待被處理,即變得井井有條、有序性地到達后端的秒殺接口,而不是像無頭蒼蠅般、一窩蜂的插隊處理!
眾所周知,RabbitMQ是一款MQ中間件,MQ正是Message Queue,即消息隊列的簡稱,而我們都知道隊列的特點是先進先出,即FIFO;它可以實現(xiàn)先進入隊列的消息先被消費處理、后進入隊列的消息后被消費處理,因此,RabbitMQ的這一特性將可以助我們實現(xiàn)“接口限流”的作用,其最終的效果如下圖所示:
下面我們就進入代碼實戰(zhàn)環(huán)節(jié):
(1) 首先,我們需要在RabbitmqConfig配置類中創(chuàng)建限流用的隊列、交換機和路由消息模型,其代碼如下所示:
//TODO:RabbitMQ限流專用
@Bean
public Queue executeLimitQueue(){
Map<String, Object> argsMap=Maps.newHashMap();
//限制channel中隊列同一時刻通過的消息數(shù)量
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"));
}
其中,讀取環(huán)境變量的對象實例讀取的配置參數(shù)是配置為配置文件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服務類中開發(fā)一個用于轉(zhuǎn)移巨大用戶流量的、將請求流量或者消息發(fā)送至RabbitMQ的功能方法,其完整源代碼如下所示:
//秒殺時異步發(fā)送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("秒殺時異步發(fā)送Mq消息-發(fā)生異常,消息為:{}",killDto,e.fillInStackTrace());
}
}
(3) 轉(zhuǎn)移消息進入隊列之后,消息將變得井井有序、規(guī)范化地等待被監(jiān)聽,消費處理,其處理邏輯即為“秒殺接口的核心處理邏輯”,完整源代碼于RabbitReceiverService服務類中,如下所示:
//秒殺時異步接收Mq消息-監(jiān)聽者
@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("用戶秒殺成功后超時未支付-監(jiān)聽者-發(fā)生異常:",e.fillInStackTrace());
}
}
RabbitMQ限流、轉(zhuǎn)移以及在接收處理層面的代碼開發(fā)已經(jīng)完成了,下篇文章我們將將其整合至秒殺的業(yè)務邏輯當中!
補充:
1、目前,這一秒殺系統(tǒng)的整體構(gòu)建與代碼實戰(zhàn)已經(jīng)全部完成了,該秒殺系統(tǒng)對應的視頻教程的鏈接地址為:https://www.fightjava.com/web/index/course/detail/6,可以點擊鏈接進行試看以及學習,實戰(zhàn)期間有任何問題都可以留言或者與Debug聯(lián)系、交流!
2、另外,Debug也開源了該秒殺系統(tǒng)對應的完整的源代碼以及數(shù)據(jù)庫,其地址可以來這里下載:https://gitee.com/steadyjack/SpringBoot-SecondKill 記得Fork跟Star啊?。?!
3、最后,不要忘記了關注一下Debug的技術(shù)微信公眾號: