10万QPS高并发场景下的防重复下单系统设计
面对高并发请求,如何构建稳健的防重复下单系统?本文从前端到后端,为您详细解析一套完整的技术解决方案。
前端拦截:第一道防线
在高并发场景下,前端拦截是保护系统的第一道防线,能有效减少无效请求对后端的冲击。
1. 按钮置灰策略
用户疯狂点击是常见现象,通过简单的按钮置灰可以防止重复请求:
const submitButton = document.getElementById('submit-order');
submitButton.disabled = true; // 禁用按钮
// 提交订单的异步操作
submitOrder().then(response => {
// 请求完成后恢复按钮
submitButton.disabled = false;
}).catch(error => {
// 请求失败也恢复按钮
submitButton.disabled = false;
});
更友好的设计可以添加提示文案,如:"已经收到你的请求,请耐心等待抢购结果"。
2. Token机制防重复
前端加载页面时获取全局唯一Token(如UUID),每次请求附带此Token:
// 页面加载时生成Token
let requestToken = generateUUID();
// 提交请求时携带Token
function submitOrder() {
return fetch('/api/order/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Request-Token': requestToken
},
body: JSON.stringify(orderData)
});
}
后端验证Token唯一性,重复Token直接拒绝请求。
后端防护:多层次防御体系
1. Nginx限流配置
在流量入口处实施限流,保护后端系统:
http {
# 基础版:IP限制
limit_req_zone $binary_remote_addr zone=order_base:10m rate=3r/m;
# 增强版:IP+用户ID双因子限流
limit_req_zone $binary_remote_addr$http_user_id zone=order_enhanced:20m rate=5r/m;
server {
listen 80;
server_name yourdomain.com;
# 订单提交接口
location = /v1/order/create {
# 启用限流
limit_req zone=order_enhanced burst=3 nodelay;
# 返回429时强制JSON响应
error_page 429 @toofast;
location @toofast {
default_type application/json;
return 200 '{"code":429,"msg":"操作过于频繁,请稍后再试"}';
}
# 反向代理到业务服务器
proxy_pass http://order_backend;
}
}
}
2. 网关层防护
网关层可实施更复杂的防护策略:
// 伪代码:网关过滤器校验Token
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String token = request.getHeaders().getFirst("X-Request-Token");
if (StringUtils.isEmpty(token)) {
return writeErrorResponse(exchange, "缺少请求Token");
}
// 检查Redis中是否存在该Token
if (redisTemplate.hasKey(token)) {
return writeErrorResponse(exchange, "重复请求");
} else {
// 设置Token,有效期60秒
redisTemplate.opsForValue().set(token, "1", Duration.ofSeconds(60));
return chain.filter(exchange);
}
}
网关层还可实现:
- 令牌桶算法限流
- 用户维度并发控制
- 请求排队与削峰填谷
3. 幂等设计保障
下单接口必须实现幂等性,常用方案:
数据库唯一索引:
CREATE TABLE orders (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
order_no VARCHAR(64) UNIQUE, -- 唯一订单号
user_id BIGINT,
-- 其他字段...
version INT DEFAULT 0 -- 乐观锁版本号
);
幂等处理逻辑:
@Transactional
public OrderResult createOrder(OrderRequest request) {
// 生成唯一订单号:用户ID+商品ID+时间戳
String orderNo = generateOrderNo(request.getUserId(), request.getProductId());
// 检查订单是否已存在
Order existingOrder = orderDao.findByOrderNo(orderNo);
if (existingOrder != null) {
return OrderResult.existingOrder(existingOrder);
}
// 创建新订单
Order newOrder = buildOrder(request, orderNo);
return OrderResult.success(orderDao.save(newOrder));
}
4. 分布式锁控制
防止同一用户同时创建多个订单:
public OrderResult createOrderWithLock(OrderRequest request) {
String lockKey = "order:lock:" + request.getUserId();
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试获取锁,等待时间0,持有时间30秒
if (lock.tryLock(0, 30, TimeUnit.SECONDS)) {
return createOrder(request);
} else {
throw new BusinessException("系统繁忙,请稍后再试");
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
5. 乐观锁兜底方案
当分布式锁失效时,数据库乐观锁提供最后保障:
@Transactional
public boolean updateOrderStatus(Long orderId, Integer oldStatus, Integer newStatus) {
String sql = "UPDATE orders SET status = ?, version = version + 1 " +
"WHERE id = ? AND status = ? AND version = ?";
int affectedRows = jdbcTemplate.update(
sql, newStatus, orderId, oldStatus, oldVersion);
return affectedRows > 0;
}
监控与运维保障
1. 全链路日志追踪
使用MDC(Mapped Diagnostic Context)实现请求追踪:
public class LogFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
String traceId = UUID.randomUUID().toString();
MDC.put("traceId", traceId);
try {
chain.doFilter(request, response);
} finally {
MDC.clear();
}
}
}
2. 关键指标监控
监控系统关键指标,及时发现异常:
- QPS/TPS变化趋势
- 接口响应时间分布
- 错误率与异常统计
- 系统资源使用情况
3. 数据核对机制
建立定期数据核对任务,确保数据一致性:
@Component
public class OrderCheckTask {
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点执行
public void checkOrderData() {
// 核对订单与支付数据
List<Order> orders = orderDao.findUncheckedOrders();
for (Order order : orders) {
Payment payment = paymentDao.findByOrderNo(order.getOrderNo());
if (payment == null || !payment.getStatus().equals(PAY_SUCCESS)) {
alertService.sendAlert("订单数据异常: " + order.getOrderNo());
}
}
}
}
总结
面对10万QPS的高并发场景,防重复下单系统需要从前端到后端构建多层次、全方位的防护体系:
- 前端层面:通过按钮置灰和Token机制减少无效请求
- 接入层:利用Nginx限流过滤异常流量
- 网关层:实现请求校验、限流和排队机制
- 业务层:采用幂等设计、分布式锁和乐观锁保证数据一致性
- 监控层:建立全链路追踪、指标监控和数据核对机制
这套方案不仅能有效防止重复下单,还能提升系统整体的稳定性和可维护性,为高并发场景下的电商系统提供可靠保障。
注意事项:
- 根据实际业务场景调整限流阈值
- 分布式锁的超时时间需要仔细权衡
- 定期演练容灾方案,确保系统高可用
- 监控告警需要设置合理的阈值,避免误报和漏报
通过以上措施,可以构建一个既能应对高并发挑战,又能保证数据一致性的稳健系统。