1. SSM电商平台架构解析
作为从业十年的Java全栈工程师,我见证过太多电商项目从零到百万级流量的演进过程。今天要分享的这套基于SSM(Spring+SpringMVC+MyBatis)的电商解决方案,是我在多个生产环境验证过的成熟架构。不同于教科书式的框架堆砌,我将重点揭示每个技术选型背后的实战考量。
Spring框架的依赖注入(DI)机制是这个系统的基石。在商品模块中,我们通过@Autowired注解将库存服务注入订单服务,这种松耦合设计使得去年双十一大促时,我们能快速替换库存服务的实现类而不影响订单核心链路。而Spring AOP实现的日志切面,在排查一个诡异的优惠券并发问题时,帮我们精准定位到了方法调用时序异常。
SpringMVC的拦截器链是我们处理电商敏感操作的利器。举个例子,支付回调接口配置了签名验证拦截器,其执行顺序严格遵循:参数解密->风控检查->幂等校验。这种清晰的层次划分,让团队新成员也能快速理解支付流程的安全防护体系。
MyBatis的动态SQL能力在复杂查询场景大放异彩。商家后台的多条件商品筛选接口,我们使用
2. 核心模块实现细节
2.1 用户认证体系设计
JWT+Redis的双令牌方案是我们的首选。access_token(30分钟过期)和refresh_token(7天过期)的分离设计,既保证了安全性又避免频繁登录。关键代码片段:
java复制public LoginResponse login(String username, String password) {
// 密码加盐验证
User user = userService.authenticate(username, password);
String accessToken = Jwts.builder()
.setSubject(user.getId())
.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
String refreshToken = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(
"refresh:" + user.getId(),
refreshToken,
7,
TimeUnit.DAYS);
return new LoginResponse(accessToken, refreshToken);
}
重要安全提示:务必对密码进行BCrypt加密存储,MD5/SHA-1等算法早已不安全
2.2 高并发库存管理
秒杀场景下的库存扣减是个经典难题。我们的解决方案是:Redis预减库存+MQ异步落库+库存分段锁。核心流程:
- 活动开始前通过Lua脚本将库存加载到Redis
- 下单时先执行DECR原子操作
- 通过RocketMQ消息持久化库存变更
- 数据库采用分段锁(如将商品ID哈希分片)降低冲突
sql复制/* 库存表设计关键字段 */
CREATE TABLE `inventory` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`product_id` bigint(20) NOT NULL COMMENT '分段ID=原商品ID%16',
`stock` int(11) NOT NULL DEFAULT '0',
`lock_stock` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_product` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 支付系统集成实践
3.1 多渠道支付适配
采用策略模式封装不同支付渠道的差异。定义PaymentStrategy接口后,各渠道实现如下:
java复制public interface PaymentStrategy {
PaymentResult pay(PaymentRequest request);
}
@Service("wechatPayment")
public class WechatPayment implements PaymentStrategy {
@Override
public PaymentResult pay(PaymentRequest request) {
// 微信支付特有逻辑
}
}
@Service
public class PaymentContext {
@Autowired
private Map<String, PaymentStrategy> strategies;
public PaymentResult execute(String channel, PaymentRequest request) {
return strategies.get(channel + "Payment").pay(request);
}
}
这种设计让新增支付渠道只需实现新策略类,无需修改现有代码。
3.2 分布式事务处理
订单支付涉及多个子系统,我们采用本地消息表+最大努力通知方案:
- 创建订单时在transaction表记录事务状态
- 支付成功后更新订单状态并发送MQ事件
- 定时任务补偿未完成的事务
java复制@Transactional
public void processPayment(Payment payment) {
// 1. 更新支付记录
paymentDao.updateStatus(payment.getId(), PAID);
// 2. 插入本地事务记录
Transaction tx = new Transaction();
tx.setType("ORDER_PAID");
tx.setBusinessId(payment.getOrderId());
transactionDao.insert(tx);
// 3. 发送MQ消息(可能失败)
mqProducer.send(new PaymentSuccessEvent(payment.getOrderId()));
}
关键经验:补偿任务需要实现幂等性,防止重复处理
4. 性能优化实战记录
4.1 缓存设计陷阱
曾经踩过的坑:商品详情页同时使用Redis缓存和本地缓存导致数据不一致。最终方案:
- 一级缓存:Caffeine(10秒过期)
- 二级缓存:Redis(30分钟过期)
- 更新时采用Delete-Through模式
java复制public ProductDetail getProduct(Long id) {
// 1. 查本地缓存
ProductDetail detail = caffeineCache.get(id);
if (detail != null) return detail;
// 2. 查Redis
detail = redisTemplate.opsForValue().get("product:" + id);
if (detail != null) {
caffeineCache.put(id, detail);
return detail;
}
// 3. 查数据库
detail = productDao.selectDetail(id);
redisTemplate.opsForValue().set(
"product:" + id,
detail,
30,
TimeUnit.MINUTES);
return detail;
}
4.2 SQL优化案例
慢查询日志发现某个订单统计SQL执行需要4.2秒。优化过程:
- 原SQL:多表JOIN+子查询
- 优化方案:
- 建立组合索引(order_time, status)
- 改用预聚合统计表
- 使用EXPLAIN验证执行计划
sql复制/* 优化前 */
SELECT COUNT(*) FROM orders
WHERE status = 'PAID'
AND create_time > DATE_SUB(NOW(), INTERVAL 7 DAY);
/* 优化后 */
SELECT daily_count FROM order_stats
WHERE stat_date BETWEEN ? AND ?;
优化后查询时间降至23ms,内存占用减少82%。
5. 线上问题排查实录
5.1 内存泄漏排查
某次大促后出现OOM报警,排查步骤:
- jmap -histo:live [pid] 查看对象分布
- 发现大量Order对象未被回收
- 追溯代码发现缓存未设置过期时间
- 使用MAT分析dump文件确认引用链
最终发现是促销活动代码中将订单列表存入静态Map导致。教训:永远不要用静态集合做缓存!
5.2 接口超时分析
商品搜索接口偶尔出现3秒超时。通过Arthas排查:
- trace com.example.ProductService search 观察调用链
- 发现Elasticsearch客户端偶尔连接超时
- 调整连接池配置:
yaml复制spring.elasticsearch.rest.connection-timeout: 3s
spring.elasticsearch.rest.read-timeout: 5s
spring.elasticsearch.rest.max-conn-per-route: 10
修改后P99响应时间从2100ms降至450ms。
6. 部署架构演进
从单机部署到K8s集群的演进路线:
- 初期:Nginx+Tomcat单节点
- 成长期:Nginx负载均衡+2台应用服务器
- 成熟期:
- 前端:CDN静态资源+API网关
- 中间层:K8s无状态应用集群
- 数据层:Redis集群+MySQL主从
- 监控:Prometheus+Grafana报警体系
特别提醒:MySQL分库分表要在设计初期预留好路由字段,我们因为早期没考虑分片导致后期改造异常痛苦。