跳到主要内容
Spring Cloud 微服务架构核心概念与实战入门 | 极客日志
Java java
Spring Cloud 微服务架构核心概念与实战入门 Spring Cloud 是构建分布式系统的工具集,基于 Spring Boot 简化微服务开发。文章梳理了从单体到微服务的架构演进,对比了集群与分布式差异,详解了 Spring Cloud 版本规范及主流实现方案(Netflix 与 Alibaba)。通过订单与商品服务的拆分示例,演示了数据库准备、工程搭建及远程调用流程,为后续学习服务发现与治理打下基础。
板砖工程师 发布于 2026/3/26 更新于 2026/7/6 31 浏览在学习 Spring Cloud 之前,我们先来了解一下什么是微服务,以及它的发展历史。架构演进过程中遇到了哪些问题?又是如何解决的?Spring Cloud 在其中又扮演了什么角色?
微服务架构的演进
单体架构
早期项目通常将所有功能模块(如用户管理、订单处理)打包在一个应用中,共享同一数据库,模块间通过函数调用直接通信。这种方式开发、测试、部署简单,适合小型项目或初创团队。
优点:
开发简单 :适合小型项目。
部署便捷 :只需部署一个应用(如单个 WAR/JAR 文件)。
性能高 :模块间调用无网络开销(本地调用)。
缺点:
难以扩展 :只能整体扩展,无法针对模块单独扩容。
维护困难 :代码库膨胀后牵一发而动全身。
技术僵化 :难以引入新语言或框架。
可靠性差 :单点故障可能导致整个系统崩溃。
随着用户量和流量增长,单体应用面临服务器压力大、业务耦合度高、微小问题导致全系统挂掉等风险。此时,我们需要考虑从横向和纵向两个维度进行优化。
集群和分布式架构
横向扩展
添加服务器,将单台机器变成多台机器的集群。通过负载均衡部署相同应用,提高并发能力和可用性。
纵向扩展
将应用按业务拆分,分为多个项目的分布式架构。将系统拆分为不同服务(如订单服务、支付服务),部署在不同节点上,服务间通过 RPC/HTTP 通信。
也可以结合使用,先以单体架构规模的项目为单位进行垂直划分,每个项目再部署多台服务器。
集群和分布式的区别:
概念 :集群是多个计算机处理相同任务;分布式是多个计算机完成不同任务。
功能 :集群节点功能相同且可替代;分布式节点业务功能不同。
关系 :两者常配合使用,实际设计中统称为分布式架构。
优缺点对比:
优点 :高可用性(避免单点)、可扩展性(水平扩展)、性能提升(并行处理)。
缺点 :复杂度高(需处理网络、一致性)、运维成本高(需服务发现、监控等)。
当服务越来越多时,重复代码和复杂的调用关系成为痛点。我们将通用的共享服务提取成独立的基础服务,这就形成了微服务 。
微服务架构 微服务按业务边界划分独立服务(如用户服务、商品服务),每个服务拥有自己的数据库,可独立开发、部署、扩展,技术栈异构,通常通过 REST/gRPC 或消息队列解耦。
简而言之,微服务就是很小的服务,仅对应单一功能,只做一件事,且可单独部署运行。相比于分布式架构,它拆分粒度更小,更强调服务的专业化和精细分工。
敏捷开发 :小团队专注单个服务,迭代更快。
弹性扩展 :可针对热点服务单独扩容。
容错性 :服务隔离,故障不易扩散。
技术多样性 :不同服务可用不同技术实现。
运维复杂 :需容器化、编排、日志聚合。
分布式难题 :需处理最终一致性、跨服务查询、链路追踪。
网络开销 :服务间调用延迟高于本地调用。
演化过程大致为:单体架构 → 集群 → 微服务 。在这个过程中,虽然微服务优势明显,但服务数量增加带来了新的治理问题。对于 Java 技术栈,Spring Cloud 是目前最成熟的解决方案之一。
Spring Cloud 详解
什么是 Spring Cloud 在微服务架构中,系统被拆分为多个独立服务,随之带来了一系列分布式环境下的挑战。Spring Cloud 针对这些问题提供了标准化解决方案,将解决常见问题的开源框架基于 Spring Cloud 规范进行了整合,并基于 Spring Boot 风格进行了封装,屏蔽了复杂的配置。
分布式系统问题 Spring Cloud 的解决方案 对应组件/技术 服务发现与注册 动态管理服务实例上下线 Eureka、Nacos、Consul、Zookeeper 服务间调用 简化远程调用,支持负载均衡和容错 OpenFeign、Ribbon、RestTemplate 配置中心 统一管理分布式配置,支持动态更新 Spring Cloud Config、Nacos、Apollo 熔断与容错 防止服务雪崩,提升系统弹性 Hystrix、Sentinel API 网关 统一入口、路由、鉴权、限流 Spring Cloud Gateway、Zuul 分布式事务 解决跨服务数据一致性问题 Seata 消息驱动 解耦服务,实现异步通信 Spring Cloud Stream 链路追踪 监控请求链路,定位性能瓶颈 Sleuth + Zipkin
Spring Cloud 版本 Spring Cloud 是一个由很多子项目组成的庞大项目,发布阶段不同。为了管理依赖关系及避免冲突,主项目版本命名采用了伦敦地铁站名称(如 Hoxton、Greenwich)。但从 Hoxton 版本之后,版本号变成了 2020.0.0 这样的日期格式。
由于所有子项目都依赖 Spring Boot,两者之间存在严格的对应关系。例如 Spring Boot 3.2.X 对应的 Spring Cloud 版本是 2023.0.X。若版本不匹配,可能出现兼容性问题。
Spring Cloud 实现方案 1. Spring Cloud Netflix
这是 Netflix OSS 在 Spring Cloud 规范下的实现,核心组件包括 Eureka、Ribbon、Hystrix、Zuul 1.x、Feign。但 Netflix 公司在 2018 年左右宣布其核心组件进入维护阶段,Spring Cloud 也删除了这些维护模块,并提供了一些替换建议:
Netflix 组件 替代方案 特点 Ribbon Spring Cloud LoadBalancer 支持响应式编程,与 Spring 生态深度集成 Hystrix Resilience4J 轻量级,支持函数式编程 Zuul 1.x Spring Cloud Gateway 基于 WebFlux 的非阻塞 IO,性能更高
2. Spring Cloud Alibaba
这是阿里的开源组件和云产品在 Spring Cloud 规范下的实现,核心组件包括 Nacos、Sentinel、RocketMQ、Seata、Dubbo。相比 Netflix 方案,Alibaba 方案在国内社区更为活跃。
服务拆分实战 了解了基本概念后,我们继续学习如何进行微服务应用开发。第一步是服务拆分。
拆分原则 拆分多小的服务才算微服务没有明确标准,并非越小越好。一般遵循以下原则:
单一职责原则(SRP) :每个服务只负责一个明确的业务能力,有清晰的定义和边界。
服务自治 :每个微服务应具备高度自治能力,独立开发、测试、构建、部署、运行。
单向依赖 :微服务之间做到单向依赖,严禁循环依赖。
简单示例 我们以一个简单的订单系统进行演示,将其拆分为两个服务:订单服务和商品服务。
订单服务 :提供订单 ID,获取订单详细信息。
商品服务 :根据商品 ID,返回商品详细信息。
数据准备 根据服务自治原则,每个服务都应该有自己独立的数据库。
工程搭建
父工程创建 创建一个空的 Maven 项目,并只保留 pom.xml。完善 pom 文件:声明父工程打包方式为 pom,使用 properties 进行版本号统一管理,通过 dependencyManagement 管理依赖。
<?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > org.example</groupId > <artifactId > spring-cloud-demo</artifactId > <version > 1.0-SNAPSHOT</version > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 3.1.6</version > <relativePath /> </parent > <properties > <maven.compiler.source > 17</maven.compiler.source > <maven.compiler.target > 17</maven.compiler.target > <java.version > 17</java.version > <mybatis.version > 3.0.3</mybatis.version > <mysql.version > 8.0.33</mysql.version > <spring-cloud.version > 2022.0.3</spring-cloud.version > </properties > <dependencies > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > </dependencies > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-dependencies</artifactId > <version > ${spring-cloud.version}</version > <type > pom</type > <scope > import</scope > </dependency > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > <version > ${mybatis.version}</version > </dependency > <dependency > <groupId > com.mysql</groupId > <artifactId > mysql-connector-j</artifactId > <version > ${mysql.version}</version > </dependency > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter-test</artifactId > <version > ${mybatis.version}</version > <scope > test</scope > </dependency > </dependencies > </dependencyManagement > </project >
这里要注意,dependencyManagement 和 dependencies 是两种不同的依赖声明方式。前者声明依赖的版本和范围(不实际引入依赖),子模块可继承并省略版本号;后者直接引入依赖到当前模块。
子项目创建 - 商品服务 在父项目下创建子项目,完善 pom 文件引入 Web、MySQL、MyBatis 依赖。
<?xml version="1.0" encoding="UTF-8" ?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > org.example</groupId > <artifactId > spring-cloud-demo</artifactId > <version > 1.0-SNAPSHOT</version > </parent > <artifactId > product-service</artifactId > <properties > <maven.compiler.source > 17</maven.compiler.source > <maven.compiler.target > 17</maven.compiler.target > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > com.mysql</groupId > <artifactId > mysql-connector-j</artifactId > </dependency > <dependency > <groupId > org.mybatis.spring.boot</groupId > <artifactId > mybatis-spring-boot-starter</artifactId > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
完善启动类、配置文件及实体类、Controller、Service、Mapper。
server:
port: 9090
spring:
application:
name: product-service
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=false
username: root
password: MySQL 密码
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
@Data public class ProductInfo { private Integer id; private String productName; private Integer productPrice; private Integer state; private Date createTime; private Date updateTime; }
@RestController @RequestMapping("/product") public class ProductController { @Autowired private ProductService productService; @RequestMapping("/{productId}") public ProductInfo getProductById (@PathVariable("productId") Integer productId) { return productService.findProductInfo(productId); } }
@Slf4j @Service public class ProductService { @Autowired private ProductMapper productMapper; public ProductInfo findProductInfo (Integer id) { log.info("收到请求,id: " + id); return productMapper.selectProductById(id); } }
@Mapper public interface ProductMapper { @Select("select * from product_detail where id = #{id}") ProductInfo selectProductById (Integer id) ; }
运行访问 127.0.0.1:9090/product/1001 进行测试。
子项目创建 - 订单服务 pom.xml 结构与商品服务类似,注意修改 artifactId 为 order-service。
server:
port: 8080
spring:
application:
name: order-service
datasource:
url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: MySQL 密码
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
map-underscore-to-camel-case: true
@Data public class OrderInfo { private Integer id; private Integer userId; private Integer productId; private Integer num; private Integer price; private Integer deleteFlag; private Date createTime; private Date updateTime; }
@RequestMapping("/order") @RestController public class OrderController { @Autowired private OrderService orderService; @RequestMapping("/{orderId}") public OrderInfo getOrderById (@PathVariable("orderId") Integer orderId) { return orderService.findOrderInfoById(orderId); } }
@Service public class OrderService { @Autowired private OrderMapper orderMapper; public OrderInfo findOrderInfoById (Integer orderId) { OrderInfo orderInfo = orderMapper.selectOrderById(orderId); return orderInfo; } }
@Mapper public interface OrderMapper { @Select("select * from order_detail where id = #{orderId}") OrderInfo selectOrderById (Integer orderId) ; }
运行访问 127.0.0.1:8080/order/1 测试。
远程调用 在查询订单信息时,其中包括商品信息,因此,我们需要根据订单里的商品 ID 获取商品的详细信息。
那么,订单服务如何访问商品服务呢?之前我们在浏览器发送 HTTP 请求获取商品详细信息,同样的,也可以在 order-service 服务中向 product-service 服务发送一个 HTTP 请求,获取返回结果,并与订单信息整合,最后一起返回。
Spring 框架提供了 RestTemplate 来帮助我们发送 HTTP 请求,因此,我们定义 RestTemplate:
@Configuration public class BeanConfig { @Bean public RestTemplate restTemplate () { return new RestTemplate (); } }
对于接收的 ProductInfo,需要在 order-service 中创建商品信息类(与商品服务中的类结构一致)。
在订单信息 OrderInfo 中添加商品信息字段:
@Data public class OrderInfo { private Integer id; private Integer userId; private Integer productId; private Integer num; private Integer price; private Integer deleteFlag; private Date createTime; private Date updateTime; private ProductInfo productInfo; }
修改 OrderService,注入 RestTemplate 并进行远程调用:
@Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private RestTemplate restTemplate; public OrderInfo findOrderInfoById (Integer orderId) { OrderInfo orderInfo = orderMapper.selectOrderById(orderId); String url = "http://127.0.0.1:9090/product/" + orderInfo.getProductId(); ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class); orderInfo.setProductInfo(productInfo); return orderInfo; } }
再次访问 127.0.0.1:8080/order/1 验证是否包含商品信息。
此时,虽然成功完成两个模块之间的远程通信,但项目中还存在着不少问题:
远程调用时,URL 的 IP 和端口号是写死的(127.0.0.1:9090),如果需要更换 IP,需要修改代码,是否可以实现让调用方不依赖服务提供方的 IP?
多机部署时,如何实现压力分摊?
商品信息代码重复编写,是否可以实现不同模块重复代码共用?
在之后的文章中,我们就来进一步学习如何使用 Spring Cloud 中的组件来解决这些问题。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online