SpringBoot电商项目实战:高并发场景下的库存扣减与消息队列整合

🎓博主介绍:Java、Python、js全栈开发 “多面手”,精通多种编程语言和技术,痴迷于人工智能领域。秉持着对技术的热爱与执着,持续探索创新,愿在此分享交流和学习,与大家共进步。
📖DeepSeek-行业融合之万象视界(附实战案例详解100+)
📖全栈开发环境搭建运行攻略:多语言一站式指南(环境搭建+运行+调试+发布+保姆级详解)
👉感兴趣的可以先收藏起来,希望帮助更多的人
在这里插入图片描述

SpringBoot电商项目实战:高并发场景下的库存扣减与消息队列整合

一、背景介绍

在电商系统中,库存管理是一个核心功能,尤其是在高并发场景下,如限时抢购、节日大促等活动期间,大量用户同时对商品进行下单操作,会对库存系统造成巨大的压力。如果库存扣减处理不当,可能会导致超卖现象,即卖出的商品数量超过了实际库存数量,这会给商家带来严重的经济损失和信誉问题。为了解决高并发场景下的库存扣减问题,我们可以引入消息队列来实现异步处理,提高系统的吞吐量和稳定性。

二、项目环境搭建

2.1 技术选型

  • Spring Boot:简化Spring应用的开发过程,提供快速搭建项目的能力。
  • Spring Data JPA:用于与数据库进行交互,简化数据库操作。
  • RabbitMQ:作为消息队列,实现异步消息处理。
  • MySQL:作为项目的数据库,存储商品信息和库存信息。

2.2 项目创建

使用Spring Initializr(https://start.spring.io/)创建一个新的Spring Boot项目,添加以下依赖:

<dependencies>
    <!-- Spring Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- MySQL Driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!-- RabbitMQ -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
</dependencies>

2.3 配置文件

application.properties中配置数据库和RabbitMQ的连接信息:

# Database configuration
spring.datasource.url=jdbc:mysql://localhost:3306/ecommerce?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# JPA configuration
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# RabbitMQ configuration
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

三、数据库设计

3.1 商品表(product)

字段名类型描述
idbigint商品ID,主键
namevarchar(255)商品名称
pricedecimal(10, 2)商品价格
stockint商品库存

3.2 订单表(order)

字段名类型描述
idbigint订单ID,主键
product_idbigint商品ID,外键关联product表的id
quantityint订单数量
statusvarchar(20)订单状态,如待支付、已支付、已取消等

3.3 创建实体类

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    private int stock;

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getStock() {
        return stock;
    }

    public void setStock(int stock) {
        this.stock = stock;
    }
}
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long productId;
    private int quantity;
    private String status;

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getProductId() {
        return productId;
    }

    public void setProductId(Long productId) {
        this.productId = productId;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }
}

四、库存扣减的同步处理

4.1 编写Service层

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository;

    @Transactional
    public boolean reduceStock(Long productId, int quantity) {
        Optional<Product> optionalProduct = productRepository.findById(productId);
        if (optionalProduct.isPresent()) {
            Product product = optionalProduct.get();
            if (product.getStock() >= quantity) {
                product.setStock(product.getStock() - quantity);
                productRepository.save(product);
                return true;
            }
        }
        return false;
    }
}

4.2 编写Controller层

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProductController {
    @Autowired
    private ProductService productService;

    @PostMapping("/products/{productId}/reduce-stock")
    public String reduceStock(@PathVariable Long productId, @RequestParam int quantity) {
        boolean result = productService.reduceStock(productId, quantity);
        if (result) {
            return "库存扣减成功";
        } else {
            return "库存不足,扣减失败";
        }
    }
}

4.3 同步处理的问题

在高并发场景下,同步处理库存扣减会导致系统吞吐量下降,因为每个请求都需要等待数据库操作完成才能返回结果。而且,由于数据库的锁机制,多个请求同时对同一商品的库存进行扣减时,会出现锁竞争问题,进一步降低系统性能。

五、引入消息队列进行异步处理

5.1 配置RabbitMQ

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {
    public static final String STOCK_QUEUE = "stock_queue";

    @Bean
    public Queue stockQueue() {
        return new Queue(STOCK_QUEUE, true);
    }
}

5.2 发送消息到队列

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MessageSenderService {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendStockMessage(Long productId, int quantity) {
        String message = productId + "," + quantity;
        rabbitTemplate.convertAndSend(RabbitMQConfig.STOCK_QUEUE, message);
    }
}

5.3 修改Controller层

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProductController {
    @Autowired
    private MessageSenderService messageSenderService;

    @PostMapping("/products/{productId}/reduce-stock")
    public String reduceStock(@PathVariable Long productId, @RequestParam int quantity) {
        messageSenderService.sendStockMessage(productId, quantity);
        return "库存扣减请求已发送,请稍后查看结果";
    }
}

5.4 消费消息并扣减库存

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MessageReceiverService {
    @Autowired
    private ProductService productService;

    @RabbitListener(queues = RabbitMQConfig.STOCK_QUEUE)
    public void receiveStockMessage(String message) {
        String[] parts = message.split(",");
        Long productId = Long.parseLong(parts[0]);
        int quantity = Integer.parseInt(parts[1]);
        productService.reduceStock(productId, quantity);
    }
}

六、解决超卖问题

6.1 数据库乐观锁

在商品表中添加一个version字段,用于实现乐观锁。

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Version;

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;
    private int stock;
    @Version
    private int version;

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getStock() {
        return stock;
    }

    public void setStock(int stock) {
        this.stock = stock;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }
}

修改ProductServicereduceStock方法:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@Service
public class ProductService {
    @Autowired
    private ProductRepository productRepository;

    @Transactional
    public boolean reduceStock(Long productId, int quantity) {
        Optional<Product> optionalProduct = productRepository.findById(productId);
        if (optionalProduct.isPresent()) {
            Product product = optionalProduct.get();
            if (product.getStock() >= quantity) {
                int rows = productRepository.reduceStock(productId, quantity, product.getVersion());
                return rows > 0;
            }
        }
        return false;
    }
}

ProductRepository中添加自定义方法:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;

public interface ProductRepository extends JpaRepository<Product, Long> {
    @Modifying
    @Transactional
    @Query("UPDATE Product p SET p.stock = p.stock - ?2, p.version = p.version + 1 WHERE p.id = ?1 AND p.version = ?3 AND p.stock >= ?2")
    int reduceStock(Long productId, int quantity, int version);
}

七、总结

通过引入消息队列和数据库乐观锁,我们可以有效地解决高并发场景下的库存扣减问题,提高系统的吞吐量和稳定性,避免超卖现象的发生。在实际项目中,还可以根据业务需求对系统进行进一步的优化和扩展,如使用分布式缓存、限流、熔断等技术。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

fanxbl957

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

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

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

打赏作者

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

抵扣说明:

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

余额充值