Spring Cloud从零开始:微服务入门指南

远方的接口
2025-12-18 10:13
阅读 1547

早上8点,成都的天刚蒙蒙亮,我已经坐在工位上泡好一杯速溶咖啡——别笑,我们中台团队最近项目排得紧,连买手冲的时间都没有了。上周五晚上加班到十一点,就为了赶在双11前把新模块上线,结果测试同学凌晨两点发消息说“注册中心挂了”,我当时真的想砸电脑。

但事情总得解决。作为公司技术中台的一员,我们的核心任务就是支撑各业务线快速迭代,而微服务架构正是这几年我们主推的方向。去年领导拍板要全面拥抱 Spring Cloud,理由很直白:“隔壁大厂都在用,咱不能掉队。”于是,我这个早起型选手(以及被逼无奈的学习者)硬着头皮从零开始啃文档、搭环境、踩坑、填坑……今天写这篇文章,既是复盘,也是给后来人少走点弯路。


为什么是 Spring Cloud?不是 Dubbo,也不是自己造轮子?

先说背景。我们公司早期是单体架构,一个 Java 应用包打天下,数据库一张表动辄几千万行。产品经理每次提需求都像在拆炸弹——改个字段,说不定就炸了支付模块。运维兄弟天天盯着 CPU 和 GC 日志,嘴里念叨着“再这样下去我要去送外卖了”。

后来搞了微服务拆分,一开始用的是简单的 HTTP + Nginx 转发,结果服务一多,配置爆炸,调用链乱成毛线团。这时候,Spring Cloud 的优势就出来了:它不是从零造轮子,而是基于 Netflix OSS、Consul、RabbitMQ 等成熟组件,提供了一套开箱即用的微服务治理方案,而且和 Spring Boot 天然集成,对我们这种 Java 技术栈为主的团队非常友好。

顺便吐槽一句:前端同事看我们折腾服务注册发现,一脸不屑:“你们后端怎么连个地址都要动态找?我们前端打包完扔 CDN 就完事了。” 哼,等哪天他们要搞微前端+动态路由+灰度发布,就知道什么叫“痛并快乐着”了。


动手!从零搭建一个最简微服务系统

第一步:服务注册与发现 —— Eureka 登场

微服务的核心是“服务能互相找到”。Spring Cloud 默认推荐 Eureka(虽然现在官方已进入维护模式,但对我们内部小规模系统完全够用)。

新建一个 eureka-server 项目:

<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
# application.yml
server:
  port: 8761

eureka:
  client:
    register-with-eureka: false  # 服务端不注册自己
    fetch-registry: false
  server:
    enable-self-preservation: false # 开发环境关掉自我保护

启动类加上 @EnableEurekaServer,搞定。访问 http://localhost:8761 就能看到 Eureka 控制台。

生产经验:线上环境一定要开启 enable-self-preservation,否则网络抖动时 Eureka 会疯狂剔除实例,导致雪崩。我们去年双11就吃过这亏。

第二步:写一个用户服务(user-service)

@RestController
public class UserController {
    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        // 这里其实应该查数据库,为了演示先 mock
        return new User(id, "张三", "zhangsan@example.com");
    }
}

关键是要让它能注册到 Eureka:

# user-service 的 application.yml
spring:
  application:
    name: user-service

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

加上 @EnableDiscoveryClient,启动后刷新 Eureka 页面,就能看到 USER-SERVICE 上线了!

第三步:订单服务调用用户服务 —— Feign 客户端

订单服务需要获取用户信息,传统做法是写 RestTemplate + URL 硬编码,但这样耦合死了。Spring Cloud 提供了声明式 HTTP 客户端 Feign

@FeignClient(name = "user-service")
public interface UserClient {
    @GetMapping("/user/{id}")
    User getUser(@PathVariable("id") Long id);
}

在订单服务里直接注入 UserClient,调用就像本地方法一样:

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

    public Order createOrder(Long userId) {
        User user = userClient.getUser(userId); // 自动负载均衡 + 服务发现
        return new Order(user.getId(), "订单123");
    }
}

踩坑提醒:Feign 默认不支持 @PathVariable 不加 value,必须写成 @PathVariable("id"),否则报错 Method has too many Body parameters。我第一次遇到时还以为是版本冲突,查了半小时源码。


配置中心?网关?熔断?一个都不能少

光有服务注册还不够。想象一下:50 个微服务,每个都要改数据库密码,难道挨个改配置重启?所以 Spring Cloud Config 必须上。

我们把配置文件扔到 GitHub 私有仓库(对,就是那个全球程序员的社交平台),Config Server 指向它:

# config-server
spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/your-company/microservice-configs
          username: your-bot
          password: your-token

其他服务通过 bootstrap.yml 拉取配置:

# user-service 的 bootstrap.yml
spring:
  application:
    name: user-service
  cloud:
    config:
      uri: http://config-server:8888

这样,改个数据库连接字符串,只需 push 到 GitHub,然后发个 /actuator/refresh 请求(配合 @RefreshScope),服务就热更新了。再也不用求运维半夜重启服务了!


关于“前端、区块链、Python”的强行关联(老板要求的关键词 😅)

我知道你在想:“说好的前端、区块链、Python 呢?”

  • 前端:我们的微服务 API 最终都是给前端消费的。前端团队用 Vue 写 SPA,通过 Nginx 反向代理到 Spring Cloud Gateway(替代 Zuul 的新一代网关)。Gateway 统一做鉴权、限流、路径重写。比如 /api/user/** 路由到 user-service,前端完全不用关心后端有几个服务。

  • 区块链:咳咳……我们公司确实有个区块链项目组,但他们用的是 Hyperledger Fabric,跟 Spring Cloud 没半毛钱关系。不过有一次他们想调用我们的用户服务做 KYC 验证,我们就通过 Feign 暴露了一个只读接口。所以,微服务的好处之一就是异构系统也能轻松集成——哪怕对方在挖矿。

  • Python:数据团队用 Python 写模型,训练完要部署成服务供业务调用。我们给他们开了个规范:用 Flask 写 REST API,注册到 Eureka(通过 eureka-client 的 Python 版本),然后我们的 Java 服务就能像调用普通微服务一样调用它。虽然性能差点,但胜在灵活。毕竟,在真实世界里,技术栈统一是理想,混搭才是常态


生产环境血泪教训

1. 服务雪崩 vs Hystrix 熔断

有次用户服务数据库慢查询,导致订单服务大量线程阻塞。幸好我们加了 Hystrix 熔断:

@FeignClient(name = "user-service")
public interface UserClient {
    @GetMapping("/user/{id}")
    @HystrixCommand(fallbackMethod = "getDefaultUser")
    User getUser(@PathVariable("id") Long id);

    default User getDefaultUser(Long id) {
        return new User(id, "匿名用户", "");
    }
}

当错误率超过阈值,Hystrix 自动熔断,返回兜底数据,避免拖垮整个系统。熔断不是万能的,但没有熔断是万万不能的。

注:Hystrix 已停止维护,新项目建议用 Resilience4j,但我们老系统还没迁移……

2. 链路追踪不能少

微服务调用链太深,出问题根本找不到源头。我们接入了 Sleuth + Zipkin,每个请求自动带上 traceId,日志里一搜就知道完整路径。运维同学终于不用问“哪个服务又慢了?”了。

3. 数据库设计要提前规划

微服务意味着每个服务独占数据库。我们早期没注意,订单服务和用户服务共用一个库,结果事务跨服务没法搞。后来拆库时,光数据迁移脚本就写了两周。教训:拆服务前先拆库!


性能与监控:别等线上炸了才看

我们用 Prometheus + Grafana 监控所有 Spring Boot Actuator 暴露的指标:JVM、HTTP 请求数、Feign 调用延迟等。

关键配置:

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  metrics:
    tags:
      application: ${spring.application.name}

配合 Kubernetes 的 HPA(水平扩缩容),CPU 超过 70% 自动加 Pod。自动化运维才是微服务的终极护城河。


总结:微服务不是银弹,但值得拥有

从零开始搞 Spring Cloud,前期投入确实大:要学一堆组件,要改开发习惯,要和运维磨合部署流程。但一旦跑顺了,好处显而易见:

  • 业务线可以独立开发、独立部署(再也不用等“全量发布日”)
  • 故障隔离(一个服务挂了不影响全局)
  • 技术异构(Python、Go 也能接入)
  • 弹性伸缩(大促前自动扩容)

当然,如果你只有两个服务,别硬上微服务——单体应用 + 良好模块化,可能更香

最后说句实在话:学 Spring Cloud 不是为了跳槽(虽然简历确实加分),而是为了让每天的工作少一点“救火”,多一点“创造”。毕竟,谁不想下班前喝着茶看系统稳稳跑着,而不是在群里狂刷“@所有人,线上挂了!”呢?


作者:某上市公司技术中台搬砖工,坐标成都,早起但不一定高效。GitHub 上有些 demo 代码(搜 spring-cloud-zero-to-hero),欢迎 star(不 star 也行,反正我又不靠这个涨工资)。

评论 0

最热最新
暂无评论
远方的接口Lv.1
0
影响力
0
文章
0
粉丝