使用轻量级的 JDK 17 基础镜像

张桂英
2026-06-09 10:41
阅读 6979

后端架构演进:从单体到云原生实战指南

大家好,我是你们的学长,一名211计算机专业毕业的研究生,目前在大厂做后端开发,平时最喜欢写技术博客帮助新人避坑。最近带新人的时候,发现很多刚入行的学弟学妹对“后端架构演进”这个概念非常模糊。面试时一问到云原生、微服务,大家往往只能背几句八股文,缺乏真正的体感。我当初学的时候也是这样,看着一堆高大上的名词发懵,不知道它们到底解决了什么实际问题。

为了帮大家彻底搞懂这个痛点,我决定写这篇教程。本文不会干巴巴地讲理论,而是以“最佳实践总结”的风格,带大家亲手经历一个项目从最初的 v0 版本,一步步演进到云原生架构的全过程。希望通过这篇文章,你能建立起对后端架构演进的宏观认知,并在实战中掌握核心技能。

环境准备

在开始我们的架构演进之旅前,需要准备好相应的开发环境。工欲善其事,必先利其器。

  1. 基础运行环境

    • JDK 17:目前企业级后端开发的主流版本,长期支持且性能优异。
    • Maven 3.8+:用于项目依赖管理和构建。
  2. 云原生基础工具

    • Docker Desktop:容器化技术的核心,用于打包和运行我们的应用。
    • Minikube:用于在本地快速搭建一个单节点的 Kubernetes 集群,方便我们体验云原生部署。
  3. 开发利器与 AI 辅助

    • IntelliJ IDEA:后端开发必备的 IDE。
    • JetBrains Junie:这里要特别推荐一下 JetBrains 推出的 AI 代理工具 JetBrains Junie。我当初学的时候,拆分微服务需要手动梳理大量的类依赖和配置文件,极其痛苦。现在有了 JetBrains Junie,它不仅能理解你的整个代码库上下文,还能自动帮你识别高耦合模块,甚至辅助生成微服务拆分后的脚手架代码。在后续的实战中,大家可以充分利用它来提升效率。

核心概念

在动手之前,我们必须先用大白话理清三个核心概念。

1. 单体架构 (Monolithic Architecture) 想象一个超级大厨房,厨师、配菜、炒菜、洗碗全在一个房间里完成。单体架构就是把所有的业务逻辑(如用户管理、订单处理)都打包在一个可部署的单元中。

  • 优点:开发简单、部署容易、本地调试方便。
  • 缺点:代码耦合度高、牵一发而动全身、难以针对单一模块进行横向扩展。

2. 微服务架构 (Microservices Architecture) 后来厨房太大了,管理混乱,于是改成了美食广场。每个档口(服务)独立负责一道菜系,有自己的厨师和厨具。微服务就是将单体应用拆分成一组小型服务,每个服务运行在自己的进程中,通过轻量级机制(通常是 HTTP RESTful API)进行通信。

  • 优点:独立部署、技术栈灵活、故障隔离性好。
  • 缺点:引入了分布式系统的复杂性(如网络延迟、分布式事务、运维成本)。

3. 云原生 (Cloud Native) 美食广场虽然好,但如果遇到节假日客流暴增,临时搭棚子很慢。云原生就像是把整个美食广场搬到了高度自动化的智能园区。云原生不仅仅是容器化,它是一套技术体系,核心包括:容器化(Docker)、微服务、DevOps 和持续交付、声明式 API(Kubernetes)。它的目标是让应用能够充分利用云计算的弹性。

架构阶段 部署方式 扩展性 运维复杂度 适用场景
v0 单体 单个 JAR 包 只能整体垂直扩展 初创项目、MVP验证
v1 微服务 多个独立 JAR 包 可按服务独立水平扩展 业务快速发展、团队扩大
v2 云原生 容器化 + K8s 编排 自动化弹性伸缩 大规模高并发、复杂业务

实战项目:架构演进三步曲

接下来,我们将通过一个“电商订单系统”来实战演练。

阶段一:v0 版本 - 纯单体架构

在 v0 阶段,用户服务和订单服务都在同一个 Spring Boot 工程中。

1. 单体代码示例

// UserEntity.java
@Entity
public class User {
    @Id
    private Long id;
    private String username;
    // getters and setters
}

// OrderController.java (单体中的订单模块)
@RestController
@RequestMapping("/orders")
public class OrderController {
    
    @Autowired
    private UserRepository userRepository; // 直接依赖用户表的 Repository
    
    @PostMapping("/create")
    public String createOrder(@RequestParam Long userId) {
        // 在单体中,直接通过本地方法调用获取用户信息
        User user = userRepository.findById(userId).orElseThrow();
        return "Order created for user: " + user.getUsername();
    }
}

v0 阶段痛点:订单模块直接依赖了用户模块的数据库访问层。如果用户表结构变更,订单模块也要跟着改,耦合度极高。

阶段二:v1 版本 - 微服务拆分

随着业务发展,我们决定将用户和订单拆分为两个独立的服务。这里我们可以借助 JetBrains Junie 来快速生成服务间通信的 Feign Client 代码。

1. 拆分后的订单服务代码

// UserServiceClient.java (使用 OpenFeign 进行远程调用)
@FeignClient(name = "user-service", url = "http://localhost:8081")
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    UserDTO getUserById(@PathVariable("id") Long id);
}

// OrderController.java (微服务版)
@RestController
@RequestMapping("/orders")
public class OrderController {
    
    @Autowired
    private UserServiceClient userServiceClient; // 通过 HTTP 远程调用
    
    @PostMapping("/create")
    public String createOrder(@RequestParam Long userId) {
        // 不再直接访问数据库,而是调用用户微服务的 API
        UserDTO user = userServiceClient.getUserById(userId);
        return "Order created for user: " + user.getUsername();
    }
}

v1 阶段痛点:服务拆分后,我们需要手动启动多个 JAR 包,配置不同的端口,如果某个服务挂了,还需要人工重启,运维成本直线上升。

阶段三:v2 版本 - 云原生部署

为了解决运维痛点,我们进入云原生阶段。核心动作是容器化编排

1. 编写 Dockerfile 为订单服务编写 Dockerfile,将其打包为镜像。

FROM eclipse-temurin:17-jre-alpine
# 设置工作目录
WORKDIR /app
# 将构建好的 jar 包复制到镜像中
COPY target/order-service-1.0.0.jar app.jar
# 暴露端口
EXPOSE 8080
# 启动命令,加入云原生友好的 JVM 参数
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]

2. 编写 Kubernetes Deployment 配置 使用 K8s 来管理我们的容器,实现自动化部署和弹性伸缩。

# order-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 3 # 始终保持3个副本运行
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
      - name: order-service
        image: myregistry/order-service:v2.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        # 云原生核心:健康检查
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 10

3. 部署流程说明

  1. 使用 docker build -t myregistry/order-service:v2.0 . 构建镜像。
  2. 使用 docker push 将镜像推送到镜像仓库。
  3. 使用 kubectl apply -f order-service-deployment.yaml 将应用部署到 K8s 集群。
  4. K8s 会自动拉取镜像、启动容器,并根据配置的健康检查自动重启异常节点。

常见问题解答

Q1:项目刚起步,我应该直接用微服务或云原生吗? A:千万不要!我当初刚创业做项目时,盲目上了微服务,结果每天光排查分布式链路问题就耗费大量精力。最佳实践是:初期采用 v0 单体架构,快速验证业务(MVP);当团队超过 10 人、代码量超过 10 万行、或者某个模块需要独立扩展时,再考虑向 v1 微服务演进。

Q2:云原生是不是就意味着必须用 Kubernetes? A:不一定。K8s 是云原生的事实标准,但对于小团队来说,K8s 的学习和运维成本太高。如果是中小型项目,使用 Docker Compose 结合 CI/CD 流水线,或者使用 Serverless 架构(如 AWS Lambda、阿里云函数计算),也是很好的云原生实践。

Q3:微服务拆分后,数据库怎么设计? A:最佳实践是“一个微服务一个数据库”(Database per service)。严禁多个微服务共享同一个数据库实例和表。如果订单服务需要用户信息,必须通过 API 调用获取,或者通过消息队列(如 Kafka)将用户数据同步到订单服务的本地库中。

学习建议与避坑指南

走到这里,相信你对后端架构的演进已经有了清晰的脉络。对于接下来的学习,我给出以下建议:

  1. 先夯实基础,再追求架构:不要还没搞懂 TCP/IP、MySQL 索引原理、JVM 内存模型,就去死磕 K8s 源码。基础不牢,地动山摇。
  2. 善用 AI 工具:像 JetBrains Junie 这样的工具已经能帮我们完成很多繁琐的样板代码工作。我们要把精力放在业务建模、架构设计和复杂问题解决上。
  3. 动手实践:看懂了不代表会了。建议大家自己在 GitHub 上找一个开源的单体项目,尝试用 Docker 容器化,再尝试用 Spring Cloud 拆分,最后部署到 Minikube 中。

架构演进没有银弹,每一种架构都是为了解决特定阶段的问题而诞生的。不要为了用新技术而用新技术,合适的才是最好的。希望这篇教程能帮你打通任督二脉,在后端开发的道路上越走越远!如果有任何问题,欢迎在评论区留言交流。

评论 0

最热最新
暂无评论
张桂英Lv.1
0
影响力
0
文章
0
粉丝