SpringCloud高并发纪念币预约系统架构实战
1. 项目背景与核心价值纪念币预约系统是金融机构和收藏爱好者之间的重要桥梁。每到热门纪念币发行时银行官网总会出现服务器崩溃、页面卡死的情况。去年某生肖纪念币预约首日某大行系统甚至瘫痪了整整3小时导致数百万用户无法正常预约。这种场景正是SpringCloud分布式架构最能发挥价值的领域。我参与开发的这个预约系统采用SpringCloud 2021.0.3版本代号Jubilee构建经过三次纪念币发行实战检验最高承载过单日800万次的预约请求。系统最核心的突破在于通过弹性扩缩容机制在预约开始后的流量洪峰时段能自动将服务实例从10个扩展到200个而预约结束后又会自动缩容到基础规模这样既保证了系统稳定性又节省了60%以上的云服务器成本。2. 系统架构设计解析2.1 微服务拆分策略我们将系统拆分为6个核心微服务用户服务user-service处理注册、登录、实名认证商品服务product-service管理纪念币信息、库存预约服务reserve-service核心业务逻辑订单服务order-service生成预约凭证支付服务payment-service对接银行支付网关通知服务notice-service短信/邮件提醒这种拆分方式遵循了业务能力优先的原则。比如将支付单独拆分为服务是因为各家银行的支付接口差异较大独立部署后可以单独升级维护不影响其他业务。在最近一次对接新银行渠道时我们只重启了payment-service其他服务完全不受影响。2.2 关键技术组件选型注册中心选用Nacos而非Eureka主要考虑三点Nacos 2.0支持长连接服务上下线感知速度比Eureka快3-5秒内置配置中心功能可以统一管理所有微服务的配置中文文档完善社区活跃度高网关采用SpringCloud Gateway而非Zuul性能比Zuul 1.x高50%以上支持异步非阻塞IO模型内置限流过滤器开发更方便在流量控制方面我们组合使用了两种方案Sentinel做接口级限流比如每人每分钟5次请求Nginx做全局限流比如每秒5000次请求3. 核心业务逻辑实现3.1 预约流程设计整个预约过程采用查询-预占-确认的三阶段模式// 伪代码示例 public Result reserve(Long userId, Long productId) { // 第一阶段校验 if(!userService.checkUserQualification(userId)){ return Result.error(用户资格校验失败); } // 第二阶段预占库存 if(!productService.lockStock(productId)){ return Result.error(库存不足); } // 第三阶段生成预约 String orderNo orderService.createOrder(userId, productId); paymentService.createPayment(orderNo); noticeService.sendSMS(userId, 预约成功); return Result.success(orderNo); }这种设计的关键在于productService.lockStock()方法实现了分布式锁采用Redisson的RLock解决并发问题。我们在压测时发现单纯使用Redis的SETNX命令在高并发下会有性能瓶颈最终改用Redisson的看门狗机制将吞吐量提升了8倍。3.2 库存管理方案纪念币库存面临两个特殊挑战全国库存需要分地区配置比如北京分配10万枚上海8万枚要防止超卖又要保证公平性我们的解决方案是使用Redis集群存储库存数据按省份做key分片采用Lua脚本保证原子性操作-- 库存扣减Lua脚本 local key KEYS[1] local change tonumber(ARGV[1]) local current tonumber(redis.call(GET, key)) if current change then return redis.call(DECRBY, key, change) else return -1 end设置库存预警机制当剩余量低于5%时触发报警运营人员可紧急调配4. 高并发优化实践4.1 缓存设计技巧我们采用了三级缓存架构本地缓存Caffeine存储用户基本信息等变化不频繁的数据Redis集群存储库存、预约计数等高频访问数据MySQL最终数据持久化特别要注意缓存击穿问题。当热门纪念币信息缓存失效时我们使用Redis的SETNX实现互斥锁public Product getProduct(Long id) { // 先查缓存 Product product redisTemplate.opsForValue().get(product: id); if (product null) { // 获取分布式锁 String lockKey lock:product: id; boolean locked redisTemplate.opsForValue().setIfAbsent(lockKey, 1, 30, TimeUnit.SECONDS); if (locked) { try { // 查数据库 product productMapper.selectById(id); // 写入缓存 redisTemplate.opsForValue().set(product: id, product, 1, TimeUnit.HOURS); } finally { redisTemplate.delete(lockKey); } } else { // 未获取到锁短暂休眠后重试 Thread.sleep(100); return getProduct(id); } } return product; }4.2 数据库优化MySQL方面我们做了这些优化使用ShardingSphere做水平分表按用户ID尾号分10个表预约记录表添加联合索引user_id product_id配置连接池参数重要spring: datasource: hikari: maximum-pool-size: 20 # 根据压测结果调整 minimum-idle: 5 connection-timeout: 3000 idle-timeout: 600000 max-lifetime: 1800000在最近一次压力测试中这些优化使数据库QPS从1500提升到了8500。5. 运维监控体系5.1 全链路监控我们搭建的监控体系包括Prometheus收集指标Grafana展示DashboardELK收集日志SkyWalking做分布式追踪特别有用的几个监控指标网关响应时间超过500ms报警服务实例CPU使用率超过70%触发扩容Redis内存使用量超过80%报警MySQL活跃连接数超过max_connections的80%报警5.2 弹性扩缩容策略基于K8s的HPA配置示例apiVersion: autoscaling/v2beta2 kind: HorizontalPodAutoscaler metadata: name: reserve-service spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: reserve-service minReplicas: 10 maxReplicas: 200 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60 - type: External external: metric: name: active_users selector: matchLabels: app: reserve-service target: type: AverageValue averageValue: 1000这个配置实现了双重扩缩容触发条件CPU使用率超过60% 或 活跃用户数超过1000。6. 源码解析与使用指南6.1 项目结构说明源码目录结构设计遵循领域驱动设计原则├── coin-reservation │ ├── api-gateway # 网关模块 │ ├── common # 公共组件 │ ├── config-center # Nacos配置 │ ├── monitor # 监控相关 │ ├── service-user # 用户服务 │ ├── service-product # 商品服务 │ ├── service-reserve # 预约服务 │ ├── service-order # 订单服务 │ ├── service-payment # 支付服务 │ └── service-notice # 通知服务每个业务服务都包含以下标准结构├── src/main/java │ ├── controller # 对外接口 │ ├── service # 业务逻辑 │ ├── dao # 数据访问 │ ├── entity # 数据实体 │ ├── dto # 传输对象 │ └── config # 配置类 ├── src/main/resources │ ├── application.yml # 应用配置 │ └── bootstrap.yml # 启动配置6.2 快速启动指南环境准备JDK 17MySQL 8.0Redis 6.2Nacos 2.0.3启动顺序# 1. 启动Nacos sh nacos/bin/startup.sh -m standalone # 2. 启动Redis redis-server /etc/redis.conf # 3. 按顺序启动微服务 nohup java -jar service-registry-1.0.0.jar nohup java -jar api-gateway-1.0.0.jar nohup java -jar service-user-1.0.0.jar # 其他服务同理...重要配置项说明spring: cloud: nacos: discovery: server-addr: 127.0.0.1:8848 sentinel: transport: dashboard: localhost:8080 reservation: limit: per-user: 5 # 每个用户最多预约5枚 per-minute: 3 # 每分钟最多3次请求7. 常见问题解决方案7.1 分布式事务问题在预约支付场景中我们最初使用Seata的AT模式但在高并发下性能较差。后来改为最终一致性本地消息表方案在order_service创建订单时同时往本地消息表插入记录定时任务扫描消息表调用payment_servicepayment_service处理成功后回调确认超过3次失败则人工干预核心代码片段Transactional public void createOrder(OrderDTO orderDTO) { // 1. 创建订单 Order order convertToOrder(orderDTO); orderMapper.insert(order); // 2. 写入本地消息表 TransactionMessage message new TransactionMessage(); message.setContent(JSON.toJSONString(order)); message.setTopic(payment); transactionMessageMapper.insert(message); // 3. 发送MQ消息可选 rocketMQTemplate.send(payment-topic, new GenericMessage(order)); }7.2 热点数据问题对于特别热门的纪念币比如生肖币我们采用以下策略库存数据分片到多个Redis节点前端加入答题验证环节延缓请求速度预约按钮随机延迟显示0-3秒服务端使用令牌桶限流这些措施使得系统在面对某次生肖币预约时虽然QPS达到了12万但核心服务依然保持稳定。8. 性能优化关键指标经过多轮优化后系统的主要性能指标指标项优化前优化后网关平均响应时间320ms85ms最大支持QPS8,00045,000库存查询TPS1,2009,500订单创建耗时(P99)650ms210ms服务启动时间45s18s这些优化主要来自JVM参数调优G1垃圾回收器Redis管道批处理MySQL索引优化Feign连接池配置异步日志记录9. 安全防护措施9.1 防刷单机制我们实现了五层防护设备指纹识别相同设备5分钟内只能预约一次行为验证码滑动拼图点击验证IP频率限制每个IP每分钟20次用户信用分级新用户限额更严格人工审核队列可疑订单进入人工审核9.2 数据加密方案敏感数据采用分级加密用户身份证号AES加密存储银行卡号脱敏后存储只留前4后4位通信数据全链路HTTPS部分敏感字段RSA加密日志数据自动脱敏处理10. 项目演进方向当前系统已经在3家省级银行上线运行接下来的改进计划包括智能预约时段推荐基于用户历史行为分析推荐最佳预约时间区块链存证将预约记录上链增强公信力多级库存联动实现总行-分行-支行的库存动态调配预约画像分析挖掘用户偏好优化纪念币发行策略这套系统经过多次实战检验核心代码已经开源。对于想要学习SpringCloud实战经验的朋友建议重点关注gateway的限流实现、sentinel的熔断配置、以及分布式事务的处理方案这些都是微服务架构中的通用解决方案。