1. 项目概述:当电商系统遇上微服务架构
去年接手的一个手机商城改造项目让我深刻体会到,传统单体架构在应对促销活动时有多么力不从心。这个基于SpringBoot+Vue+SpringCloud的微服务商城系统,通过业务解耦和弹性扩展,成功扛住了双十一期间每秒3000+的订单峰值。不同于传统电商系统将所有功能塞进单个War包,我们将用户服务、商品服务、订单服务等拆分为独立部署的微服务单元,每个服务都有自己的数据库和API网关。
这种架构带来的直接好处是:
- 开发团队可以并行开发不同服务模块
- 单个服务故障不会导致整个系统崩溃
- 可以根据业务压力单独扩展特定服务节点
- 技术栈选择更加灵活(比如推荐服务可以用Python实现)
关键提示:微服务不是银弹,拆分粒度需要根据团队规模和业务复杂度权衡。过细的拆分会导致分布式事务和运维复杂度剧增。
2. 技术栈选型与核心组件
2.1 后端技术矩阵
SpringBoot 2.7 作为各微服务的开发框架,其内嵌Tomcat和约定优于配置的特性大幅简化了服务部署。我们特别看重它的Actuator端点对服务监控的支持,这对后期运维至关重要。
SpringCloud Hoxton.SR12 提供了完整的微服务套件:
- Nacos 作为服务注册中心(替代Eureka),同时承担配置中心职责
- OpenFeign 处理服务间声明式调用
- Hystrix 实现服务熔断降级
- Gateway 替代Zuul作为API网关
数据库方面采用混合策略:
- 用户、订单等强一致性数据用 MySQL 8.0 配合ShardingSphere分库分表
- 商品详情、评价等读多写少数据用 MongoDB 5.0
- 购物车、秒杀库存等高频访问数据用 Redis 6.2 缓存
2.2 前端技术方案
Vue 3 + TypeScript 的组合让前端开发更规范:
- Composition API 提升代码复用性
- Vite 构建工具使热更新速度提升80%
- Pinia 状态管理替代Vuex
- Element Plus UI组件库快速搭建管理后台
特别设计的微前端架构让各业务模块能独立部署:
- 主应用使用qiankun框架
- 商品模块、用户中心等作为子应用单独开发
- 通过nginx路由实现按需加载
3. 核心微服务拆分与实现
3.1 服务边界划分原则
我们根据业务能力而非技术层级进行拆分,每个服务包含完整的垂直业务栈:
| 服务名称 | 职责范围 | 数据存储 | QPS峰值 |
|---|---|---|---|
| 用户服务 | 注册/登录/权限 | MySQL | 1200 |
| 商品服务 | SPU/SKU/库存 | MongoDB | 3500 |
| 订单服务 | 创建/支付/物流 | MySQL+Redis | 2800 |
| 促销服务 | 优惠券/秒杀 | Redis | 5000 |
3.2 典型服务实现示例:订单服务
订单状态机是核心业务逻辑:
java复制// 订单状态枚举
public enum OrderStatus {
UNPAID, PAID, SHIPPED, COMPLETED, CANCELLED
}
// 状态转换处理器
@Service
public class OrderStateMachine {
@Transactional
public Order handleEvent(Order order, OrderEvent event) {
switch (order.getStatus()) {
case UNPAID:
if (event == OrderEvent.PAY_SUCCESS) {
order.setStatus(PAID);
// 触发库存扣减
inventoryClient.deduct(order.getItems());
}
break;
// 其他状态转换...
}
return orderRepository.save(order);
}
}
分布式事务采用Seata的AT模式:
- 全局事务ID通过拦截器自动传递
- 各服务本地事务提交前先写undo_log
- 出现异常时根据undo_log回滚
4. 关键问题解决方案实录
4.1 购物车高并发优化
原始方案直接读写Redis导致性能瓶颈,优化后采用:
- 本地缓存:Guava Cache保存用户最近浏览商品
- 异步合并:将多次加减操作合并为单次请求
- 库存预扣:加入购物车时预占库存5分钟
java复制// 优化后的购物车服务
@RestController
public class CartController {
@Cacheable(value = "userCart", key = "#userId")
public Cart getCart(Long userId) {
// 合并Redis和本地缓存数据
}
@PostMapping("/add")
public void addItem(@RequestBody CartItem item) {
// 发送MQ消息异步更新
rocketMQTemplate.send("cart_update",
MessageBuilder.withPayload(item).build());
}
}
4.2 分布式锁选型对比
我们测试了三种方案后选择Redisson:
| 方案 | 实现方式 | TPS | 缺点 |
|---|---|---|---|
| Redis SETNX | Lua脚本 | 4500 | 锁续期复杂 |
| Zookeeper | 临时节点 | 1800 | 性能瓶颈 |
| Redisson | WatchDog机制 | 6800 | 依赖Redis |
配置示例:
yaml复制redisson:
lock:
watchDogTimeout: 30000
leaseTime: 10000
5. 运维监控体系建设
5.1 立体化监控方案
日志系统:
- ELK收集各服务日志
- 通过log4j2的MDC添加traceId实现链路追踪
指标监控:
- Prometheus采集JVM/DB等指标
- Grafana展示自定义看板
APM工具:
- SkyWalking监控服务拓扑
- 定位慢SQL和接口
5.2 灰度发布策略
通过Gateway实现流量染色:
- 新版本服务部署到K8s但不接入流量
- 网关根据cookie将部分用户路由到新版本
- 监控错误率和性能指标
- 逐步放大流量比例
nginx复制# 网关路由配置示例
spring:
cloud:
gateway:
routes:
- id: canary_route
uri: lb://user-service
predicates:
- Cookie=canary, true
filters:
- RewritePath=/user/(?<segment>.*), /$\{segment}
这个项目让我深刻认识到,微服务改造不仅仅是技术架构升级,更需要配套的DevOps能力和团队协作方式的转变。特别是在事务一致性和链路追踪方面,需要建立完善的开发规范。对于正准备采用微服务的团队,建议先从单体模块化开始,随着业务复杂度提升再逐步拆分,避免过早优化带来的运维负担。