refactor: DuplicateRequestLimiter and ratelimit

This commit is contained in:
laker
2025-04-09 15:03:21 +08:00
parent d66d5d77e1
commit 36edaeaca5
6 changed files with 169 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
package com.laker.admin.module.ext.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import com.laker.admin.module.ext.service.ProductService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "△△△4.电商△△△")
@RequestMapping("/product")
@RestController
public class ProductController {
@Autowired
private ProductService productService;
/**
* 抢购商品
* 运用工具(像 Postman 或者 Apache JMeter模拟多个线程同时访问 /purchase/{productId} 接口。
* 当多个线程同时抢购商品时,就可能出现超卖的情况。
*
* @param productId 商品ID
* @return 抢购结果
*/
@SaIgnore
@GetMapping("/purchase/{productId}")
@Operation(summary = "抢购商品")
public String purchase(@PathVariable Long productId) {
boolean success = productService.purchaseProduct(productId);
if (success) {
return "抢购成功";
} else {
return "库存不足,抢购失败";
}
}
}

View File

@@ -0,0 +1,43 @@
package com.laker.admin.module.ext.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
public class Product {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String name;
private int stock;
public Product() {
}
public Product(String name, int stock) {
this.name = name;
this.stock = stock;
}
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 int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
}

View File

@@ -0,0 +1,11 @@
package com.laker.admin.module.ext.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.laker.admin.module.ext.entity.Product;
import org.apache.ibatis.annotations.Update;
public interface ProductMapper extends BaseMapper<Product> {
// 更新商品库存,防止超卖
@Update("update product set stock = stock - 1 where id = #{productId} and stock > 0")
int purchaseProduct(Long productId);
}

View File

@@ -0,0 +1,36 @@
package com.laker.admin.module.ext.service;
import com.laker.admin.module.ext.entity.Product;
import com.laker.admin.module.ext.mapper.ProductMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class ProductService {
@Autowired
private ProductMapper productMapper;
// @Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
public boolean purchaseProduct(Long productId) {
log.info("开始购买商品商品ID: {}", productId);
// 查询商品库存
Product product = productMapper.selectById(productId);
// 如果商品不存在或者库存不足,返回失败
if (product == null || product.getStock() <= 0) {
log.warn("商品不存在或者库存不足商品ID: {}", productId);
return false;
}
// 更新商品库存,如果库存不足,更新失败
final int updateCount = productMapper.purchaseProduct(productId);
if (updateCount > 0) {
log.info("购买成功商品ID: {}", productId);
return true;
} else {
log.warn("购买失败商品ID: {}", productId);
}
return false;
}
}

View File

@@ -0,0 +1,9 @@
-- 商品表
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL,
`stock` bigint DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO product (id, name, stock) VALUES(1, 'aa', 100);

View File

@@ -0,0 +1,30 @@
package com.laker.admin.example;
import org.junit.jupiter.api.Test;
public class ThreadCounter {
private int count = 0;
@Test
public void testThreadCounter() throws InterruptedException {
Runnable task = new Runnable() {
public void run() {
for (int i = 0; i < 10000; ++i) {
count += 1;
}
}
};
Thread t1 = new Thread(task);
t1.start();
Thread t2 = new Thread(task);
t2.start();
t1.join();
t2.join();
// Assertions.assertThat(count).isEqualTo(20000);
}
}