三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Spring Cloud微服务架构在电商平台重构中的实践

Spring Cloud微服务架构在电商平台重构中的实践

1. 项目概述

去年接手公司电商平台重构项目时,我们面临着一个典型的技术困境:原有单体架构在促销活动期间频繁崩溃,数据库连接池经常被打满。经过技术选型评估,最终决定采用Spring Cloud微服务架构进行改造,其中用户模块和商品模块作为核心业务组件率先完成迁移。这次重构不仅让系统QPS从原来的200提升到5000+,更重要的是建立了可弹性扩展的技术底座。

2. 技术架构设计

2.1 微服务组件选型

在技术栈选择上,我们采用了以下组合方案:

  • 服务注册中心:Nacos(替代原计划的Eureka)
  • API网关:Spring Cloud Gateway
  • 配置中心:Nacos Config
  • 服务调用:OpenFeign + Ribbon
  • 熔断降级:Sentinel
  • 链路追踪:SkyWalking

特别说明:选择Nacos而非Eureka的主要原因是其配置管理一体化特性,实测发现配置变更推送速度比Spring Cloud Config快3倍以上。

2.2 模块划分原则

用户模块与商品模块的拆分遵循了三个核心原则:

  1. 业务高内聚:用户相关操作(注册/登录/信息管理)全部收敛在user-service
  2. 数据独立性:用户基础信息与商品信息分库存储
  3. 性能隔离:将读多写少的商品查询与高频更新的用户行为分离

3. 用户模块实现细节

3.1 关键数据结构设计

用户核心表结构如下:

CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '雪花算法ID', `username` varchar(64) COLLATE utf8mb4_bin NOT NULL, `password` varchar(128) COLLATE utf8mb4_bin NOT NULL COMMENT 'BCrypt加密', `phone` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL, `status` tinyint NOT NULL DEFAULT '1' COMMENT '0-禁用 1-正常', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`), KEY `idx_phone` (`phone`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

3.2 认证授权方案

采用JWT+OAuth2混合方案实现:

  1. 密码加密:BCryptPasswordEncoder(迭代次数设为12)
  2. Token生成:JJWT库生成HS256签名token
  3. 权限控制:基于Spring Security实现RBAC模型

关键配置示例:

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/auth/**").permitAll() .antMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authenticationManager())) .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }

4. 商品模块核心技术

4.1 多级缓存架构

为解决商品详情页的高并发读取问题,设计了四级缓存方案:

缓存层级技术实现命中率响应时间
客户端缓存HTTP Cache-Control30%<10ms
CDN缓存阿里云CDN40%20-50ms
分布式缓存Redis Cluster25%1-3ms
本地缓存Caffeine4%0.1ms

4.2 库存扣减方案

采用Redis+Lua脚本实现原子性扣减:

-- KEYS[1]: 库存key -- ARGV[1]: 扣减数量 local stock = tonumber(redis.call('GET', KEYS[1])) if stock >= tonumber(ARGV[1]) then return redis.call('DECRBY', KEYS[1], ARGV[1]) else return -1 end

配合定时任务进行数据库同步:

@Scheduled(cron = "0 */5 * * * ?") public void syncStockToDB() { // 获取Redis中所有需要同步的商品ID Set<String> keys = redisTemplate.keys("stock:*"); for (String key : keys) { Long productId = Long.parseLong(key.split(":")[1]); Integer stock = Integer.valueOf(redisTemplate.opsForValue().get(key)); productMapper.updateStock(productId, stock); } }

5. 服务通信设计

5.1 Feign最佳实践

定义商品服务接口:

@FeignClient(name = "product-service", configuration = FeignConfig.class, fallbackFactory = ProductClientFallbackFactory.class) public interface ProductClient { @GetMapping("/api/internal/products/{id}") Result<ProductDTO> getProductById(@PathVariable Long id); @PostMapping("/api/internal/products/stock/deduct") Result<Boolean> deductStock(@RequestBody StockDeductDTO dto); }

配置类示例:

public class FeignConfig { @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } @Bean public Retryer retryer() { return new Retryer.Default(100, 1000, 3); } }

5.2 熔断降级策略

Sentinel配置示例:

@PostConstruct public void initFlowRules() { List<FlowRule> rules = new ArrayList<>(); FlowRule rule = new FlowRule(); rule.setResource("getProductById"); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(1000); // 阈值 rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP); rule.setWarmUpPeriodSec(10); // 预热时间 rules.add(rule); FlowRuleManager.loadRules(rules); }

6. 生产环境问题实录

6.1 典型故障排查

问题现象:商品查询接口在晚高峰出现周期性超时

排查过程

  1. 通过SkyWalking发现Gateway到product-service的延迟突增
  2. 检查Redis监控发现CPU使用率超过90%
  3. 分析慢查询日志发现大量KEYS *操作
  4. 定位到某个开发在代码中误用了keys命令扫描

解决方案

  1. 替换所有keys命令为SCAN迭代
  2. 对热点商品增加本地缓存
  3. 调整Redis线程池大小

6.2 性能优化记录

优化前后对比:

指标优化前优化后提升幅度
用户登录RT120ms65ms45%
商品查询QPS8003500337%
订单创建TPS50200300%

关键优化措施:

  1. 引入Caffeine缓存用户权限数据
  2. 商品图片迁移到OSS
  3. 数据库分库分表(用户表按ID哈希,商品表按类目范围)

7. 持续集成方案

7.1 容器化部署

Dockerfile示例:

FROM openjdk:11-jre WORKDIR /app COPY target/user-service-1.0.0.jar ./app.jar ENV JAVA_OPTS="-Xms512m -Xmx512m -XX:MaxMetaspaceSize=256m" EXPOSE 8080 ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar app.jar"]

Kubernetes部署片段:

apiVersion: apps/v1 kind: Deployment metadata: name: user-service spec: replicas: 3 selector: matchLabels: app: user-service template: metadata: labels: app: user-service spec: containers: - name: user-service image: registry.example.com/user-service:1.0.0 ports: - containerPort: 8080 resources: limits: cpu: "1" memory: 1Gi

7.2 监控告警配置

Prometheus监控指标示例:

- job_name: 'user-service' metrics_path: '/actuator/prometheus' static_configs: - targets: ['user-service:8080'] relabel_configs: - source_labels: [__address__] target_label: instance regex: '(.*):\d+' replacement: '$1'

Grafana看板包含的关键图表:

  1. JVM内存使用趋势
  2. 接口成功率热力图
  3. 数据库连接池使用情况
  4. Redis命中率变化曲线

8. 踩坑经验分享

  1. Feign超时陷阱:默认超时设置过短导致偶发失败

    • 解决方案:全局配置超时时间
    feign: client: config: default: connectTimeout: 5000 readTimeout: 10000
  2. Nacos注册延迟:服务下线后客户端感知延迟

    • 解决方案:调整心跳参数
    spring.cloud.nacos.discovery.heart-beat-interval=5s spring.cloud.nacos.discovery.heart-beat-timeout=15s
  3. 分布式事务问题:用户积分与订单状态不一致

    • 最终方案:采用本地消息表+定时任务补偿
  4. 缓存雪崩防护:商品缓存同时失效导致DB压力骤增

    • 实施措施:
      • 设置随机过期时间
      • 使用永不过期的基准数据+增量过期策略
      • 实现缓存重建互斥锁

这个项目让我深刻体会到,微服务架构不是简单的技术堆砌,而是需要根据业务特点进行深度定制。特别是在电商场景下,对一致性和可用性的平衡取舍,往往需要结合具体业务场景做出决策。比如在用户注册流程中,我们最终选择了AP而非CP,允许极端情况下短暂的数据不一致,换取更高的系统可用性。

← 返回列表