一、安装RocketMQ

官方下载地址:http://rocketmq.apache.org/dowloading/releases/
打开下载地址找到要下载的版本、复制链接。

然后再Linux中使用命令
wget 链接进行下载,然后使用unzip命令解压缩。
解压完之后给他最高的可执行权限
执行 chmod -R 777 rocketmq-all-4.8.0-bin-release
更改配置
修改相应的配置、应为默认的内存大小是4个G、我们要根据需求改成我们合适的大小。
修改runserver.sh

vim runserver.sh

然后修改第82行的配置为如图所示、改好之后保存退出。

再改runbroker.sh、改好之后保存退出。
再去改conf文件夹下的配置文件broker.conf、增加23、24行
至此就配置好了。

启动RocketMQ
进入解压文件目录下、输入如下命令后台启动
先启动mqnamesrv、输入启动命令后有如下日志输出表示成功

nohup sh ./bin/mqnamesrv -n localhost:9876 &

再启动mqbroker 、输入启动命令后有如下日志输出表示成功

nohup sh ./bin/mqbroker -n localhost:9876 -c ./conf/broker.conf &

二、异步扣减库存(核心)

1. 为什么要异步扣减库存

因为下单时候扣减库存马上要去数据库修改数据、直接去数据库修改数据是比较消耗性能的、并发量一大会造成性能很差体验很不好、所以我们可以直接在下单的时候先不去修改数据库中的库存表,先修改缓存中的数据,然后过一段时间再去修改数据库中的数据,这样就可以大大提高秒杀下单时的并发性能了,而这个功能通过rocketmq消息队列中的事务型消息实现的。

上面说到先扣减缓存中的数据、然后使用异步的方式去扣减数据库中的数据。这样的话就涉及到数据的一致性问题了,就是本地缓存中扣减了库存但有可能消息在传送的过程中没有被消费者消费从而导致数据库中没有扣减库存,那么如何去保证本地事务和消费者消费的一致性呢。这就要使用到rocketmq中的事务型消息了。

官方文档:https://github.com/apache/rocketmq/blob/master/docs/cn/design.md

2. 下单时保证本地事务和消费者消费的一致性问题(重中之重)

这个过程使用到了rocketmq中的事务性消息。总共分成了8步

  1. 生产者先向mq服务(mqserver由servername和broker server组成)发送一个消息,这个消息在内部为hard消息是一种半成品消息,意思是向mqserver打招呼,要向他发送消息了。
  2. 然后mqserver接收到了消息之后会返回一个ok,表示已经准备好了。
  3. 准备好之后在生产者中就会执行本地事务。
  4. 事务成功之后生产者会向发送一个commit消息表示事务执行成功、假设事务失败就会发送一个事务回滚消息rollback消息
  5. 但是4步骤有可能在一些网络环境差等意外情况下发送不到mqserver、这样的话这条消息是无法被处理的不管本地事务有没有执行成功、这条消息会一直卡在这。这里mq使用了回查机制来解决这个问题
    mqserver在接收到send并成功返回ok后会每隔一段时间会通知生产者检查事务有没有成功、默认第一次是30s后来每次间隔时间会越来越长。
  6. 接收到mqserver发送过来的check之后mqserver会去检查事务有没有执行成功
  7. 将事务检查的结果再次发送,成功就发送commit消息,失败发送rollback消息。
  8. 接收到commit或者rollback消息之后才会把消息放到正式队列中、接收到的是commit通知消费者消费、如果是rollback则消费者不消费会撤销这条消息。

本项目中的异步扣减库存为了保证缓存和mysql的异步扣减库存的一致性使用的就是上面mq的事务型消息实现的,只是在上面的一些步骤中加了自己的逻辑代码。

大概如下图

根据步骤进行说明:

  1. 步骤1在发送消息时发送一个要扣减库存的消息、并且生成一个流水,流水的目的是用于check的,后面会说到。
  2. 然后会在执行本地事务中创建订单扣减本地缓存中的库存、并且更新项目中是在创建订单时扣减库存的、假设扣减成功了就会到步骤4表示已经扣减成功,然后最终会被消费者消费这条消息、消费者消费消息时的逻辑就是扣减mysql数据库中的库存。
  3. 假设步骤4的消息没有成功发送到mqserver,那么上面说了会触发mqserver的2次检查机制、那么事务中检查什么比较好呢, 答案是流水、流水表的锁粒度是远远比订单的锁粒度要小的、因为流水表的值是递增、所以锁的时候我们只锁一条流水,而不是整个库存。所以在步骤3的事务中会去更新流水,步骤6检查check检查时检查流水有没有更新成功,更新成功就表示事务成功没成功就表示事务失败。。
  4. 为什么要在发送消息时生成流水呢、因为如果在事务时生成流水假设事务失败,那么连本次流水都没有后面还怎么检查流水。

上面大概的逻辑做好了、接下来就是看项目中对应的代码实现了

3.代码实现

使用事务型消息逻辑实现异步化扣减库存对应项目中的实现代码大体位置如下图

更新销量代码实现

先看orderserviceimpl、由代码可知、是通过异步方式更新销量的、通过发送一个包含itemid商品id和商品数量进行更新的。其中是消息是通过一个json形式转化为字符串之后再转为二进制消息发送给消费者消费的。
接着看更新销量数据的消费者。消费者通过订阅更新订单的消费者信息来监听数据、当接收到消息之后,通过重写onMessage方法在里面先将接收到的消息通过JSONObjective解析成字符串,然后将参数调用itemservice去更新销量。

异步化扣减库存的代码实现

缓存预热代码实现
因为在代码逻辑中要实现先扣减缓存中的数据再扣减数据库中的数据,所以秒杀活动进行之前我们应该先将商品数据加到缓存中去,这叫做缓存预热。本项目没有写后台管理模块、所以通过测试代码模拟、根据下面的代码可知通过将所有item商品全部查出来然后将商品加到redis缓存当中去。

扣减缓存中的商品数量实现
扣减缓存商品数量的实现在ItemServiceImpl中、具体代码如下

流水数据表分析
在本项目中检查的流水对应的表是如图下所示的库存日志表、表id的数据类型使用的是字符类型、不适用数字类型的原因很明显因为商品订单量很大怕越界。然后会存商品的id和要购买的数量,最后一个就是当前商品订单的状态默认是0表示没有被处理、1表示订单创建成功、2表示订单创建失败。而在异步扣减库存的事务型消息中二次检查检查的就是status的值。

对应逻辑图的最后一步的扣减库存实现

对应的实现代码

生产者的监听实现代码

本地消息也就是第3步中创建订单的实现


本地检查方法的实现(对应步骤6检查流水)

本地检查是通过检查流水的状态实现的、在实现代码中可以得到答案

第1步发送消息的实现
这里发送消息是通过controller发送的、传入的参数有用户id、商品数量、活动id

对应的service实现代码

到此异步扣减库存的步骤代码就看完了。但这样还是很乱现在整理下

三、异步化扣减库存代码实现流程整理

  1. 对应逻辑中的第一步发送消息、发送消息进入的是 controller的create方法、然后传递用户id、商品id、商品数量、活动ID过去、然后createOrderAsync进行处理、service中会先去有没有该商品如果没有就抛出一个告窑异常给controller、假如没有告窑就会去创建一个流水、创建流水就是向流水日志表中添加一条记录添加的记录id为随机生成的uuid、商品id、数量还有一个用于二次检查的status状态码默认是0、0表示该流水暂时没有被处理,接着将传过来的用户参数使用json封装成本地事务(本地事务涉及了创建订单操作)需要的参数。将商品id、数量、库存日志id使用json封装成消息参数(消息最终被消费者消费和检查是涉及到库存日志id的)。最后就是发送一个秒杀主题,扣减库存tag的消息了,投递消息后成功后,假设本地事务执行失败会向controller抛出一个订单创建失败异常、目的是让用于知道订单创建失败了。

  2. 消息发送成功之后接着就是执行本地事务了、执行本地事务是在一个本地事务生产者代码里。他会先通过监听然后执行对应的方法、接收到秒杀主题、tag为扣减库存的消息之后,就会去执行创建订单的方法,该方法会异步更新销量并且将扣减库存日志记录的状态码改为1、表示执行成功、也就是执行一个创建订单的事务创建订单成功后会返回一个commit成功的状态码,commit表示事务成功。这里对应的是图中的3

  3. 当commit之后表示事务成功、事务成功之后生产者会将信息发送给mq服务器中的队列中、然后被消费者消费,消费者消费中的逻辑对应的是扣减数据库中的库存。这里对应的是图中的最后一步。

  4. 当在并发量大本地事务成功后product发送的commit没有被mq服务器接收到就会隔一段时间执行check、这个实现是在product中实现的,他是根据库存日志中的状态码字段进行判断的、对应的是图中的check阶段的逻辑。具体逻辑如下

四、总结

异步扣减库存功能是本项目中最最核心的内容也是难点和亮点、一定要理解并熟记该流程。
面试时的热点问题有:涉及的知识点是rocketmq的特性

  1. 消息丢失问题。
    面试时面试官可能会问你你的项目是怎么处理消息丢失问题的、这里涉及到的是rocketmq中的消息可靠性的特性

  1. 消费者消费失败问题
    消费失败的原因有可能是消息本身的原因、也有可能是网络的原因、rocketmq中有一种消息重试的机制解决这个问题、他会将失败的消息放到一个主题为“%RETRY%+consumerGroup”的重试队列中、然后会隔一段时间去重试消费、重试的次数越多间隔的时间就越长。这样就在一定程度上解决了消费者消费失败的问题。

  2. 重复消费问题
    重复消费是因为生产者发送消息时发送失败后会重试重新发送消息、这就会导致消费者消费的消息重复问题、在rocketmq中重复消费是无法避免的、但在一般情况下不会发生、当出现消息量大、网络抖动,消息重复就会是大概率事件。另外,生产者主动重发、consumer负载变化也会导致重复消息。

  3. 死信队列

(秒杀项目) 4.8 异步化扣减库存(核心)相关推荐

  1. 电商扣减库存_电商系统秒杀架构设计

    作者:曹林华 https://blog.51cto.com/13527416/2085258 前言 最近在部门内部分享了原来在电商业务做秒杀活动的整体思路,大家对这次分享反馈还不错,所以我就简单整理了 ...

  2. java 电商锁库存实现_电商项目扣减库存方案

    阿里巴巴b2b电商算法实战电子商务 85.3元 包邮 (需用券) 去购买 > 各位小宝贝们,大家是不是在面试过程中经常被问到,你电商项目扣减库存时,到底是下单减库存呢?还是付款减库存? 那今天给 ...

  3. 浅析「扣减库存」的方案设计

    今天我们来探讨下扣减库存的方案. 生活中,我们总是用各种电商 APP 抢购商品,但是库存数是很少的,特别是秒杀场景,商品可能就一件,那如何保证不会出现超卖的情况呢? 一.扣减库存的三种方案 1.1 下 ...

  4. 基于redis实现的扣减库存

    2019独角兽企业重金招聘Python工程师标准>>> 在日常开发中有很多地方都有类似扣减库存的操作,比如电商系统中的商品库存,抽奖系统中的奖品库存等. 解决方案 使用mysql数据 ...

  5. 电商库存设计mysql redis_基于redis实现的扣减库存

    在日常开发中有很多地方都有类似扣减库存的操作,比如电商系统中的商品库存,抽奖系统中的奖品库存等. 解决方案 使用mysql数据库,使用一个字段来存储库存,每次扣减库存去更新这个字段. 还是使用数据库, ...

  6. 电商扣减库存_经验分享:电商库存体系设计笔记

    最近在做仓库库存管理相关的项目,清晰地了解了仓库是如何管理库存的,并且整清楚了各个系统的库存是如何交互的,整理了下分享给大家. 库存是什么 这里是百度百科给出的解释: "库存(invento ...

  7. 扣减库存,redis你值得拥有

    扯扯犊子 并发知识点总结: 1.秒杀,物理业务隔离,抽成单独的服务器 2.接口设计,防止洪流访问数据库. 3.加redis缓存. 4.缓存雪崩,一个请求,多个key同时失效,避免同时失效.多个请求,一 ...

  8. 电商扣减库存_电商之购物车

    我从刚开始做产品起,就只想做电商,对医疗.教育类的都不太感兴趣.公司转型做电商,整个电商模块重构,有幸负责购物流程优化,之后支付中心和订单中心都会相继优化,今天先来讲购物车. 一.购物车目的 任何产品 ...

  9. 高并发下 如何安全、高效扣减库存? 有更好的方案?

    参考文档 干货分享:五分钟教你解决高并发场景下的订单和库存处理方案 聊聊高并发下库存加减那些事儿--"如何实现异步扣减库存" Redis如何实现高并发分布式锁? 如何利用Redis ...

  10. 电商扣减库存_严选库存中心性能优化

    性能是技术同学时常关注的问题,但在不同的业务模式下.业务板块中开展的优化手段又不尽相同.本文主要介绍基于严选现有业务模式,在未显著降低数据一致性的情况下如何进行库存的性能优化以满足大促活动的要求. 背 ...

最新文章

  1. cocos2dx游戏开发必备工具之PhysicsEditor【ZT】
  2. 计算a[0]*a[1]*...*a[n-1]/a[i]
  3. 优秀网页设计各种国外站的素材
  4. 冒泡、鸡尾酒、选择、插入、归并、快速排序的C++程序
  5. dos命令查看电脑配置
  6. 使用USBWriter等工具做U盘启动盘后容量变小的解决办法
  7. 基于python的饭店点餐外卖管理系统#毕业设计
  8. 寻宝游戏设定_第1集的答案(寻宝游戏)
  9. conductor restart和rerun机制
  10. html 表格和表单知识点
  11. 观音灵签 第六十四签下签卯宫 古人马前覆水
  12. python logging配置时间或大小轮转
  13. 微信公众号自定义菜单如何添加emoji表情图标?
  14. css实现文字或者div盒子水平垂直居中的方法
  15. 13款用于拍摄全景照片的iOS应用
  16. Python3 queue队列类
  17. LyX 发布撑持 CJK 的 1.5 正式版
  18. 西文字体相关术语解说及《干货分享》
  19. python对视频中美女下半身大长腿进行识别,小心脏受不了的千万别看!
  20. ajax动态获取后台数据绘制echarts图表

热门文章

  1. 路由器故障排除的思路与理论
  2. 配置Web.Config连接数据库
  3. 9.携程架构实践 --- 网站高可用
  4. 2.性能之巅 洞悉系统、企业与云计算 --- 方法
  5. 5. PHP APC APCu 安装使用
  6. 45.Linux 网络排错
  7. css3中border-image的用法(fill 、border-image-outset 、border-image-width)
  8. css定位中position:absolute与float的区别
  9. background-color:#e5eecc; border:solid 1px #c3c3c3;
  10. java中float double利用BigDecimal运算