【黑马点评-3秒杀优惠券】七、Kafka异步秒杀优化

1 思路解析

用户 → seckillVoucher() ↓
      [Redis Lua 脚本校验库存和下单资格]
      ↓ 通过? → 否:返回失败信息
      ↓ 是:发送订单消息到 Kafka
后台 → Kafka 消费线程 ← 订单消息 
      ↓ 调用 createVoucherOrder()
      ↓ 更新数据库(扣减库存、写订单)

1.1 从前端的voucherId进到controller方法,后转到service中的VoucherOrderServiceImplKafka.java中的seckillVoucher()方法

  1. 从ThreadLocal中获取用户id
  2. 生成一个uuid
  3. 执行lua脚本,在redis中进行判断
    1. 输入用户券id和用户id
    2. 库存key为"stock"+用户券id,订单key为"order"+用户券id
    3. 如果库存key对应的value<0, 脚本返回1。
    4. 如果通过redis的sismember命令(判断member是key对应set的一员)发现用户id是订单key对应set中的一员,说明之前下过单了,脚本返回2。
    5. 否则说明库存超足,用户没下过单
      • 使用redis执行库存数-1
      • 在该订单key对应的set里增加用户id。
      • 返回0
  4. 若脚本返回值为0,则创建订单对象voucherOrder,包括以下属性
    • 用户id
    • 秒杀券id
    • 订单id
  5. 使用kafka发送这个订单对象, 主题为"voucher-orders",data为voucherOrder。

1.2 创建消费者类KafkaConsumer

主要使用两个注解:@KafkaListener与@KafkaHnadler。这样,Spring Kafka会自动添加线程进行Kafka中订单的异步读取,无需手动创建线程池。因此之前被删除的handleVoucherOrder方法也最好在这个类中实现。

  1. 使用@KafkaListener注解,监听voucher-order主题
  2. @Resource 是 Java EE 提供的注解,按名称(byName)注入。
  3. 使用 @KafkaHandler 注解方法handleVoucherOrder(),方法内部:
    1. 从ThreadLocal获取用户id
    2. 使用Redisson的redisssonClient.getLock(用户id)创建锁
    3. 尝试获取锁后,去try执行createVoucher()创建订单
    4. 最后解锁。

1.3 createVoucherOrder(Long userId, Long voucherId)方法

  1. 根据userId判断eq,count计数判断是否<1,否则不是第一单
  2. update方法,相同的voucherId,的库存stock-1
  3. 前面对于秒杀券的更新完成后,随后创建订单voucherOrder
    • 设置id即orderId
    • 设置用户id,threadLocal中取出
    • 设置voucherId

2 kafka配置

一、配置Kafka

  1. 在pom.xml中导入Maven坐标
<dependency>
	<groupId>org.springframework.kafka</groupId>
	<artifactId>spring-kafka</artifactId>
</dependency>
  1. 对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
  1. 使用IDEA中的插件Big Data Tools Core + Kafka 创建一个”voucher-orders“的主题

3 同步和异步

同步

同步(Synchronous)是指程序按照顺序依次执行,每一步操作完成后再进行下一步。
在同步模式下,当一个任务开始执行时,程序会一直等待该任务完成后才会继续执行下一个任务。

异步

异步(Asynchronous)是指程序在执行任务时,不需要等待当前任务完成,而是在任务执行的同时继续执行其他任务。
在异步模式下,任务的执行顺序是不确定的,程序通过回调、事件通知等方式来获取任务执行的结果。

显然异步的性能是要高于同步的,但是会牺牲掉一定的数据一致性,所以也不是无脑用异步,要根据具体业务进行分析,这里的下单是可以使用异步的,因为下单操作比较耗时,后端操作步骤多,可以进行拆分

4 分析如何优化

之前的

之前秒杀业务的流程:
image

可以看到这个流程是同步执行的,同步是比较耗费时间的,

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

现在

  1. 新增秒杀优惠券的同时,将优惠券信息保存到redis中
  2. 基于lua脚本,判断秒杀库存、一人一单、决定用户是否抢购成功
  3. 如果抢购成功,将优惠券id和userId封装后存入Kafka队列
  4. 不断从Kafka队列中获取信息,实现异步下单功能
posted @ 2025-04-23 18:19  kuki'  阅读(313)  评论(0)    收藏  举报