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

北风里的开发者
2025-12-17 12:53
阅读 1402

上周五晚上十点半,我坐在工位上盯着屏幕上一串红色的Connection refused: connect错误,差点把咖啡杯砸了。这已经是这个月第三次因为单体应用部署问题导致线上回滚了。产品经理还在群里@我:“哥,双11大促就剩两周了,这个需求必须上!” 我默默打开日历,发现自己通勤一小时到公司的时间,居然比写代码的时间还多——北京的早高峰地铁,真的能榨干人最后一丝精力。

作为一个在北京卷了快六年的后端开发,最近被领导“温柔”地安排了一个新任务:把我们那个30万行代码的Spring Boot单体应用,拆成微服务。理由很充分:“你看人家阿里、腾讯都微服务化了,咱们也得跟上时代啊。” 虽然内心一万只草泥马奔腾而过,但为了年终奖,只能硬着头皮上。

其实我对微服务并不陌生。之前用Python写过一些小工具,也试过Django + Celery的异步架构,但真正在生产环境搞Spring Cloud,还是头一回。各种AI编程工具我都试过,GitHub Copilot、CodeWhisperer、Tabnine……最后还是选了Cursor,主要是它对Java生态的理解更深入,特别是在处理Spring配置的时候,能直接给我生成符合最佳实践的代码,省了我不少查文档的时间。

为什么是Spring Cloud?

可能有人会问:“现在都2024年了,干嘛不用Go或者Rust重写?”(没错,我最近确实在研究Rust,感觉所有权机制简直太优雅了!)但现实是,我们团队的技术栈主要还是Java,而且业务逻辑已经非常复杂,重写成本太高。Spring Cloud作为Java生态里最成熟的微服务解决方案,成了最稳妥的选择。

我们的目标很明确:

  • 拆分用户服务、订单服务、商品服务三个核心模块
  • 实现服务注册与发现
  • 配置中心统一管理
  • API网关统一入口
  • 保证高可用和容错

说白了,就是不想再因为一个服务挂了,整个系统瘫痪。

项目实战:从零搭建微服务架构

第一步:服务注册与发现 - Eureka

首先得让各个服务能找到彼此。Spring Cloud Netflix Eureka虽然Netflix已经停止维护了,但在国内还是有很多公司在用,社区支持也足够。我新建了一个eureka-server项目:

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

配置文件application.yml也很简单:

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

这里有个坑:enable-self-preservation一定要设为false,否则在网络抖动时Eureka不会剔除失效的服务实例,导致请求打到已经挂掉的服务上。

然后在用户服务里加上客户端注解:

@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}
spring:
  application:
    name: user-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

启动之后,访问http://localhost:8761就能看到注册的服务了。那一刻的感觉,就像第一次成功跑通Hello World一样爽!

第二步:配置中心 - Spring Cloud Config

以前改个配置要重新打包部署,测试环境、预发环境、生产环境的配置文件混乱不堪。有了Config Server,一切都变得井井有条。

创建Config Server:

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

配置指向Git仓库(我们用的是公司内部的GitLab):

spring:
  cloud:
    config:
      server:
        git:
          uri: https://gitlab.company.com/config-repo
          username: ${GIT_USERNAME}
          password: ${GIT_PASSWORD}

服务端只需要在bootstrap.yml中指定Config Server地址:

spring:
  application:
    name: user-service
  cloud:
    config:
      uri: http://localhost:8888

这样,所有配置都集中管理了。再也不用担心测试同学抱怨“为什么我的配置和你不一样”。

第三步:API网关 - Spring Cloud Gateway

微服务拆分后,前端不可能直接调用各个服务的接口,需要一个统一的入口。Spring Cloud Gateway基于WebFlux,性能比Zuul好很多。

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

路由配置:

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**

这里的lb://前缀表示使用负载均衡,会自动从Eureka获取服务实例。

第四步:服务间调用 - OpenFeign

服务之间怎么通信?直接用RestTemplate?太low了!OpenFeign让你像调用本地方法一样调用远程服务。

在订单服务中定义用户服务的客户端:

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

然后直接注入使用:

@Service
public class OrderService {
    
    @Autowired
    private UserClient userClient;
    
    public OrderDetail getOrderDetail(Long orderId) {
        Order order = orderRepository.findById(orderId);
        User user = userClient.getUserById(order.getUserId());
        return new OrderDetail(order, user);
    }
}

是不是很优雅?不过要注意超时配置,不然一个服务慢了,整个调用链都会被拖垮。

生产环境的那些坑

理论很美好,现实很骨感。上线过程中踩了不少坑:

坑1:服务启动顺序问题 Eureka Server必须最先启动,然后是Config Server,最后才是业务服务。我们用Docker Compose编排时,加了depends_on,但发现这只能保证容器启动顺序,不能保证应用就绪。后来用了健康检查:

services:
  user-service:
    depends_on:
      eureka-server:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3

坑2:配置刷新问题 修改配置后,需要手动触发/actuator/refresh端点。后来集成了Spring Cloud Bus + RabbitMQ,实现配置自动刷新。

坑3:熔断降级 某个第三方接口响应慢,导致整个服务线程池耗尽。引入Hystrix(虽然已停更,但够用):

@FeignClient(name = "user-service")
public interface UserClient {
    
    @GetMapping("/users/{id}")
    @HystrixCommand(fallbackMethod = "getUserFallback")
    User getUserById(@PathVariable("id") Long id);
    
    default User getUserFallback(Long id) {
        // 返回默认用户或抛出友好异常
        return new User(id, "Unknown");
    }
}

性能对比:单体 vs 微服务

上线一个月后,我们做了详细的性能对比:

指标 单体应用 微服务架构
部署时间 15分钟 用户服务3分钟,订单服务4分钟
故障隔离 全站挂掉 单个服务故障不影响其他
扩容粒度 整个应用 按需扩容特定服务
开发效率 多人协作冲突多 团队按服务划分,独立开发
内存占用 2GB 总计3.5GB(但可精细化控制)

虽然总体资源消耗增加了,但稳定性和开发效率提升非常明显。特别是双11期间,订单服务压力大的时候,我们只扩容了订单服务,其他服务保持不变,节省了不少服务器成本。

关于Python和代码人生的思考

说到这里,可能有人疑惑:标题里提到Python,但全文都是Java?其实我在做技术选型时,认真考虑过用Python的FastAPI重构部分服务。Python在数据处理、脚本编写方面确实无敌,我平时用Python写自动化脚本、数据分析都很顺手。

但微服务的核心在于稳定性、性能和团队协作。Java的强类型、完善的监控体系、丰富的生态,在企业级应用中还是不可替代的。我的“代码人生”哲学就是:用合适的工具解决合适的问题。写爬虫用Python,做高并发服务用Java,最近研究Rust也是想在系统编程层面有更深的理解。

给初学者的建议

如果你也准备踏上微服务之路,我的建议是:

  1. 不要为了微服务而微服务:先评估业务复杂度,简单的应用强行拆分只会增加运维成本
  2. 监控和日志先行:没有完善的监控,微服务就是一场灾难。我们集成了Prometheus + Grafana + ELK
  3. 自动化测试很重要:服务多了,手工测试根本来不及,CI/CD流水线必须跟上
  4. 团队沟通成本会增加:微服务不仅是技术问题,更是组织架构问题

最后,分享一个小技巧:用Cursor这样的AI工具时,不要完全依赖它生成的代码。特别是涉及到安全配置、数据库事务这些关键地方,一定要自己review。我之前就遇到过AI生成的Feign客户端没有处理超时,差点造成生产事故。

现在回头看,从那个周五晚上的崩溃时刻,到现在系统稳定运行,虽然过程痛苦,但收获满满。微服务不是银弹,但它确实帮我们解决了单体应用的痛点。下次技术分享会上,我打算聊聊Spring Cloud Alibaba的实践,毕竟Nacos比Eureka在国内用得更多。

对了,今天又是通勤一小时的日子,地铁上写完这篇文章,希望对你有所帮助。记住,在这个充满Bug的世界里,我们都是在不断debug中成长的程序员。

评论 0

最热最新
暂无评论
北风里的开发者Lv.1
0
影响力
0
文章
0
粉丝