Spring Cloud从零开始:一个后端开发者的微服务入门实战分享

架构_胡玉_引领者
2025-06-25 05:09
阅读 770

开篇:为什么我决定写这篇文章?

开篇:为什么我决定写这篇文章?

作为一个在互联网公司工作多年的后端开发者,我经历过从单体架构到分布式、再到微服务架构的完整演变过程。特别是在公司业务快速增长的那几年,我们不得不面对越来越多的系统复杂性和技术挑战。

2019年的时候,我参与了一个全新的电商平台重构项目。原本我们是用Spring Boot做的单体应用,随着用户量上涨和新功能需求的激增,系统越来越吃力,部署也变得频繁且容易出错。最终团队决定拆分微服务,使用Spring Cloud来搭建整套微服务架构。

刚开始的时候,我也是一头雾水:服务注册与发现怎么配置?API网关要怎么设计?各个服务之间如何通信?还有熔断、限流、配置中心这些听起来高大上的概念……统统都需要一边学一边实践。

今天我想借这篇真实经历的技术分享文章,结合我在那个项目中的经验教训,和大家一起从零开始学习Spring Cloud微服务。如果你是一个刚接触微服务的新手或者正在犹豫要不要上手Spring Cloud,希望这篇文章能让你少走一些弯路。


问题描述:从单体应用到微服务,我们的痛

问题描述:从单体应用到微服务,我们的痛

那个电商平台的原始版本是一个典型的单体架构:

  • 所有业务逻辑都在同一个jar包里
  • 数据库共用一张表(甚至没有分库分表)
  • 每次发布必须整个项目重新打包、重启
  • 新人接手困难,部署成本高
  • 随着流量增长,经常出现接口超时甚至服务器崩溃

具体来说,我们遇到了几个典型的问题:

  1. 部署效率低下
    每个修改哪怕只是前端文案,都要重跑一遍CI/CD流程。一次构建可能需要5分钟以上,测试环境还要反复等待。

  2. 代码耦合度太高
    订单模块和商品模块互相依赖严重,改一个地方经常牵一发动全身。

  3. 性能瓶颈明显
    商品搜索接口响应时间长,在高峰期经常出现慢查询甚至超时。

  4. 扩展性差
    想给某个服务做横向扩容?对不起,只能整个应用一起扩。

这些问题让我们意识到,是时候尝试微服务了。


解决方案:Spring Cloud + 微服务架构初探

我们的目标很明确:

  • 将原有单体应用拆分成多个独立的服务
  • 实现服务治理,包括注册发现、负载均衡、容错处理等
  • 构建统一的API网关进行路由和鉴权
  • 实现配置集中管理
  • 提供链路追踪能力,方便排障

于是我们选择了Spring Cloud这一整套微服务解决方案,并围绕它搭建了基础架构平台。主要技术选型如下:

功能点 技术组件
服务注册与发现 Eureka Server
负载均衡 Ribbon + RestTemplate / Feign
API网关 Zuul(后期换成Gateway)
配置中心 Spring Cloud Config
分布式事务 Seata(简单场景下直接本地事务+补偿机制)
日志与监控 ELK + Zipkin
熔断限流 Hystrix(后来换成了Sentinel)

接下来,我会重点讲我们是怎么一步步搭起这个框架的。


代码实践:Spring Cloud微服务起步实例

1. 创建Eureka Server —— 注册中心

这是我们第一个启动的服务。用于管理所有微服务的注册和发现。

# application.yml
server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

服务器部署方案-1

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

访问 http://localhost:8761,你会看到Eureka控制台界面。

2. 创建商品服务(Product Service)

每个微服务都向Eureka注册自己。

# product-service的application.yml
spring:
  application:
    name: product-service
server:
  port: 8081

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
@RestController
@RequestMapping("/products")
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping("/{id}")
    public ResponseEntity<Product> getProductById(@PathVariable Long id) {
        return ResponseEntity.ok(productService.getProductById(id));
    }
}

启动之后可以在Eureka页面看到这个服务。

3. 创建订单服务(Order Service),调用商品服务

这时候就需要用Ribbon或Feign来做服务间调用了。

使用Feign:

feign:
  client:
    config:
      default:
        http:
          enabled: true

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
@FeignClient(name = "product-service")
public interface ProductServiceClient {
    @GetMapping("/products/{id}")
    Product getProductById(@PathVariable("id") Long id);
}

@Service
public class OrderService {

    @Autowired
    private ProductServiceClient productClient;

    public Order createOrder(Long productId) {
        Product product = productClient.getProductById(productId);
        // 处理订单创建逻辑
    }
}

这样就实现了服务间的调用。


踩坑经验:那些“小坑”让人印象深刻

虽然整体方案可行,但实际开发中我们也踩了不少坑。这里分享几个印象深刻的案例。

坑一:Feign Client调用失败,找不到服务

报错信息类似:

LoadBalancerException: No instances available for service

原因是:

  • Eureka没同步上来
  • 服务名拼错了
  • 没有启动Eureka Server
  • Feign未启用Hystrix(默认不启用)

解决办法:

  • 检查Eureka控制台看服务是否在线
  • 检查Feign注解里的name值是否一致
  • 在启动类加@EnableFeignClients
  • 如果开了Hystrix注意降级逻辑

坑二:服务注册慢、延迟明显

有时候服务启起来以后,在Eureka上需要过好几秒才能显示出来,导致其他服务调不到。

我们通过调整Eureka的刷新频率和缓存时间解决:

eureka:
  client:
    registry-fetch-interval-seconds: 5
  instance:
    lease-expiration-duration-in-seconds: 10
    lease-renewal-interval-in-seconds: 5

不过要注意这种修改会增加网络开销和内存占用。

坑三:Zuul网关跨域问题

前端调用网关地址的时候遇到CORS报错。最初我们以为是网关的问题,结果排查下来发现Zuul本身不处理跨域,是被代理的下游服务没有设置。

解决方式是在下游服务里加上全局跨域过滤器:

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedOrigins("*")
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .allowedHeaders("*")
            .exposedHeaders("Authorization");
    }
}

如果是使用Gateway的话也可以在网关层面统一处理。


效果总结:从混乱到有序

系统架构设计图-2

经过两个月的努力,我们的微服务架构逐渐稳定上线。整个项目的收益体现在以下几个方面:

  1. 部署效率提升:可以按模块单独发版,不再需要每次全部重启,构建时间减少70%+
  2. 系统稳定性提高:核心服务如支付、库存等进行了隔离,避免故障扩散
  3. 可扩展性强:高峰期能快速对热点服务做水平扩容
  4. 开发协作更清晰:每个服务边界明确,多人协作更顺畅

更重要的是,我们建立了一套标准化的服务模板,后续新项目基本都可以复用这个架构结构,极大提升了开发效率。


经验分享:几点忠告送给刚起步的同学

如果你现在也是第一次接触微服务,准备用Spring Cloud作为技术栈,以下是我根据这几年经验整理的一些建议:

✅ 1. 不要为了拆分而拆分

很多人一开始就把所有服务全都拆得特别细,结果后面发现根本无法维护。建议前期拆分保持适度粒度即可,比如先按照业务领域划分(商品、订单、支付),后续再细化。

✅ 2. 优先考虑服务治理

微服务一旦多了,服务治理就是首要任务。注册发现、熔断限流、配置中心这些基础设施要尽早规划,不要等到系统出问题再去补。

✅ 3. 日志和监控必须同步建设

你一定会在生产环境中遇到各种奇怪的问题,所以一开始就要接入日志收集(ELK)、链路追踪(Sleuth + Zipkin)以及健康检查(Actuator + Prometheus)。

✅ 4. 接口设计要有规范

服务之间的调用要有一致的格式,比如请求参数、返回封装、错误码定义等。否则后期维护起来非常痛苦。

✅ 5. 数据库设计同样重要

微服务不是数据拆分的理由。你可以先共享数据库,后期根据业务发展慢慢做分库分表、引入中间件。别一开始就搞得太复杂。


写在最后:技术没有银弹,但方向要对

说实话,微服务并不是万能药,也不是每个项目都适合拆成微服务。它带来的不仅是架构上的灵活性,也有运维、协同、测试等方面的复杂性。选择这条路之前,你要问自己几个问题:

  • 我们的业务真的需要吗?
  • 团队是否有足够的支撑能力?
  • 是否已经具备相应的自动化工具链?

对我而言,那次转型确实是个挑战,但也带来了不小的成长。Spring Cloud作为一套成熟的开源微服务框架,虽然有些组件已进入维护状态(比如Zuul、Hystrix),但它仍然提供了完整的微服务治理思路,是很多公司的首选。

如今Spring生态又有了Spring Cloud Alibaba这样的增强扩展,结合Nacos、Sentinel、Dubbo等,更加贴近国内企业的需求。

无论你是刚入行的小白,还是有多年经验的工程师,我都鼓励你在合适的时机多去尝试新技术、新架构。只有通过实际项目中的落地,你才能真正理解它的优缺点,进而做出更明智的技术决策。

愿你也能在微服务的世界里走得更远、更稳。共勉!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝