从单机到K8S:一个电商项目的真实架构演进史

2018年夏天,我们团队接手了一个日均订单不足100的小型电商项目。当时整个系统跑在一台4核8G的云服务器上,技术栈是经典的Spring Boot+MySQL组合。谁也没想到,三年后这个系统需要支撑百万级并发,架构也经历了五次重大迭代。今天我就用真实配置片段,还原这段惊心动魄的技术升级之旅。

1. 单机时代的生死时速

最初的架构简单到可以用一张图说明:

[前端] -> [Nginx] -> [Tomcat+Spring Boot] -> [MySQL]

第一个618大促成了架构演进的导火索。当天上午10点流量暴涨后,数据库CPU直接飙到100%,订单提交接口响应时间从200ms恶化到15秒。我们临时做了三件事救火:

  1. 紧急扩容云服务器到8核16G
  2. 在Nginx配置静态资源缓存
    location ~* \.(jpg|css|js)$ {
      expires 7d;
      add_header Cache-Control "public";
    }
    
  3. 给商品表添加了索引
    ALTER TABLE products ADD INDEX idx_category_status (category_id, status);
    

提示:单机架构下,垂直扩容(Scale Up)是最快的应急方案,但成本呈指数级增长

这次事件让我们意识到两个关键问题:

  • 数据库成为明显瓶颈
  • 静态资源消耗了60%的带宽

2. 第一次解耦:应用与数据分离

我们把架构拆分成三个独立部分:

  1. 2台应用服务器(4核8G)
  2. 1台数据库服务器(8核32G,SSD存储)
  3. 1台文件存储服务器(专门存放图片视频)

关键配置变化

  • 应用服务器禁用本地存储
    spring.servlet.multipart.location=/tmp
    file.upload-dir=http://storage-server/uploads
    
  • 数据库连接池调整
    @Bean
    public HikariDataSource dataSource() {
      HikariConfig config = new HikariConfig();
      config.setMaximumPoolSize(20); // 原为50
      config.setConnectionTimeout(30000);
      return new HikariDataSource(config);
    }
    

这个阶段我们收获的最大教训是:网络延迟成为新瓶颈。应用服务器与数据库之间的平均往返时间(RTT)达到3ms,导致复杂查询性能反而下降。

3. 缓存体系的构建

当DAU突破5万时,数据库再次成为瓶颈。我们引入了多级缓存体系:

缓存层级 实现方案 命中率 响应时间
CDN 阿里云CDN 40% <50ms
页面缓存 Redis 30% 2ms
对象缓存 Redis 25% 1ms
数据库 MySQL 5% 10-100ms

最关键的Redis配置:

spring:
  redis:
    lettuce:
      pool:
        max-active: 50
        max-idle: 20
        min-idle: 5
    timeout: 5000
    cache:
      ttl: 1800 # 30分钟过期

缓存带来的性能提升立竿见影:

  • 商品详情页QPS从200提升到1500
  • 数据库负载下降70%

但我们也踩了个大坑:某次大促时Redis内存爆满触发LRU淘汰,导致缓存雪崩。后来通过以下措施解决:

  • 增加内存监控报警
  • 采用多级过期策略
  • 实现缓存降级机制

4. 微服务化改造

当代码库膨胀到50万行时,单体架构的弊端开始显现:

  • 每次发布需要全量部署
  • 局部修改可能影响全局
  • 技术栈无法按需选择

我们按业务域拆分为六个微服务:

  1. 用户中心
  2. 商品服务
  3. 订单服务
  4. 支付服务
  5. 营销服务
  6. 物流服务

服务通信方案对比

方案 协议 性能 复杂度 最终选择
Feign HTTP
gRPC HTTP/2 部分场景
RocketMQ 消息队列 异步 最终一致场景

订单服务的Dubbo配置示例:

<dubbo:service
  interface="com.ec.order.OrderService"
  ref="orderService"
  version="1.0.0"
  timeout="3000"
  retries="2"/>

微服务化过程中最大的挑战是分布式事务。我们最终采用"最终一致性+补偿机制"的方案:

@Transactional
public void createOrder(OrderDTO order) {
  // 1. 本地事务
  orderMapper.insert(order);
  
  // 2. 发送MQ消息
  rocketMQTemplate.asyncSend(
    "order-create-topic", 
    JSON.toJSONString(order),
    new SendCallback() {
      @Override
      public void onSuccess(SendResult sendResult) {
        // 记录发送成功日志
      }
      @Override
      public void onException(Throwable e) {
        // 启动补偿流程
        compensateOrder(order);
      }
    });
}

5. 容器化与K8S落地

随着服务器数量突破50台,运维成本呈指数增长。我们开始向容器化架构迁移:

Docker化改造关键步骤

  1. 编写多阶段构建Dockerfile
    FROM maven:3.8-jdk-11 AS build
    COPY . /app
    RUN mvn -f /app/pom.xml clean package
    
    FROM openjdk:11-jre-slim
    COPY --from=build /app/target/*.jar /app.jar
    ENTRYPOINT ["java","-jar","/app.jar"]
    
  2. 构建镜像并推送到仓库
    docker build -t registry.ec.com/order-service:v1.2.0 .
    docker push registry.ec.com/order-service:v1.2.0
    
  3. 编写K8S部署文件
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: order-service
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: order-service
      template:
        metadata:
          labels:
            app: order-service
        spec:
          containers:
          - name: order-service
            image: registry.ec.com/order-service:v1.2.0
            ports:
            - containerPort: 8080
            resources:
              limits:
                cpu: "1"
                memory: 1Gi
    

K8S带来的核心收益

  • 资源利用率提升40%
  • 部署时间从小时级降到分钟级
  • 自动扩缩容应对流量高峰

在迁移过程中,我们特别注重监控体系的建设。这是Prometheus的监控配置片段:

- job_name: 'order-service'
  metrics_path: '/actuator/prometheus'
  kubernetes_sd_configs:
  - role: pod
  relabel_configs:
  - source_labels: [__meta_kubernetes_pod_label_app]
    action: keep
    regex: order-service

6. 架构演进的经验总结

回顾这段演进历程,有几个关键决策点值得分享:

  1. 技术选型评估矩阵

    • 团队熟悉度(权重30%)
    • 社区活跃度(权重25%)
    • 企业长期需求(权重20%)
    • 学习成本(权重15%)
    • 商业支持(权重10%)
  2. 架构改造的黄金法则

    • 永远先监控再优化
    • 能水平扩展就不垂直扩展
    • 非核心链路先试水新技术
    • 保持架构的可逆性
  3. 性能优化checklist

    • [ ] 数据库查询是否走索引
    • [ ] 缓存命中率是否>90%
    • [ ] 线程池配置是否合理
    • [ ] JVM参数是否优化
    • [ ] 网络延迟是否可控

在容器化过程中,我们最大的收获是建立了完善的CI/CD流水线。这是Jenkinsfile的核心片段:

pipeline {
  agent any
  stages {
    stage('Build') {
      steps {
        sh 'mvn clean package -DskipTests'
      }
    }
    stage('Test') {
      steps {
        parallel(
          "Unit Test": { sh 'mvn test' },
          "Integration Test": { sh 'mvn verify -Pintegration' }
        )
      }
    }
    stage('Deploy') {
      when {
        branch 'main'
      }
      steps {
        sh 'kubectl apply -f k8s/deployment.yaml'
      }
    }
  }
}

架构演进没有终点。现在我们正在探索Service Mesh和Serverless方向,但每次技术升级都遵循一个原则:用最小改动解决当前最主要矛盾

更多推荐