使用Redis和RabbitMQ实现一个秒杀系统

使用Redis和RabbitMQ实现一个秒杀系统

在电商领域,秒杀活动是吸引用户、提升销量的有效手段。但秒杀场景对系统架构提出了严峻挑战,如何处理瞬时高并发、保证数据一致性是关键所在。本文会提供一个基于Redis和RabbitMQ实现一个秒杀系统示例,以下为具体代码:

首先引入maven依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

应用配置

在 application.yml 中配置 Redis 和 RabbitMQ:

spring:
  redis:
    host: localhost
    port: 6379
  rabbitmq:
    host: localhost
    port: 5672

商品库存模型

创建一个商品库存模型 ProductStock.java:

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * @Description: 商品库存
 * @ClassName: ProductStock
 * @Author: ceshi
 * @Date: 2025/1/15 10:37
 * @Version: 1.0
 */
@Data
@AllArgsConstructor
public class ProductStock {

    // 商品id
    private Integer productId;
    // 库存数量
    private Integer stock;
}

秒杀请求模型

定义秒杀请求模型 SeckillRequest.java:

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * @Description: 秒杀请求
 * @ClassName: SecKillRequest
 * @Author: ceshi
 * @Date: 2025/1/14 14:59
 * @Version: 1.0
 */
@Data
@AllArgsConstructor
public class SecKillRequest {

    // 商品id
    private Integer productId;
    // 用户id
    private Integer userId;
}

秒杀处理逻辑

创建秒杀控制器 SecKillController.java:

import com.demo.seckill.model.SecKillRequest;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.concurrent.TimeUnit;

/**
 * @Description: 秒杀controller
 * @ClassName: SecKillController
 * @Author: ceshi
 * @Date: 2025/1/14 14:50
 * @Version: 1.0
 */
@RestController
@RequestMapping("/secKill")
public class SecKillController {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;

    /**
     * Redis用于存储商品库存, RabbitMQ用于处理秒杀请求
     * @param productId
     * @param userId
     * @return
     */
    @PostMapping("/startSecKill/{productId}")
    public ResponseEntity<String> startSecKill(@PathVariable Integer productId, @RequestParam Integer userId) {
        // 从缓存查询库存
        String key = "product_stock:" + productId;
        Integer stock = redisTemplate.opsForValue().get(key);
        // 校验库存
        if (stock == null || stock <= 0) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("秒杀失败,商品库存不足");
        }
        // 一人一单校验
        if (redisTemplate.opsForValue().setIfAbsent("secKill:user:" + userId, productId, 30, TimeUnit.MINUTES)) {
            // 发送秒杀请求
            rabbitTemplate.convertAndSend("secKill_queue", new SecKillRequest(productId, userId));
            return ResponseEntity.ok("秒杀请求已提交,请稍后查看结果");
        } else {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("您已经参与过该商品的秒杀活动");
        }
    }
}

秒杀消费者服务

从消息队列中获取秒杀请求,进行库存校验和扣减。

import com.demo.seckill.model.SecKillRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

/**
 * @Description: 秒杀消费者
 * @ClassName: SecKillConsumer
 * @Author: ceshi
 * @Date: 2025/1/14 14:54
 * @Version: 1.0
 */
@Component
public class SecKillConsumer {

    private static final Logger logger = LoggerFactory.getLogger(SecKillConsumer.class);

    @Autowired
    private RedisTemplate<String, Integer> redisTemplate;

    /**
     * 监听特secKill_queue消息队列,异步处理秒杀请求
     * @param message
     */
    @RabbitListener(queues = "secKill_queue")
    public void receiveMessage(SecKillRequest message) {
        String key = "product_stock:" + message.getProductId();
        Integer stock = redisTemplate.opsForValue().get(key);
        // 再次校验库存
        if (stock != null && stock > 0) {
            // 扣减库存
            redisTemplate.opsForValue().decrement(key);
            // 创建订单业务逻辑
        } else {
            // 库存不足,处理逻辑
            logger.info("秒杀失败: 商品 " + message.getProductId() + " 库存不足");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java.程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值