告别单体应用:用SpringCloudAlibaba快速拆分出你的第一个微服务(Order/Stock实战)
告别单体应用:用SpringCloudAlibaba快速拆分出你的第一个微服务(Order/Stock实战)
电商系统在业务扩张时常常面临这样的困境:促销活动导致订单激增,连带库存查询压力暴增,整个系统响应变慢。传统单体架构下,订单和库存模块强耦合,任何一方的性能瓶颈都会拖累全局。这正是微服务架构大显身手的场景——将系统拆分为独立部署、独立扩展的服务单元。
本文将以电商中最典型的订单-库存交互为例,手把手带你用SpringCloudAlibaba实现服务拆分。不同于单纯的技术演示,我们将从业务痛点出发,先理解"为什么拆",再解决"怎么拆"。最终你会得到两个独立运行的Spring Boot服务:Order服务通过HTTP调用Stock服务完成库存扣减。
1. 环境准备与项目骨架搭建
在开始编码前,需要确保本地环境满足以下条件:
- JDK 1.8或更高版本
- Maven 3.6+
- IntelliJ IDEA(推荐2021.3+版本)
创建父工程是微服务项目的最佳实践,它能够统一管理子模块的依赖版本。新建一个名为springcloudalibaba的Maven项目,关键配置如下:
<!-- 父工程pom.xml关键片段 --> <packaging>pom</packaging> <modules> <module>order</module> <module>stock</module> </modules> <properties> <spring-boot.version>2.7.7</spring-boot.version> <spring-cloud.version>2021.0.5</spring-cloud.version> <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version> </properties> <dependencyManagement> <dependencies> <!-- 统一管理Spring Boot/Cloud/Alibaba版本 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>注意:父工程只需要做依赖管理,不要添加不必要的依赖。实际依赖应该在各个子模块中按需引入。
2. 订单服务(Order)实现
订单服务作为消费者,需要调用库存服务完成商品库存检查。我们先创建order子模块:
mvn archetype:generate -DgroupId=com.example \ -DartifactId=order \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false关键依赖配置:
<dependencies> <!-- Web服务基础依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 用于服务间调用 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies>订单服务的核心控制器实现:
@RestController @RequestMapping("/orders") public class OrderController { private final RestTemplate restTemplate; // 构造器注入更利于测试 public OrderController(RestTemplateBuilder builder) { this.restTemplate = builder.build(); } @PostMapping public String createOrder(@RequestBody OrderRequest request) { // 调用库存服务 String stockResult = restTemplate.postForObject( "http://localhost:8081/stocks/deduct", request.getItems(), String.class ); if ("SUCCESS".equals(stockResult)) { return "订单创建成功"; } else { throw new RuntimeException("库存不足"); } } }配置服务端口(application.yml):
server: port: 8080 spring: application: name: order-service3. 库存服务(Stock)实现
库存服务作为提供者,需要暴露库存扣减接口。创建stock子模块的方式与order类似,核心控制器如下:
@RestController @RequestMapping("/stocks") public class StockController { @PostMapping("/deduct") public String deductStock(@RequestBody List<OrderItem> items) { items.forEach(item -> { System.out.printf("扣减商品%s库存%d件%n", item.getSkuCode(), item.getQuantity()); }); return "SUCCESS"; } }库存服务的配置(application.yml):
server: port: 8081 spring: application: name: stock-service4. 服务通信与测试验证
启动两个服务后,我们可以通过cURL测试服务调用:
# 测试订单创建 curl -X POST http://localhost:8080/orders \ -H "Content-Type: application/json" \ -d '[{"skuCode":"IPHONE_13","quantity":1}]'此时在订单服务控制台可以看到:
开始创建订单... 调用库存服务结果:SUCCESS 订单创建完成而在库存服务控制台会打印:
扣减商品IPHONE_13库存1件这种硬编码URL的方式(http://localhost:8081)存在明显问题:
- 服务地址变更需要修改代码
- 无法实现负载均衡
- 没有故障转移机制
这正是后续需要引入服务注册中心(如Nacos)和服务调用组件(如OpenFeign)的原因。不过在当前阶段,这种简单实现已经帮助我们理解了微服务最核心的特征:
- 独立部署:Order和Stock服务有各自独立的进程
- 明确边界:每个服务有清晰的职责划分
- 轻量通信:通过HTTP API进行协作
5. 常见问题与调试技巧
在微服务拆分初期,开发者常会遇到以下典型问题:
问题1:服务调用超时
现象:订单服务调用库存服务时长时间无响应
解决方案:
@Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder .setConnectTimeout(Duration.ofSeconds(3)) .setReadTimeout(Duration.ofSeconds(5)) .build(); }问题2:端口冲突
现象:启动时报端口已被占用
快速排查命令:
# Linux/Mac lsof -i :8080 # Windows netstat -ano | findstr 8080问题3:循环依赖
反模式示例:
- 订单服务调用库存服务
- 库存服务又回调订单服务查询订单状态
正确做法:
- 通过领域事件(Domain Events)解耦
- 使用消息队列异步处理
6. 从简单实现到生产就绪
虽然当前实现简陋,但已经包含了微服务的核心要素。要使其达到生产级别,还需要考虑:
- 服务发现:用Nacos替代硬编码URL
- 容错处理:引入Sentinel防止雪崩
- API网关:统一入口管理(推荐Spring Cloud Gateway)
- 配置中心:动态调整参数
- 分布式追踪:使用Sleuth+Zipkin
一个典型的演进路线可能是:
- 服务拆分 → 2. 服务注册发现 → 3. 负载均衡 → 4. 容错处理 → 5. 统一网关
在IDEA中同时启动多个服务的技巧:
- 打开Run/Debug Configurations
- 添加Compound配置
- 将OrderApplication和StockApplication纳入组合
调试微服务时,合理使用日志级别能事半功倍。建议在开发环境配置:
logging: level: org.springframework.web: DEBUG com.example: TRACE随着服务数量增加,可以考虑使用Arthas等工具进行线上诊断。例如查看某个服务的HTTP接口:
# 安装Arthas后 watch org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping getHandlerMethods