从单体到微服务:Java开发者的第一步实战指南

CSS摆烂王
2025-12-20 18:38
阅读 981

大家好,我是团队的培训负责人,过去五年带过上百名应届生。每次新人入职,总有人问我:“微服务到底难不难?我连单体应用都没写明白。”其实,我当初学的时候也一样——被“服务注册”“熔断降级”这些词吓到过。但今天我想告诉你:只要动手做一遍,微服务没那么玄

这篇教程专为零基础的你设计。我们将用最简单的 Java 代码,从一个“产品管理”的单体应用出发,亲手把它拆成两个微服务。过程中你会学到核心概念、避坑技巧,甚至能应对常见的面试题挑战。


为什么微服务值得学?

简单说:微服务是一种把大系统拆成多个小服务的架构方式。每个服务独立开发、部署、扩展。比如电商系统可以拆成“用户服务”“订单服务”“产品服务”。

  • 优点:故障隔离(一个服务挂了不影响整体)、技术栈灵活(不同服务可用不同语言)、团队协作高效。
  • 代价:复杂度上升(要处理网络调用、数据一致性等)。

💡 小提示:不是所有项目都需要微服务!如果你的应用只有 3 个接口,单体更合适。微服务适合业务复杂、团队规模大的场景。


环境准备:5 分钟搭好开发环境

我们需要以下工具(全是免费开源的):

工具 用途 安装建议
JDK 17 Java 运行环境 推荐 Adoptium Temurin
Maven 项目依赖管理 官网下载后配置 PATH
IntelliJ IDEA Community 开发 IDE 免费版足够
Spring Boot 3.x 快速构建微服务 通过 start.spring.io 生成

创建第一个项目(产品服务)

  1. 访问 start.spring.io
  2. 填写:
    • Project: Maven
    • Language: Java
    • Spring Boot: 3.2.x
    • Group: com.example
    • Artifact: product-service
  3. 添加依赖:Spring Web, Spring Data JPA, H2 Database
  4. 点击 “Generate”,解压后用 IDEA 打开

⚠️ 注意:别选错 Spring Boot 版本!3.x 要求 JDK 17+。


核心概念:用“产品管理”说清楚微服务

假设我们要做一个产品管理系统。在单体架构中,所有功能(增删改查产品)都在一个应用里。但在微服务中,我们可以只让这个应用负责“产品”相关逻辑。

关键角色解释(超简版)

  • 服务提供者(Provider):提供 API 的服务,比如我们的 product-service
  • 服务消费者(Consumer):调用其他服务的 API,比如未来会有的 order-service
  • 服务注册中心:像“电话簿”,记录所有服务的地址(我们后面会用 Eureka 演示)。
  • RESTful API:服务间通信的标准方式,用 HTTP + JSON。

🧠 我当初学的时候总混淆“服务”和“模块”。记住:一个微服务 = 一个独立运行的进程(jar 包),不是代码里的一个 package!


实战第一步:打造单体版“产品服务”

先别急着拆!我们先写出完整的单体应用,再改造它。

1. 定义产品实体

// src/main/java/com/example/productservice/Product.java
package com.example.productservice;

import jakarta.persistence.*;

@Entity
@Table(name = "products")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    private Double price;

    // 构造函数、getter/setter 省略(IDEA 可自动生成)
}

2. 创建 Repository

// src/main/java/com/example/productservice/ProductRepository.java
package com.example.productservice;

import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository<Product, Long> {
}

3. 编写 Controller

// src/main/java/com/example/productservice/ProductController.java
package com.example.productservice;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @Autowired
    private ProductRepository productRepository;

    @GetMapping
    public List<Product> getAllProducts() {
        return productRepository.findAll();
    }

    @PostMapping
    public Product createProduct(@RequestBody Product product) {
        return productRepository.save(product);
    }
}

4. 配置 H2 内存数据库(方便测试)

application.properties 中添加:

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.jpa.hibernate.ddl-auto=create-drop
spring.h2.console.enabled=true

5. 启动并测试

运行 ProductServiceApplication.java,访问 http://localhost:8080/h2-console,用默认设置登录。

再用 curl 或 Postman 测试:

# 添加产品
curl -X POST http://localhost:8080/api/products \
  -H "Content-Type: application/json" \
  -d '{"name":"笔记本电脑","price":5999}'

# 查询所有产品
curl http://localhost:8080/api/products

✅ 恭喜!你已经有一个能跑的单体应用了。


实战第二步:拆出第二个服务(订单服务)

现在,我们模拟“订单服务”需要查询产品信息。这意味着订单服务要远程调用产品服务。

创建订单服务

重复之前的步骤,在 start.spring.io 创建新项目 order-service,依赖同样选 Spring Web, Spring Data JPA, H2 Database

在订单服务中调用产品服务

关键来了:如何让订单服务知道产品服务在哪?

方案 A:硬编码(不推荐,仅用于演示)

// OrderService 中直接写死地址
String productUrl = "http://localhost:8080/api/products/1";
// 用 RestTemplate 发起 HTTP 请求...

但这样一旦产品服务端口变了,订单服务就挂了。

方案 B:使用服务发现(推荐)

我们引入 Eureka 服务注册中心

步骤 1:创建 Eureka Server

新建一个 Spring Boot 项目 eureka-server,只加 Eureka Server 依赖。

主类加上注解:

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

配置 application.yml

server:
  port: 8761
eureka:
  client:
    register-with-eureka: false
    fetch-registry: false

启动后访问 http://localhost:8761,看到 Eureka 仪表盘就算成功。

步骤 2:让产品服务注册自己

product-servicepom.xml 加:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

并在 application.properties 加:

spring.application.name=product-service
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

重启 product-service,刷新 Eureka 页面,你会看到 PRODUCT-SERVICE 出现在 Instances 列表中!

步骤 3:订单服务通过服务名调用

order-service 同样加入 Eureka Client 依赖,并配置:

spring.application.name=order-service
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/

然后注入 RestTemplate 并启用服务发现:

@Configuration
public class AppConfig {
    @Bean
    @LoadBalanced // 关键!启用服务发现
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

在 OrderController 中调用:

@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/test-product")
    public String getProductFromService() {
        // 注意:这里用服务名 product-service,不是 IP+端口!
        String url = "http://product-service/api/products";
        return restTemplate.getForObject(url, String.class);
    }
}

启动 order-service,访问 http://localhost:8081/test-product(假设订单服务端口 8081),如果返回产品列表,说明微服务调用成功!


新手常见问题 & 避坑指南

Q1:为什么我的服务没注册到 Eureka?

  • 检查 spring.application.name 是否配置
  • 确认 Eureka Server 地址是否正确(注意结尾有 /eureka/
  • 查看控制台日志是否有 Registered instance 字样

Q2:调用时报 “UnknownHostException: product-service”?

  • 确保 RestTemplate 加了 @LoadBalanced 注解
  • 确认两个服务都已成功注册到 Eureka

Q3:H2 数据库的数据怎么没了?

  • 因为用了 ddl-auto=create-drop,每次重启清空数据。生产环境用 MySQL 并设为 validateupdate

Q4:微服务之间怎么传用户信息?

  • 初学者可先用 Token 放在 Header 里透传。后续学 Spring Cloud Gateway + JWT。

面试题挑战:你能答对几道?

作为培训负责人,我常问新人这些问题:

  1. 微服务 vs 单体,什么时候该拆?
    → 答:当单体应用导致团队协作效率低、部署频繁冲突、技术栈无法演进时。

  2. 服务注册中心的作用是什么?
    → 答:动态管理服务实例地址,实现服务发现与负载均衡。

  3. 如果 product-service 挂了,order-service 怎么办?
    → 答:引入熔断机制(如 Resilience4j),返回兜底数据或错误提示。


下一步学习建议

你已经走出了最难的第一步!接下来:

  1. 深入 Spring Cloud Alibaba:用 Nacos 替代 Eureka(国内更流行)
  2. 学 API 网关:用 Spring Cloud Gateway 统一入口
  3. 解决分布式事务:了解 Seata 或 Saga 模式
  4. 监控与链路追踪:集成 Zipkin + Sleuth

🌟 最后送你一句话:微服务不是目标,而是手段。架构演进的核心永远是“解决当前业务痛点”。别为了用微服务而微服务!

动手敲完这篇教程的代码,你已经超过了 80% 只看不练的初学者。加油,未来的架构师!

评论 0

最热最新
暂无评论
CSS摆烂王Lv.1
0
影响力
0
文章
0
粉丝