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

MQ堵车了
2025-06-27 22:34
阅读 444

开篇:我们的项目背景与为何选择Spring Cloud

开篇:我们的项目背景与为何选择Spring Cloud

去年年初,我加入了一个新的项目组,负责开发一个面向金融行业的风控决策平台。这个系统需要支持多种数据源接入、规则引擎运行、模型调用和审批流程自动化,功能模块复杂度高,并且对性能、扩展性和稳定性都有较高要求。

我们当时的架构是典型的单体应用,代码已经臃肿不堪,部署周期长、更新困难。每次上线都需要整包打包部署,风险很高。再加上业务发展迅速,各种新需求频繁变更,旧架构逐渐难以支撑。

为了应对这些问题,项目组决定将系统拆分为多个独立的微服务,以提高可维护性和扩展性。在技术选型阶段,我们调研了Dubbo + Zookeeper、Kubernetes原生方案以及Spring Cloud生态,最终选择了Spring Cloud Alibaba + Nacos作为核心微服务解决方案。

为什么?不是因为赶时髦,而是因为我们希望有一套成熟、文档丰富、社区活跃又能快速上手的技术栈,而Spring Cloud刚好满足我们的需求。

这篇文章就想结合我们整个团队从零开始搭建微服务架构的过程,分享一下我们在使用Spring Cloud过程中的一些经验教训,希望能给刚起步的朋友一些启发。


问题描述:最初的困境

问题描述:最初的困境

刚开始,我们遇到的问题并不少。

  1. 服务发现机制不熟悉,不知道怎么配置注册中心。
  2. 服务之间如何通信?RestTemplate vs Feign?有什么区别?
  3. 配置管理混乱,不同环境(开发、测试、生产)的配置该如何统一管理?
  4. 没有熔断机制,某个服务故障导致连锁反应。
  5. 网关路由配置不清楚,权限控制怎么加?

这些问题看似简单,但组合在一起后,就变得异常棘手。而且当时团队成员中,真正接触过Spring Cloud的人不多,基本都是边学边干。

最严重的一次事故发生在灰度发布时:A服务调用B服务失败,由于未配置熔断降级,A服务不断重试,导致线程池满,最终整个链路的服务都不可用。那一次直接让线上交易停摆了十几分钟,教训极其深刻。


解决方案:技术选型与架构设计

解决方案:技术选型与架构设计

我们最终确定的技术栈如下:

  • Nacos:作为服务注册与配置中心
  • OpenFeign + Ribbon:服务间通信
  • Sentinel:熔断限流
  • Gateway:统一 API 网关,鉴权、限流等
  • SkyWalking:服务监控与链路追踪(后来接入)
  • RocketMQ:异步通信队列(后续加入)

微服务划分原则

在微服务拆分上,我们坚持“按业务边界来分”的原则。比如:

  • 用户服务:处理用户信息相关逻辑
  • 决策服务:负责规则引擎执行
  • 数据采集服务:处理数据来源的对接
  • 模型服务:封装算法模型调用
  • 审批服务:流程编排与人工审核

每个服务各自拥有独立数据库,杜绝共享数据库的设计方式,这样可以最大限度减少服务间的耦合。


代码实践:从0到1的搭建过程

下面通过实际示例展示我们是如何一步一步搭建起这套体系的。

Step 1:Nacos 注册中心部署

首先我们部署了 Nacos 服务(建议采用集群部署,这里演示单机版):

wget https://github.com/alibaba/nacos/releases/download/v2.2.3/nacos-server-2.2.3.zip
unzip nacos-server-2.2.3.zip
cd nacos/bin
sh startup.sh -m standalone

启动完成后,访问 http://localhost:8848/nacos,默认账号密码是nacos/nacos。

Step 2:创建两个基础服务(user-service 和 order-service)

user-service 的 POM 配置(简化):

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

application.yml 配置:

server:
  port: 8081

spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

入口类加上注解:

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

同理创建order-service,并引入相同依赖。

Step 3:Feign 远程调用

定义 OrderFeignClient 接口:

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

OrderController 调用:

@RestController
@RequestMapping("/orders")
public class OrderController {

    private final UserFeignClient userFeignClient;

    public OrderController(UserFeignClient userFeignClient) {
        this.userFeignClient = userFeignClient;
    }

    @GetMapping("/{id}")
    public ResponseEntity<OrderDTO> getOrderWithUser(@PathVariable String id) {
        var userResponse = userFeignClient.getUserById(1L);
        return ResponseEntity.ok(new OrderDTO(id, userResponse.getBody()));
    }
}

看起来很简单,但这一步背后其实有负载均衡(Ribbon)和客户端容错机制(Sentinel/Hystrix),后面会讲踩坑点。


踩坑经验:那些让我们彻夜难眠的Bug

坑点一:Feign 默认不开启日志输出

调试服务调用的时候,我们一度很困惑,为什么请求就是走不到目标服务?

最后发现,原来 Feign 默认的日志级别很低,连错误信息都不打出来。

解决方法

在application.yml中增加:

logging:
  level:
    com.example.client.UserFeignClient: DEBUG

或者启用 OpenFeign 日志拦截器:

@Bean
Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL;
}

这个小细节耽误了不少排查时间,提醒大家别忽略了日志配置的重要性。


坑点二:Feign调用找不到实例,报 LoadBalancerException

有时候我们本地启动服务后,Feign却始终无法找到目标服务,出现类似以下错误:

LoadBalancerException: No instances available for service

检查发现,原来是某些机器或环境中,host解析出了多个IP地址,导致Nacos获取的IP和服务注册的不一样。

解决办法

强制指定服务注册使用的 IP 地址:

spring.cloud.nacos.discovery.ip: 192.168.1.100
spring.cloud.nacos.discovery.port: 8081

或者设置为本机 IP,确保注册的IP和服务访问的IP一致。


坑点三:Hystrix/Sentinel 熔断配置不合理

上线前做压力测试时,我们模拟了一波流量高峰,结果部分服务响应变慢,Feign调用超时堆积,最终导致雪崩效应。

我们紧急启用了 Sentinel 的熔断限流策略,在Feign调用中增加了 fallback 机制:

@FeignClient(name = "user-service", fallback = UserFeignClientFallback.class)
public interface UserFeignClient {}

fallback实现:

@Component
public class UserFeignClientFallback implements UserFeignClient {
    @Override
    public ResponseEntity<User> getUserById(Long id) {
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();
    }
}

数据库设计模型-1

同时,在 Gateway 层做了全局限流保护,防止突发流量击穿下游服务。


效果总结:我们收获了什么?

整个微服务改造完成后,我们对比了一下前后差异:

维度 单体时代 微服务改造后
部署效率 打包时间长,部署全量 各自独立部署,互不干扰
故障隔离能力 一挂全挂 单个服务出问题影响有限
技术演进灵活性 牵一发而动全身 各服务可灵活升级、替换语言
团队协作效率 多人改同一份代码冲突频发 各司其职,代码冲突大大减少
新需求响应速度 上线周期长达一周以上 一般3天内完成验证并发布

更重要的是,我们逐步建立了完善的 DevOps 流程,包括 CI/CD 自动化部署、服务健康监测、异常告警机制,提升了整体运维效率。


经验分享:送给正在入坑Spring Cloud的你

如果你刚接触微服务,想从Spring Cloud入手,这里是我总结的几点建议:

1. 先搞清楚概念,不要上来就写代码

Spring Cloud 生态非常庞大,里面涉及的概念很多:服务注册、服务发现、负载均衡、熔断器、API网关、分布式事务等等。如果不了解这些组件之间的关系,很容易迷失方向。

我的建议是先花一两天时间把整个技术栈的关系图理清楚,再从最小可用单元开始搭建,比如先跑通两个服务+注册中心,再逐步加上限流、熔断、网关等模块。


2. 尽早引入监控与日志聚合工具

微服务带来的最大的挑战之一就是运维成本上升。以前单体程序一个日志文件就能看懂;现在几十个服务,上百个实例,日志分散在各个机器上,查问题难度骤增。

我们后来接入了 SkyWalking 做链路追踪,ELK 做日志收集,Prometheus + Grafana 做指标监控,极大提升了问题排查效率。


3. 不要忽视接口设计和数据库设计

很多人只关注服务拆分,却不注意接口设计数据库隔离。我们早期就犯过这种错误,导致后期服务间调用频繁、接口反复修改。

建议:

  • 接口尽量保持稳定,对外提供版本化接口;
  • 每个服务必须拥有自己的数据库;
  • 服务间调用避免深层嵌套,可以通过事件驱动解耦(如 RocketMQ);
  • 异常情况下要有 fallback 机制,避免服务雪崩。

4. 别忘了团队的沟通与协作

微服务不仅是技术上的事,更是组织结构、工作流程的调整。我们最初也是“各扫门前雪”,后来发现问题都在边界地带。

于是我们建立了“接口契约制”:服务间接口由双方共同评审,定期举行跨服务会议,确保接口一致性。


结语:成长是最好的礼物

回望这一年多的微服务之路,跌跌撞撞也走了过来。虽然一开始踩了很多坑,但正是这些挫折让我们更深入地理解了微服务的本质。

如果你也在微服务的路上前行,请记住一句话:
“不是所有的项目都适合微服务,但一旦决定出发,就要全力以赴。”

愿你的每一次服务拆分,都能带来更大的自由和更强的生命力。

评论 0

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