如何设计10万QPS秒杀系统?缓存+消息队列+分布式锁架构实战
🚀 极客小贴士
💡 你知道吗?
想象一下,如果100万人同时冲进一家只有1000个座位的电影院,会发生什么?要么门被挤坏,要么系统崩溃!这就是为什么高并发系统需要"智能排队"、"快速通道"和"安全门禁"的原因!
🎯 从"双11抢购"看高并发架构
还记得去年双11,你熬夜抢iPhone 16 Pro Max的场景吗?零点一到,手指疯狂点击"立即购买",结果页面卡死,刷新后显示"商品已售罄"…
这种"秒杀"场景,就是高并发架构设计的经典案例。今天我们就来聊聊,如何让100万人同时抢购1000台手机,系统还能稳稳当当不崩溃。
📱 项目背景
假设你正在开发一个双11秒杀系统,需要处理以下挑战:
用户量:100万+ 同时在线(相当于一个小城市的人口)
商品:iPhone 16 Pro Max,库存1000台(僧多粥少)
时间:11月11日 00:00:00 准时开抢(生死时速)
要求:系统不能崩溃,用户体验要流畅(不能让大家白熬夜)
🐌 传统架构:像"单通道收费站"
如果使用传统的单体架构,就像在高速公路上只开一个收费通道,会出现什么情况?
// 传统架构的问题代码示例
@RestController
public class SeckillController {
@Autowired
private ProductService productService;
@PostMapping("/seckill")
public Result seckill(@RequestParam Long productId, @RequestParam Long userId) {
// 1. 查询商品库存(数据库查询)
Product product = productService.getById(productId);
if (product.getStock() <= 0) {
return Result.error("商品已售罄");
}
// 2. 扣减库存(数据库更新)
boolean success = productService.reduceStock(productId);
if (!success) {
return Result.error("库存不足");
}
// 3. 创建订单(数据库插入)
Order order = new Order();
order.setProductId(productId);
order.setUserId(userId);
orderService.createOrder(order);
return Result.success("秒杀成功");
}
}
问题分析:
- 数据库瓶颈:100万请求同时查询数据库,就像100万人同时问一个服务员"还有座位吗?",服务员直接累趴下
- 超卖问题:多个线程同时扣减库存,可能出现"卖了1001台手机,但实际只有1000台"的尴尬
- 响应缓慢:每个请求都要等待数据库操作,用户等得花儿都谢了
- 系统雪崩:一个服务崩溃,整个系统就像多米诺骨牌一样倒下
🚀 高并发架构:像"智能交通系统"
使用高并发架构设计,就像把单通道收费站升级为智能交通系统:
// 高并发架构解决方案
@RestController
public class SeckillController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private RabbitTemplate rabbitTemplate;
@PostMapping("/seckill")
public Result seckill(@RequestParam Long productId, @RequestParam Long userId) {
// 1. 分布式锁防止重复提交
String lockKey = "seckill:lock:" + userId + ":" + productId;
Boolean lockAcquired = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", Duration.ofSeconds(30));
if (!lockAcquired) {
return Result.error("请勿重复提交");
}
try {
// 2. 从Redis缓存检查库存
String stockKey = "seckill:stock:" + productId;
Long stock = redisTemplate.opsForValue().decrement(stockKey);
if (stock < 0) {
// 库存不足,回滚
redisTemplate.opsForValue().increment(stockKey);
return Result.error("商品已售罄");
}
// 3. 异步处理订单(消息队列)
SeckillMessage message = new SeckillMessage();
message.setProductId(productId);
message.setUserId(userId);
message.setTimestamp(System.currentTimeMillis());
rabbitTemplate.convertAndSend("seckill.exchange", "seckill.order", message);
return Result.success("秒杀成功,订单处理中...");
} finally {
// 4. 释放分布式锁
redisTemplate.delete(lockKey);
}
}
}
优势分析:
- 缓存加速:Redis缓存库存信息,就像给服务员配了个"智能助手",查询速度提升1000倍
- 异步处理:消息队列异步处理订单,就像"先拿号,后取餐",响应时间从2秒降到50ms
- 分布式锁:防止重复提交和超卖问题,就像"一人一票"的入场券
- 系统解耦:各个服务独立部署,互不影响,就像"各司其职"的专业团队
1. 高并发系统设计原则:从"银行管理"学架构
1.1 CAP理论:分布式系统的"三难选择"
想象一下,你开了一家银行,有三个要求:
- 一致性:所有分行的账本必须完全一致
- 可用性:客户随时都能存取款
- 分区容错性:即使网络断了,银行还能正常营业
CAP理论告诉我们:这三个要求不能同时满足,就像"鱼和熊掌不可兼得"。
TypeError: undefined is not an object (evaluating 't.v')
实际应用场景:
// CP系统示例:强一致性要求(像银行转账)
@Service
public class BankAccountService {
// 银行转账必须保证强一致性,宁可慢一点也不能出错
@Transactional
public void transfer(Long fromAccount, Long toAccount, BigDecimal amount) {
// 必须保证数据一致性,可以牺牲可用性
Account from = accountRepository.findById(fromAccount);
Account to = accountRepository.findById(toAccount);
if (from.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException("余额不足");
}
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
accountRepository.save(from);
accountRepository.save(to);
}
}
// AP系统示例:高可用性要求(像社交媒体)
@Service
public class UserProfileService {
// 用户资料可以最终一致性,但必须高可用(宁可看到旧数据也不能卡死)
public UserProfile getUserProfile(Long userId) {
// 优先从缓存获取,保证高可用
UserProfile profile = redisTemplate.opsForValue().get("user:profile:" + userId);
if (profile == null) {
// 缓存未命中,从数据库获取
profile = userRepository.findById(userId);
redisTemplate.opsForValue().set("user:profile:" + userId, profile, Duration.ofHours(1));
}
return profile;
}
}
1.2 BASE理论:最终一致性(像"外卖配送")
BASE理论是对CAP理论的补充,强调最终一致性,就像外卖配送:
- BA (Basically Available):基本可用(餐厅还在营业,能接单)
- S (Soft State):软状态(订单状态:已下单→制作中→配送中→已送达)
- E (Eventually Consistent):最终一致性(最终你一定能收到外卖)
// BASE理论实践:最终一致性(像外卖订单处理)
@Service
public class OrderService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private RabbitTemplate rabbitTemplate;
public void createOrder(Order order) {
// 1. 立即返回成功(基本可用)- 就像外卖平台立即给你订单号
order.setStatus(OrderStatus.PROCESSING);
orderRepository.save(order);
// 2. 异步处理(软状态)- 订单状态会不断变化
OrderProcessMessage message = new OrderProcessMessage();
message.setOrderId(order.getId());
message.setTimestamp(System.currentTimeMillis());
rabbitTemplate.convertAndSend("order.exchange", "order.process", message);
// 3. 最终一致性:通过消息队列保证数据最终一致(最终订单一定会完成)
}
@RabbitListener(queues = "order.process.queue")
public void processOrder(OrderProcessMessage message) {
try {
// 异步处理订单逻辑
Order order = orderRepository.findById(message.getOrderId());
// 库存扣减
inventoryService.reduceStock(order.getProductId(), order.getQuantity());
// 支付处理
paymentService.processPayment(order);
// 更新订单状态
order.setStatus(OrderStatus.COMPLETED);
orderRepository.save(order);
} catch (Exception e) {
// 处理失败,记录日志,后续重试
log.error("订单处理失败: {}", message.getOrderId(), e);
}
}
}
2. 多级缓存架构设计:像"城市交通系统"
2.1 缓存架构层次
多级缓存架构就像城市交通系统,从高速公路到小区道路,层层递进,让数据"跑"得更快:
TypeError: undefined is not an object (evaluating 't.v')
2.2 本地缓存实现:像"办公桌上的便签"
// 本地缓存实现:Caffeine(像办公桌上的便签,随手就能拿到)
@Configuration
public class CacheConfig {
@Bean
public Cache<String, Object> localCache() {
return Caffeine.newBuilder()
.maximumSize(10000) // 最大缓存数量(便签本最多10000张)
.expireAfterWrite(Duration.ofMinutes(5)) // 写入后5分钟过期(便签5分钟后自动撕掉)
.expireAfterAccess(Duration.ofMinutes(2)) // 访问后2分钟过期(不看就撕掉)
.recordStats() // 开启统计(记录使用情况)
.build();
}
}
@Service
public class ProductService {
@Autowired
private Cache<String, Object> localCache;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductRepository productRepository;
public Product getProduct(Long productId) {
String cacheKey = "product:" + productId;
// 1. 先从本地缓存获取(先看办公桌上的便签)
Product product = (Product) localCache.getIfPresent(cacheKey);
if (product != null) {
log.info("从本地缓存获取商品: {}(便签上找到了!)", productId);
return product;
}
// 2. 本地缓存未命中,从Redis获取(便签没有,去文件柜找)
product = (Product) redisTemplate.opsForValue().get(cacheKey);
if (product != null) {
log.info("从Redis缓存获取商品: {}(文件柜里找到了!)", productId);
// 回填本地缓存(抄一份放到便签上)
localCache.put(cacheKey, product);
return product;
}
// 3. Redis缓存未命中,从数据库获取(文件柜也没有,去档案室找)
product = productRepository.findById(productId);
if (product != null) {
log.info("从数据库获取商品: {}(档案室里找到了!)", productId);
// 回填Redis缓存(复印一份放到文件柜)
redisTemplate.opsForValue().set(cacheKey, product, Duration.ofHours(1));
// 回填本地缓存(再抄一份放到便签上)
localCache.put(cacheKey, product);
}
return product;
}
}
2.3 缓存一致性策略
// 缓存一致性策略:Cache-Aside模式
@Service
public class ProductService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private ProductRepository productRepository;
public void updateProduct(Product product) {
// 1. 先更新数据库
productRepository.save(product);
// 2. 删除缓存(让下次查询时重新加载)
String cacheKey = "product:" + product.getId();
redisTemplate.delete(cacheKey);
// 3. 可选:异步更新缓存
CompletableFuture.runAsync(() -> {
try {
Thread.sleep(100); // 等待数据库事务提交
redisTemplate.opsForValue().set(cacheKey, product, Duration.ofHours(1));
} catch (Exception e) {
log.error("异步更新缓存失败", e);
}
});
}
// 缓存预热:系统启动时预加载热点数据
@PostConstruct
public void warmUpCache() {
List<Product> hotProducts = productRepository.findHotProducts();
for (Product product : hotProducts) {
String cacheKey = "product:" + product.getId();
redisTemplate.opsForValue().set(cacheKey, product, Duration.ofHours(1));
}
log.info("缓存预热完成,预加载{}个商品", hotProducts.size());
}
}
3. 消息队列架构设计:像"智能邮局系统"
3.1 消息队列的作用
消息队列就像智能邮局系统,解决了以下问题:
- 解耦:服务之间不直接调用,通过消息通信(就像写信,不用面对面)
- 削峰:将突发流量平滑处理(就像邮局分时段处理邮件,避免拥堵)
- 异步:提高系统响应速度(就像"先投递,后处理")
- 可靠性:消息持久化,确保不丢失(就像挂号信,有回执保证)
TypeError: undefined is not an object (evaluating 't.v')
3.2 RabbitMQ实战配置
// RabbitMQ配置
@Configuration
@EnableRabbit
public class RabbitMQConfig {
// 秒杀交换机
@Bean
public TopicExchange seckillExchange() {
return new TopicExchange("seckill.exchange", true, false);
}
// 秒杀订单队列
@Bean
public Queue seckillOrderQueue() {
return QueueBuilder.durable("seckill.order.queue")
.withArgument("x-message-ttl", 300000) // 5分钟TTL
.withArgument("x-dead-letter-exchange", "seckill.dlx.exchange")
.withArgument("x-dead-letter-routing-key", "seckill.order.failed")
.build();
}
// 绑定关系
@Bean
public Binding seckillOrderBinding() {
return BindingBuilder.bind(seckillOrderQueue())
.to(seckillExchange())
.with("seckill.order");
}
// 死信交换机
@Bean
public TopicExchange seckillDlxExchange() {
return new TopicExchange("seckill.dlx.exchange", true, false);
}
// 死信队列
@Bean
public Queue seckillDlxQueue() {
return QueueBuilder.durable("seckill.dlx.queue").build();
}
@Bean
public Binding seckillDlxBinding() {
return BindingBuilder.bind(seckillDlxQueue())
.to(seckillDlxExchange())
.with("seckill.order.failed");
}
}
3.3 消息生产者实现
// 消息生产者
@Service
public class SeckillMessageProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendSeckillMessage(SeckillMessage message) {
try {
// 设置消息属性
MessageProperties properties = new MessageProperties();
properties.setDeliveryMode(MessageDeliveryMode.PERSISTENT); // 持久化
properties.setExpiration("300000"); // 5分钟过期
Message rabbitMessage = new Message(JSON.toJSONBytes(message), properties);
// 发送消息
rabbitTemplate.send("seckill.exchange", "seckill.order", rabbitMessage);
log.info("秒杀消息发送成功: userId={}, productId={}",
message.getUserId(), message.getProductId());
} catch (Exception e) {
log.error("秒杀消息发送失败", e);
throw new RuntimeException("消息发送失败", e);
}
}
}
// 消息对象
@Data
public class SeckillMessage {
private Long userId;
private Long productId;
private Long timestamp;
private String requestId; // 用于幂等性控制
}
3.4 消息消费者实现
// 消息消费者
@Component
public class SeckillMessageConsumer {
@Autowired
private OrderService orderService;
@Autowired
private InventoryService inventoryService;
@Autowired
private PaymentService paymentService;
@RabbitListener(queues = "seckill.order.queue")
public void handleSeckillMessage(SeckillMessage message, Channel channel,
@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
try {
log.info("开始处理秒杀消息: userId={}, productId={}",
message.getUserId(), message.getProductId());
// 1. 幂等性检查
if (orderService.isOrderExists(message.getUserId(), message.getProductId())) {
log.info("订单已存在,跳过处理: userId={}, productId={}",
message.getUserId(), message.getProductId());
channel.basicAck(deliveryTag, false);
return;
}
// 2. 创建订单
Order order = orderService.createOrder(message.getUserId(), message.getProductId());
// 3. 扣减库存
boolean stockReduced = inventoryService.reduceStock(message.getProductId(), 1);
if (!stockReduced) {
log.error("库存扣减失败: productId={}", message.getProductId());
channel.basicNack(deliveryTag, false, true); // 重新入队
return;
}
// 4. 处理支付
boolean paymentSuccess = paymentService.processPayment(order);
if (!paymentSuccess) {
log.error("支付处理失败: orderId={}", order.getId());
// 回滚库存
inventoryService.rollbackStock(message.getProductId(), 1);
channel.basicNack(deliveryTag, false, true);
return;
}
// 5. 更新订单状态
order.setStatus(OrderStatus.COMPLETED);
orderService.updateOrder(order);
log.info("秒杀消息处理成功: orderId={}", order.getId());
channel.basicAck(deliveryTag, false);
} catch (Exception e) {
log.error("秒杀消息处理失败", e);
try {
// 处理失败,拒绝消息并重新入队
channel.basicNack(deliveryTag, false, true);
} catch (IOException ioException) {
log.error("消息拒绝失败", ioException);
}
}
}
// 死信队列处理
@RabbitListener(queues = "seckill.dlx.queue")
public void handleFailedMessage(SeckillMessage message) {
log.error("秒杀消息处理失败,进入死信队列: userId={}, productId={}",
message.getUserId(), message.getProductId());
// 可以在这里进行补偿操作,比如发送通知、记录日志等
notificationService.sendFailedNotification(message);
}
}
4. 分布式锁架构设计:像"银行金库的钥匙管理"
4.1 分布式锁的实现方案对比
分布式锁就像银行金库的钥匙管理,确保同一时间只有一个"钥匙持有者",防止"多人同时开锁"的混乱:
实现方案 | 优点 | 缺点 | 适用场景 | 生活比喻 |
---|---|---|---|---|
Redis | 性能高、实现简单 | 单点故障、数据丢失 | 高并发、对一致性要求不高 | 像"智能门锁",快但可能故障 |
Zookeeper | 强一致性、可靠性高 | 性能较低、实现复杂 | 对一致性要求高 | 像"银行金库锁",安全但慢 |
数据库 | 实现简单、可靠性高 | 性能低、容易死锁 | 并发量不大、简单场景 | 像"传统门锁",简单但容易卡住 |
TypeError: undefined is not an object (evaluating 't.v')
4.2 Redis分布式锁实现
// Redis分布式锁实现
@Component
public class RedisDistributedLock {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String LOCK_PREFIX = "lock:";
private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) " +
"else return 0 end";
/**
* 获取分布式锁
* @param lockKey 锁的key
* @param requestId 请求ID,用于标识锁的持有者
* @param expireTime 过期时间(毫秒)
* @return 是否获取成功
*/
public boolean tryLock(String lockKey, String requestId, long expireTime) {
String key = LOCK_PREFIX + lockKey;
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(key, requestId, Duration.ofMillis(expireTime));
return Boolean.TRUE.equals(success);
}
/**
* 释放分布式锁
* @param lockKey 锁的key
* @param requestId 请求ID
* @return 是否释放成功
*/
public boolean unlock(String lockKey, String requestId) {
String key = LOCK_PREFIX + lockKey;
DefaultRedisScript<Long> script = new DefaultRedisScript<>();
script.setScriptText(UNLOCK_SCRIPT);
script.setResultType(Long.class);
Long result = redisTemplate.execute(script,
Collections.singletonList(key), requestId);
return result != null && result == 1L;
}
/**
* 带重试的锁获取
* @param lockKey 锁的key
* @param requestId 请求ID
* @param expireTime 过期时间
* @param retryTimes 重试次数
* @param retryInterval 重试间隔(毫秒)
* @return 是否获取成功
*/
public boolean tryLockWithRetry(String lockKey, String requestId,
long expireTime, int retryTimes, long retryInterval) {
for (int i = 0; i < retryTimes; i++) {
if (tryLock(lockKey, requestId, expireTime)) {
return true;
}
try {
Thread.sleep(retryInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
return false;
}
}
4.3 Redisson分布式锁实现
// Redisson分布式锁实现(推荐)
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://localhost:6379")
.setDatabase(0)
.setConnectionPoolSize(10)
.setConnectionMinimumIdleSize(5);
return Redisson.create(config);
}
}
@Service
public class SeckillService {
@Autowired
private RedissonClient redissonClient;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public Result seckill(Long productId, Long userId) {
String lockKey = "seckill:lock:" + productId;
String requestId = UUID.randomUUID().toString();
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试获取锁,最多等待3秒,锁自动释放时间10秒
boolean lockAcquired = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (!lockAcquired) {
return Result.error("系统繁忙,请稍后重试");
}
// 执行业务逻辑
return doSeckill(productId, userId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return Result.error("系统异常");
} finally {
// 释放锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
private Result doSeckill(Long productId, Long userId) {
// 1. 检查库存
String stockKey = "seckill:stock:" + productId;
Long stock = redisTemplate.opsForValue().decrement(stockKey);
if (stock < 0) {
// 库存不足,回滚
redisTemplate.opsForValue().increment(stockKey);
return Result.error("商品已售罄");
}
// 2. 检查用户是否已经购买过
String userKey = "seckill:user:" + productId;
Boolean hasBought = redisTemplate.opsForSet().isMember(userKey, userId);
if (Boolean.TRUE.equals(hasBought)) {
// 用户已购买,回滚库存
redisTemplate.opsForValue().increment(stockKey);
return Result.error("您已经购买过该商品");
}
// 3. 记录用户购买
redisTemplate.opsForSet().add(userKey, userId);
redisTemplate.expire(userKey, Duration.ofHours(1));
// 4. 异步处理订单
SeckillMessage message = new SeckillMessage();
message.setProductId(productId);
message.setUserId(userId);
message.setRequestId(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend("seckill.exchange", "seckill.order", message);
return Result.success("秒杀成功,订单处理中...");
}
}
5. 秒杀系统完整架构实战:从"双11抢购"到"系统稳定"
5.1 系统架构图:像"智能购物中心"
TypeError: undefined is not an object (evaluating 't.v')
5.2 核心代码实现
// 秒杀控制器
@RestController
@RequestMapping("/seckill")
public class SeckillController {
@Autowired
private SeckillService seckillService;
@PostMapping("/{productId}")
public Result seckill(@PathVariable Long productId,
@RequestParam Long userId) {
try {
// 参数校验
if (productId == null || userId == null) {
return Result.error("参数不能为空");
}
// 执行秒杀
return seckillService.seckill(productId, userId);
} catch (Exception e) {
log.error("秒杀异常: productId={}, userId={}", productId, userId, e);
return Result.error("系统异常,请稍后重试");
}
}
@GetMapping("/stock/{productId}")
public Result getStock(@PathVariable Long productId) {
try {
Long stock = seckillService.getStock(productId);
return Result.success(stock);
} catch (Exception e) {
log.error("查询库存异常: productId={}", productId, e);
return Result.error("查询失败");
}
}
}
// 秒杀服务
@Service
public class SeckillService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private RedissonClient redissonClient;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private ProductRepository productRepository;
public Result seckill(Long productId, Long userId) {
// 1. 预检查:商品是否存在且可秒杀
Product product = productRepository.findById(productId);
if (product == null || !product.isSeckillEnabled()) {
return Result.error("商品不存在或未开启秒杀");
}
// 2. 预检查:用户是否已经购买过
String userKey = "seckill:user:" + productId;
Boolean hasBought = redisTemplate.opsForSet().isMember(userKey, userId);
if (Boolean.TRUE.equals(hasBought)) {
return Result.error("您已经购买过该商品");
}
// 3. 预检查:库存是否充足
String stockKey = "seckill:stock:" + productId;
Long stock = (Long) redisTemplate.opsForValue().get(stockKey);
if (stock == null || stock <= 0) {
return Result.error("商品已售罄");
}
// 4. 分布式锁控制并发
String lockKey = "seckill:lock:" + productId;
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试获取锁,最多等待100ms,锁自动释放时间5秒
boolean lockAcquired = lock.tryLock(100, 5000, TimeUnit.MILLISECONDS);
if (!lockAcquired) {
return Result.error("系统繁忙,请稍后重试");
}
// 5. 双重检查:再次检查库存
stock = redisTemplate.opsForValue().decrement(stockKey);
if (stock < 0) {
// 库存不足,回滚
redisTemplate.opsForValue().increment(stockKey);
return Result.error("商品已售罄");
}
// 6. 记录用户购买
redisTemplate.opsForSet().add(userKey, userId);
redisTemplate.expire(userKey, Duration.ofHours(1));
// 7. 异步处理订单
SeckillMessage message = new SeckillMessage();
message.setProductId(productId);
message.setUserId(userId);
message.setRequestId(UUID.randomUUID().toString());
message.setTimestamp(System.currentTimeMillis());
rabbitTemplate.convertAndSend("seckill.exchange", "seckill.order", message);
return Result.success("秒杀成功,订单处理中...");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return Result.error("系统异常");
} finally {
// 释放锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
public Long getStock(Long productId) {
String stockKey = "seckill:stock:" + productId;
Long stock = (Long) redisTemplate.opsForValue().get(stockKey);
return stock != null ? stock : 0L;
}
}
5.3 性能优化配置
# application.yml
spring:
redis:
cluster:
nodes:
- 192.168.1.100:7000
- 192.168.1.100:7001
- 192.168.1.100:7002
- 192.168.1.101:7000
- 192.168.1.101:7001
- 192.168.1.101:7002
max-redirects: 3
lettuce:
pool:
max-active: 20
max-idle: 10
min-idle: 5
max-wait: 3000ms
timeout: 5000ms
rabbitmq:
host: 192.168.1.100
port: 5672
username: admin
password: admin
virtual-host: /
connection-timeout: 15000
publisher-confirm-type: correlated
publisher-returns: true
listener:
simple:
acknowledge-mode: manual
concurrency: 10
max-concurrency: 20
prefetch: 1
# 线程池配置
seckill:
thread-pool:
core-size: 20
max-size: 100
queue-capacity: 1000
keep-alive: 60s
thread-name-prefix: seckill-
// 线程池配置
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Bean("seckillExecutor")
public ThreadPoolTaskExecutor seckillExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(20);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(1000);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("seckill-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
6. 监控与运维
6.1 关键指标监控
// 监控指标收集
@Component
public class SeckillMetrics {
private final MeterRegistry meterRegistry;
private final Counter seckillSuccessCounter;
private final Counter seckillFailCounter;
private final Timer seckillTimer;
private final Gauge stockGauge;
public SeckillMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.seckillSuccessCounter = Counter.builder("seckill.success")
.description("秒杀成功次数")
.register(meterRegistry);
this.seckillFailCounter = Counter.builder("seckill.fail")
.description("秒杀失败次数")
.register(meterRegistry);
this.seckillTimer = Timer.builder("seckill.duration")
.description("秒杀处理时间")
.register(meterRegistry);
this.stockGauge = Gauge.builder("seckill.stock")
.description("商品库存")
.register(meterRegistry, this, SeckillMetrics::getCurrentStock);
}
public void recordSeckillSuccess() {
seckillSuccessCounter.increment();
}
public void recordSeckillFail() {
seckillFailCounter.increment();
}
public void recordSeckillDuration(Duration duration) {
seckillTimer.record(duration);
}
private double getCurrentStock() {
// 返回当前库存数量
return 0.0; // 实际实现中从Redis获取
}
}
6.2 健康检查
// 健康检查
@Component
public class SeckillHealthIndicator implements HealthIndicator {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private RabbitTemplate rabbitTemplate;
@Override
public Health health() {
try {
// 检查Redis连接
redisTemplate.opsForValue().get("health:check");
// 检查RabbitMQ连接
rabbitTemplate.convertAndSend("health.exchange", "health.check", "ping");
return Health.up()
.withDetail("redis", "连接正常")
.withDetail("rabbitmq", "连接正常")
.withDetail("timestamp", System.currentTimeMillis())
.build();
} catch (Exception e) {
return Health.down()
.withDetail("error", e.getMessage())
.withDetail("timestamp", System.currentTimeMillis())
.build();
}
}
}
7. 最佳实践总结:从"菜鸟"到"架构师"的成长之路
7.1 高并发系统设计原则:像"盖房子"一样设计系统
- 分层架构:Web层、服务层、数据层分离(就像房子的地基、主体、装修)
- 缓存优先:多级缓存,减少数据库压力(就像在门口放个快递柜,不用每次都敲门)
- 异步处理:消息队列解耦,提高响应速度(就像"先下单,后制作")
- 分布式锁:保证数据一致性(就像"一人一票"的入场券)
- 监控告警:实时监控系统状态(就像房子的"智能监控系统")
7.2 常见问题与解决方案
问题 | 原因 | 解决方案 | 生活比喻 |
---|---|---|---|
超卖问题 | 并发扣减库存 | 分布式锁 + Redis原子操作 | 像"限流门",一次只能进一个人 |
重复下单 | 用户重复提交 | 分布式锁 + 幂等性控制 | 像"防重复按钮",按一次就够了 |
系统雪崩 | 服务依赖过多 | 熔断器 + 降级策略 | 像"保险丝",一个坏了不影响其他 |
数据不一致 | 缓存与数据库不同步 | 缓存更新策略 + 最终一致性 | 像"同步更新",确保信息一致 |
7.3 性能优化建议:像"老司机"一样优化系统
// 性能优化示例(像老司机开车,既快又稳)
@Service
public class OptimizedSeckillService {
// 1. 使用本地缓存减少Redis访问(像在车里放个水杯,不用每次都去服务站)
@Autowired
private Cache<String, Object> localCache;
// 2. 批量处理减少网络开销(像拼车,一次拉多人)
public void batchProcessOrders(List<SeckillMessage> messages) {
// 批量处理逻辑
}
// 3. 连接池优化(像停车场,提前规划好车位)
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(20); // 最大连接数(最多20个车位)
poolConfig.setMaxIdle(10); // 最大空闲连接(平时保持10个车位)
poolConfig.setMinIdle(5); // 最小空闲连接(至少5个车位)
poolConfig.setMaxWaitMillis(3000); // 最大等待时间(最多等3秒)
return new LettuceConnectionFactory(redisClusterConfiguration(), poolConfig);
}
// 4. 异步处理优化(像外卖配送,不用等,先下单后处理)
@Async("seckillExecutor")
public CompletableFuture<Void> asyncProcessOrder(SeckillMessage message) {
// 异步处理逻辑
return CompletableFuture.completedFuture(null);
}
}
🎯 总结:从"双11抢购"到"系统架构师"
通过本文的学习,我们就像从"双11抢购的用户"变成了"设计抢购系统的架构师",掌握了高并发架构设计的核心要点:
- 系统设计原则:CAP理论、BASE理论指导架构设计(就像盖房子的设计图纸)
- 多级缓存架构:本地缓存 + Redis缓存,提升系统性能(就像在门口放快递柜,不用每次都敲门)
- 消息队列设计:RabbitMQ实现异步处理和解耦(就像智能邮局,先投递后处理)
- 分布式锁实现:Redis和Redisson保证数据一致性(就像银行金库的钥匙管理)
- 秒杀系统实战:完整的架构设计和代码实现(就像从设计到施工的完整流程)
高并发系统设计是一个系统工程,需要从架构设计、技术选型、代码实现、监控运维等多个维度综合考虑。就像盖房子一样,不仅要考虑美观,更要考虑实用性和安全性。只有掌握了这些核心技能,才能设计出真正高性能、高可用的系统。
记住:好的架构师不是天生的,而是在解决一个又一个实际问题中成长起来的!