Skip to content
第 35 章 架构 ⏱ 12 分钟阅读

第 35 章:架构演进 ​

学习目标 ​

  • 掌握架构演进的 5 个阶段
  • 学会从单体到微服务的演进路径
  • 理解云原生与 Service Mesh 趋势
  • 避免过早演进、过度技术的坑

一、架构演进 5 阶段 ​

text
阶段 1:单体应用
阶段 2:垂直应用
阶段 3:SOA 面向服务
阶段 4:微服务
阶段 5:云原生 / Service Mesh
java
// 阶段 1:单体
com.example.app
├── controller
├── service
└── dao
// 1 个 WAR,1 个数据库

// 阶段 2:垂直(按业务)
com.example.app
├── user-module/
├── order-module/
└── payment-module/
// 多个 WAR,共享数据库

// 阶段 4:微服务
user-service, order-service, payment-service
// 独立部署,独立数据库

二、单体到微服务的演进 ​

text
阶段 1:单体
   ↓ 拆分时机
阶段 2:模块化单体
   - 内部模块化,边界清晰
   - 公共代码收敛
   ↓ 业务压力
阶段 3:独立部署
   - 把用户、认证独立
   - 不引入分布式事务
   ↓ 团队规模
阶段 4:微服务拆分
   - 拆核心业务
   - 引入分布式能力
   ↓ 云原生
阶段 5:Service Mesh
   - 服务网格化
   - 治理与业务分离

⚠️ 坑 1:"上来就拆微服务" → 50% 项目死在该阶段。先模块化单体,再独立部署,最后微服务。

三、模块化单体 ​

java
// 1. 包结构
com.example.ecommerce
├── user/                 // 用户模块
│   ├── UserApplicationService
│   ├── UserDomainService
│   └── UserRepository
├── order/                // 订单模块
│   ├── OrderApplicationService
│   └── OrderRepository
└── shared/               // 共享代码
    └── common/
java
// 2. 模块间调用走接口(便于后续拆服务)
public interface OrderService {
    OrderDto getOrder(Long id);
}

@org.springframework.stereotype.Service
public class OrderServiceImpl implements OrderService {
    public OrderDto getOrder(Long id) {
        return orderRepository.findById(id).orElseThrow();
    }
}

// 3. 编排层(后续可换 RPC)
@Service
public class OrderFacade {
    @Resource private OrderService orderService;
    @Resource private UserService userService;

    public OrderDetail getOrderDetail(Long orderId) {
        OrderDto order = orderService.getOrder(orderId);
        UserDto user = userService.getUser(order.getUserId());
        return new OrderDetail(order, user);
    }
}
java
// 4. 未来拆服务时,接口保留,实现换成 RPC
@org.springframework.stereotype.Service
public class OrderServiceImpl implements OrderService {
    @Resource private OrderClient orderClient;  // Feign 客户端

    public OrderDto getOrder(Long id) {
        return orderClient.getOrder(id);
    }
}
// 上层调用方零改动

四、独立部署 ​

java
// Spring Boot 单 jar 部署
@SpringBootApplication
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

// Dockerfile
FROM eclipse-temurin:17-jre-alpine
COPY target/user-service.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
yaml
# k8s 部署
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: user-service:1.0.0
        resources:
          requests:
            cpu: 100m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 512Mi

五、服务拆分策略 ​

java
// 1. 优先拆:无状态、无分布式事务
user-service, auth-service, notification-service

// 2. 中期:有一定业务复杂度
product-service, shopping-cart-service

// 3. 最后:分布式事务重的
order-service, payment-service, inventory-service
java
// 拆分示意
@FeignClient(name = "user-service")
public interface UserClient {
    @GetMapping("/api/users/{id}")
    UserDto getUser(@PathVariable Long id);
}

@Service
public class OrderService {
    @Resource private UserClient userClient;

    public OrderDetail getDetail(Long orderId) {
        Order order = orderRepository.findById(orderId);
        UserDto user = userClient.getUser(order.getBuyerId());
        return new OrderDetail(order, user);
    }
}

六、Service Mesh ​

text
传统微服务
├── 业务代码(Spring Cloud)
├── 服务发现(Nacos)
├── 熔断(Sentinel)
├── 限流(Sentinel)
└── 监控(自研)

Service Mesh(Istio)
├── 业务代码(只管业务)
├── 服务发现(Envoy)
├── 熔断(Envoy)
├── 限流(Envoy)
└── 监控(Envoy)
yaml
# Istio 限流(代替 Sentinel)
apiVersion: networking.istio.io/v1beta1
kind: EnvoyFilter
metadata:
  name: rate-limit
spec:
  configPatches:
  - applyTo: HTTP_FILTER
    match:
      context: SIDECAR_INBOUND
    patch:
      operation: INSERT_BEFORE
      value:
        name: envoy.filters.http.local_ratelimit
        typed_config:
          "@type": type.googleapis.com/udpa.type.v1.TypedStruct
          type_url: type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
          value:
            stat_prefix: http_local_rate_limiter
            token_bucket:
              max_tokens: 100
              tokens_per_fill: 100
              fill_interval: 60s
text
Service Mesh 优势
├── 业务无侵入
├── 多语言支持
├── 统一治理
└── 边车代理

Service Mesh 代价
├── 性能损耗(每跳 1-2ms)
├── 复杂度上升
└── 排障更难

七、云原生 ​

text
云原生 = 微服务 + 容器 + DevOps + 服务网格 + 不可变基础设施

CNCF Landscape
├── 容器: Docker / Containerd
├── 编排: Kubernetes
├── 服务网格: Istio / Linkerd
├── 监控: Prometheus / Grafana
├── 日志: Loki / Fluentd
├── Tracing: Jaeger / Tempo
├── CI/CD: Argo / Tekton
├── 配置: Consul / etcd
└── 存储: MinIO / Ceph
java
// Kubernetes 核心
// 1. Pod(最小部署单元)
apiVersion: v1
kind: Pod
metadata:
  name: order-service
spec:
  containers:
  - name: order-service
    image: order-service:1.0.0
    ports:
    - containerPort: 8080

// 2. Service(服务发现)
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
  ports:
  - port: 80
    targetPort: 8080

// 3. HPA(自动扩缩容)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: order-service
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-service
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70

八、Serverless ​

java
// AWS Lambda
public class OrderPlacedHandler implements RequestHandler<OrderEvent, String> {
    @Override
    public String handleRequest(OrderEvent event, Context context) {
        // 发送通知
        emailService.send(event.getUserEmail(), "订单已创建");
        return "ok";
    }
}

// 阿里云 FC
public class ImageResizeHandler implements RequestHandler<HttpRequest, HttpResponse> {
    @Override
    public HttpResponse handleRequest(HttpRequest request, Context context) {
        String imageUrl = request.getQueryParameters().get("url");
        byte[] resized = imageService.resize(imageUrl, 200, 200);
        return new HttpResponse(200, new ByteArrayInputStream(resized));
    }
}
text
Serverless 适用
├── 流量波动大
├── 短任务
├── 事件驱动
└── 边缘计算

Serverless 不适用
├── 长任务(> 15 分钟)
├── 强状态
├── 极致性能
└── 复杂业务

⚠️ 坑 2:核心交易链路用 Serverless,冷启动 + 厂商绑定,生产事故频发。Serverless 适合辅助场景(通知、日志)。

九、DDD 与架构演进 ​

text
DDD 战略设计识别边界
├── 限界上下文 = 微服务边界
├── 聚合 = 事务边界
├── 领域事件 = 跨服务通信
└── 统一语言 = 团队沟通
java
// 演进过程中,DDD 一直在
// 阶段 1:单体 + DDD 战术
// 阶段 2:模块化单体 + DDD 战略
// 阶段 3:每个限界上下文 = 一个微服务
// 阶段 4:DevOps 自动化部署

十、架构选型决策 ​

text
决策框架
├── 业务复杂度
│   ├── 简单 → 单体
│   ├── 中等 → 模块化单体
│   └── 复杂 → 微服务
├── 团队规模
│   ├── < 10 → 单体
│   ├── 10-50 → 模块化 + 部分微服务
│   └── 50+ → 微服务
├── 业务稳定性
│   ├── 探索期 → 简单架构
│   └── 成熟期 → 考虑微服务
└── 运维能力
    ├── 无 → 单体
    ├── 基础 → 模块化
    └── 成熟 → 微服务 / 云原生
java
// 决策表
public class ArchitectureDecision {
    public Architecture choose(Requirements req) {
        if (req.teamSize < 10 && req.complexity < 5) {
            return Architecture.MONOLITH;
        }
        if (req.teamSize < 50 && req.complexity < 10) {
            return Architecture.MODULAR_MONOLITH;
        }
        if (req.hasDevOps()) {
            return Architecture.MICROSERVICE;
        }
        return Architecture.MODULAR_MONOLITH;
    }
}

⚠️ 坑 3:"技术越新越好" → 上 K8s + Istio + Knative,3 个月后才上线,业务停滞。架构服务于业务。

十一、演进中的反模式 ​

java
// ❌ 分布式单体:微服务但耦合严重
// 表现:改一个服务,5 个服务要发版
// 原因:服务边界划错、共享数据库
// 解决:重新审视边界,合并过细服务

// ❌ 大泥球:没有清晰架构
// 表现:几千行 Service,代码没注释
// 解决:DDD 重构,引入清晰的领域模型

// ❌ 过度设计:为了 K8s 而 K8s
// 表现:5 个 QPS 的服务,搞容器化 + 服务网格
// 解决:根据业务规模选择技术

十二、架构演进总结 ​

text
关键原则
├── 演进式架构,不要 Big Bang
├── 业务驱动,不要技术驱动
├── 先单体后分布
├── 监控先行,问题驱动
└── 持续迭代,小步快跑
java
// 架构选型伪代码
public Architecture currentArchitecture() {
    if (yearsSinceStart() < 1) return MONOLITH;
    if (yearsSinceStart() < 3 && teamSize < 50) return MODULAR_MONOLITH;
    if (teamSize > 50) return MICROSERVICE;
    if (hasServiceMeshExpertise()) return SERVICE_MESH;
    return MICROSERVICE;
}

本章小结 ​

阶段架构适用
1单体早期,小团队
2模块化单体业务增长
3微服务50+ 团队
4云原生复杂业务
5Serverless辅助场景
关键点建议
演进小步快跑
业务驱动架构
团队决定复杂度
监控先行

动手练习 ​

  1. 架构评估:评估你当前项目处于哪个阶段,设计下一步演进路径
  2. 模块化:把单体项目按限界上下文拆包,接口和实现分离
  3. 独立部署:把一个模块独立部署,验证接口可替换
  4. 决策树:用决策框架评估你的业务应该选哪个架构

架构篇结束

本站基于 VitePress 构建 · 由 StackHub 团队维护