您好,登录后才能下订单哦!
# 怎么用Java实现秒杀系统
## 1. 秒杀系统核心挑战与技术难点
秒杀系统是典型的高并发场景,需要解决以下核心问题:
1. **瞬时高并发流量**:活动开始瞬间可能产生数十万级QPS
2. **超卖问题**:库存扣减的原子性保证
3. **恶意请求**:黄牛脚本、DDOS攻击等
4. **系统雪崩**:避免单一服务崩溃导致整个系统不可用
5. **数据一致性**:库存、订单、支付的状态同步
## 2. 系统架构设计
### 2.1 分层架构设计
┌───────────────────────────────────┐ │ 客户端层 │ │ (静态资源CDN、请求限流、人机验证) │ └───────────────┬───────────────────┘ │ ┌───────────────▼───────────────────┐ │ 接入层 │ │ (负载均衡、API网关、风控拦截) │ └───────────────┬───────────────────┘ │ ┌───────────────▼───────────────────┐ │ 服务层 │ │ (业务逻辑、分布式事务、缓存) │ └───────────────┬───────────────────┘ │ ┌───────────────▼───────────────────┐ │ 数据层 │ │ (分库分表、读写分离、MQ异步) │ └───────────────────────────────────┘
### 2.2 技术选型建议
| 组件类型 | 推荐方案 |
|----------------|-----------------------------------|
| 开发框架 | Spring Boot + Spring Cloud |
| 缓存 | Redis Cluster + Redisson |
| 消息队列 | RocketMQ/Kafka |
| 数据库 | MySQL + ShardingSphere |
| 限流组件 | Sentinel/Guava RateLimiter |
| 分布式锁 | Redis Lua脚本/Zookeeper |
| 监控系统 | Prometheus + Grafana |
## 3. 核心代码实现
### 3.1 库存预热与缓存
```java
@Service
public class InventoryService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 商品库存预热
public void preheatInventory(Long itemId, int stock) {
String key = "sec_kill:stock:" + itemId;
redisTemplate.opsForValue().set(key, String.valueOf(stock));
// 设置过期时间避免脏数据
redisTemplate.expire(key, 24, TimeUnit.HOURS);
}
// 获取当前缓存库存
public int getCacheStock(Long itemId) {
String key = "sec_kill:stock:" + itemId;
String val = redisTemplate.opsForValue().get(key);
return val == null ? 0 : Integer.parseInt(val);
}
}
public class RedisDistributedLock {
private static final String LOCK_PREFIX = "sec_kill:lock:";
private static final int DEFAULT_EXPIRE = 30; // 秒
public static boolean tryLock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
String result = commands.set(key, requestId,
"NX", "EX", DEFAULT_EXPIRE);
return "OK".equals(result);
});
}
public static boolean releaseLock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
return redisTemplate.execute(
(RedisCallback<Boolean>) connection -> {
Object result = connection.eval(
script.getBytes(),
ReturnType.INTEGER,
1,
(LOCK_PREFIX + lockKey).getBytes(),
requestId.getBytes());
return result.equals(1L);
});
}
}
@RestController
public class SecKillController {
@PostMapping("/seckill")
public Result seckill(@RequestParam Long itemId,
@RequestParam Integer userId) {
// 1. 参数校验
if (itemId == null || userId == null) {
return Result.error("参数错误");
}
// 2. 风控校验(频率限制、黑名单等)
if (!riskCheck(userId)) {
return Result.error("操作频繁");
}
// 3. 内存标记(减少Redis访问)
if (!ItemStockCache.getStatus(itemId)) {
return Result.error("已售罄");
}
// 4. Redis预减库存
Long stock = redisTemplate.opsForValue().decrement(
"sec_kill:stock:" + itemId);
if (stock == null || stock < 0) {
// 恢复库存
redisTemplate.opsForValue().increment(
"sec_kill:stock:" + itemId);
ItemStockCache.setStatus(itemId, false);
return Result.error("已售罄");
}
// 5. 消息队列异步处理
SecKillMessage message = new SecKillMessage(userId, itemId);
rocketMQTemplate.send("sec_kill_queue", message);
return Result.success("排队中");
}
}
@Component
@RocketMQMessageListener(
topic = "sec_kill_queue",
consumerGroup = "sec_kill_group")
public class OrderConsumer implements RocketMQListener<SecKillMessage> {
@Override
public void onMessage(SecKillMessage message) {
try {
// 1. 数据库校验库存
Item item = itemMapper.selectForUpdate(message.getItemId());
if (item.getStock() <= 0) {
return;
}
// 2. 创建订单
Order order = createOrder(message.getUserId(), item);
// 3. 扣减真实库存
int affected = itemMapper.reduceStock(
message.getItemId(), order.getCount());
if (affected == 0) {
throw new RuntimeException("库存不足");
}
// 4. 支付超时处理(定时任务)
delayQueue.add(new PayCheckTask(order.getId()));
} catch (Exception e) {
// 恢复Redis库存
redisTemplate.opsForValue().increment(
"sec_kill:stock:" + message.getItemId());
ItemStockCache.setStatus(message.getItemId(), true);
}
}
}
// 多级缓存示例
public class ItemCache {
@Autowired
private RedisTemplate redisTemplate;
private Cache<Long, Item> localCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
public Item getItem(Long id) {
// 1. 查本地缓存
Item item = localCache.getIfPresent(id);
if (item != null) return item;
// 2. 查Redis
String key = "item:" + id;
item = (Item)redisTemplate.opsForValue().get(key);
if (item != null) {
localCache.put(id, item);
return item;
}
// 3. 查数据库
item = itemMapper.selectById(id);
if (item != null) {
redisTemplate.opsForValue().set(key, item, 1, TimeUnit.HOURS);
localCache.put(id, item);
}
return item;
}
}
// 令牌桶限流实现
public class RateLimiterService {
private final RateLimiter rateLimiter = RateLimiter.create(1000); // QPS=1000
public boolean tryAcquire() {
return rateLimiter.tryAcquire();
}
}
// 分桶库存示例
public class BucketStockService {
public boolean reduceStock(Long itemId) {
// 将库存分为10个桶
String bucketKey = "stock_bucket:" + itemId + ":" +
(ThreadLocalRandom.current().nextInt(10));
Long remain = redisTemplate.execute(
new DefaultRedisScript<>(STOCK_REDUCE_SCRIPT, Long.class),
Collections.singletonList(bucketKey));
return remain != null && remain >= 0;
}
private static final String STOCK_REDUCE_SCRIPT =
"local current = tonumber(redis.call('get', KEYS[1])) " +
"if current > 0 then " +
" redis.call('decr', KEYS[1]) " +
" return 1 " +
"else " +
" return -1 " +
"end";
}
// 滑动窗口限流
public class SlidingWindowLimiter {
private final RedisTemplate<String, String> redisTemplate;
public boolean allowRequest(String key, int windowSize, int maxCount) {
long now = System.currentTimeMillis();
long windowStart = now - windowSize * 1000L;
// 使用Redis ZSET实现滑动窗口
redisTemplate.opsForZSet().add(key, String.valueOf(now), now);
redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
Long count = redisTemplate.opsForZSet().zCard(key);
return count != null && count <= maxCount;
}
}
// 熔断降级示例
@RestController
@DefaultProperties(defaultFallback = "defaultFallback")
public class SecKillController {
@HystrixCommand(
commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1000"),
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="50")
},
fallbackMethod = "seckillFallback"
)
@PostMapping("/seckill")
public Result seckill(...) {
// 正常业务逻辑
}
public Result seckillFallback(...) {
return Result.error("系统繁忙,请稍后重试");
}
public Result defaultFallback() {
return Result.error("服务暂时不可用");
}
}
实现高性能秒杀系统需要综合运用多种技术:
扩展方向: - 结合大数据分析用户行为 - 实现动态库存调整 - 搭建智能风控系统 - 探索Serverless架构应用
实际开发中需要根据业务特点进行调整,建议先在小流量场景验证方案可行性,再逐步全量上线。 “`
这篇文章共计约3650字,涵盖了秒杀系统的完整实现方案,从架构设计到核心代码实现,再到优化策略和监控方案。如需进一步扩展某些模块或调整技术方案,可以具体讨论补充。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。