【黑马点评-3秒杀优惠券】七、Kafka异步秒杀优化
1 思路解析
用户 → seckillVoucher() ↓
[Redis Lua 脚本校验库存和下单资格]
↓ 通过? → 否:返回失败信息
↓ 是:发送订单消息到 Kafka
后台 → Kafka 消费线程 ← 订单消息
↓ 调用 createVoucherOrder()
↓ 更新数据库(扣减库存、写订单)
1.1 从前端的voucherId进到controller方法,后转到service中的VoucherOrderServiceImplKafka.java中的seckillVoucher()方法
- 从ThreadLocal中获取用户id
- 生成一个uuid
- 执行lua脚本,在redis中进行判断
- 输入用户券id和用户id
- 库存key为"stock"+用户券id,订单key为"order"+用户券id
- 如果库存key对应的value<0, 脚本返回1。
- 如果通过redis的sismember命令(判断member是key对应set的一员)发现用户id是订单key对应set中的一员,说明之前下过单了,脚本返回2。
- 否则说明库存超足,用户没下过单
- 使用redis执行库存数-1
- 在该订单key对应的set里增加用户id。
- 返回0
- 若脚本返回值为0,则创建订单对象voucherOrder,包括以下属性
- 用户id
- 秒杀券id
- 订单id
- 使用kafka发送这个订单对象, 主题为"voucher-orders",data为voucherOrder。
1.2 创建消费者类KafkaConsumer
主要使用两个注解:@KafkaListener与@KafkaHnadler。这样,Spring Kafka会自动添加线程进行Kafka中订单的异步读取,无需手动创建线程池。因此之前被删除的handleVoucherOrder方法也最好在这个类中实现。
- 使用@KafkaListener注解,监听voucher-order主题
- @Resource 是 Java EE 提供的注解,按名称(byName)注入。
- 使用 @KafkaHandler 注解方法handleVoucherOrder(),方法内部:
- 从ThreadLocal获取用户id
- 使用Redisson的
redisssonClient.getLock(用户id)创建锁 - 尝试获取锁后,去try执行createVoucher()创建订单
- 最后解锁。
1.3 createVoucherOrder(Long userId, Long voucherId)方法
- 根据userId判断eq,count计数判断是否<1,否则不是第一单
- update方法,相同的voucherId,的库存stock-1
- 前面对于秒杀券的更新完成后,随后创建订单voucherOrder
- 设置id即orderId
- 设置用户id,threadLocal中取出
- 设置voucherId
2 kafka配置
一、配置Kafka
- 在pom.xml中导入Maven坐标
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
- 对application.yaml文件进行配置
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: voucher-order-group
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
properties:
spring.json.trusted.packages: "com.hmdp.entity"
producer:
key-serializer: org.apache.kafka.common.serialization.StringSerializer
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
- 使用IDEA中的插件Big Data Tools Core + Kafka 创建一个”voucher-orders“的主题
3 同步和异步
同步
同步(Synchronous)是指程序按照顺序依次执行,每一步操作完成后再进行下一步。
在同步模式下,当一个任务开始执行时,程序会一直等待该任务完成后才会继续执行下一个任务。
异步
异步(Asynchronous)是指程序在执行任务时,不需要等待当前任务完成,而是在任务执行的同时继续执行其他任务。
在异步模式下,任务的执行顺序是不确定的,程序通过回调、事件通知等方式来获取任务执行的结果。
显然异步的性能是要高于同步的,但是会牺牲掉一定的数据一致性,所以也不是无脑用异步,要根据具体业务进行分析,这里的下单是可以使用异步的,因为下单操作比较耗时,后端操作步骤多,可以进行拆分
4 分析如何优化
之前的
之前秒杀业务的流程:

可以看到这个流程是同步执行的,同步是比较耗费时间的,
我们直接将同步变成异步,从而大幅提高秒杀业务的性能,我们可以将一部分的工作交给Redis,并且不能直接去调用Redis,而是通过开启一个独立的子线程去异步执行,从而大大提高效率

现在
- 新增秒杀优惠券的同时,将优惠券信息保存到redis中
- 基于lua脚本,判断秒杀库存、一人一单、决定用户是否抢购成功
- 如果抢购成功,将优惠券id和userId封装后存入Kafka队列
- 不断从Kafka队列中获取信息,实现异步下单功能

浙公网安备 33010602011771号