零基础学Spring Cloud微服务完整指南
开篇:为什么要写这篇教程?
大家好,我是一名211计算机专业的在读研究生,平时喜欢写技术博客帮助新人入门。最近收到很多学弟学妹的私信,说想学Spring Cloud但不知道从哪开始,网上的教程要么太老要么太深,看得一头雾水。
我当初学的时候也是这样,对着官方文档啃了三天三夜,结果连个服务注册都跑不起来。所以今天这篇教程,我会用最通俗的语言,带你从零开始搭建一个完整的Spring Cloud微服务项目。
Spring Cloud是什么?
简单来说,Spring Cloud是一套微服务架构的开发工具包。它基于Spring Boot,提供了一系列开箱即用的组件,帮你解决微服务开发中的各种问题:
| 问题 | Spring Cloud解决方案 |
|---|---|
| 服务太多怎么管理? | 服务注册与发现(Nacos/Eureka) |
| 服务之间怎么调用? | 远程调用(OpenFeign) |
| 流量太大怎么办? | 负载均衡(LoadBalancer) |
| 某个服务挂了怎么办? | 熔断降级(Sentinel) |
| 配置太多怎么管理? | 配置中心(Nacos Config) |
| 请求怎么路由? | 网关(Gateway) |
一句话总结:Spring Cloud就是帮你把微服务架构中那些麻烦事都解决了的工具箱。
环境准备:工欲善其事,必先利其器
在开始之前,我们需要准备好开发环境。我当初就是环境没配好,折腾了一整天。
1. 基础环境清单
| 工具 | 版本要求 | 说明 |
|---|---|---|
| JDK | 1.8 或 17 | 推荐JDK 17,但JDK 8兼容性更好 |
| Maven | 3.6+ | 项目构建工具 |
| IntelliJ IDEA | 2022+ | 推荐使用Ultimate版 |
| MySQL | 5.7 或 8.0 | 数据库 |
| Nacos | 2.1+ | 服务注册与配置中心 |
2. JDK安装与配置
# 验证JDK是否安装成功
java -version
# 验证Maven是否安装成功
mvn -version
3. Nacos下载与启动
Nacos是阿里巴巴开源的服务注册与配置中心,我们用它来替代老版本的Eureka。
# 下载Nacos(以Windows为例)
# 访问 https://github.com/alibaba/nacos/releases
# 下载 nacos-server-2.x.zip
# 解压后,进入bin目录,单机模式启动
# Windows:
startup.cmd -m standalone
# Linux/Mac:
sh startup.sh -m standalone
启动成功后,访问 http://localhost:8848/nacos,默认账号密码都是 nacos。
避坑指南:Nacos 2.x版本需要JDK 1.8以上,如果你用的是JDK 17,可能会遇到兼容性问题,建议先用JDK 8或11。
核心概念:用大白话理解微服务
在动手写代码之前,我们先搞清楚几个核心概念。我当初就是概念没搞清,代码写得一塌糊涂。
什么是微服务?
想象你在开一家大餐厅:
- 单体架构:所有厨师在一个厨房,所有菜都在一个锅里炒。菜品多了,厨房就乱了。
- 微服务架构:把厨房分成炒菜区、凉菜区、面点区,每个区域独立运作,互不干扰。
微服务就是把一个大系统拆分成多个小服务,每个服务独立部署、独立运行。
微服务的核心角色
┌─────────────────────────────────────────────────────────┐
│ 客户端请求 │
└───────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────┐
│ API网关(Gateway) │ ← 统一入口,路由转发
└────────┬────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 用户服务 │ │ 订单服务 │ │ 商品服务 │ ← 各个微服务
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└─────────────┼─────────────┘
▼
┌─────────────────┐
│ Nacos注册中心 │ ← 服务注册与发现
└─────────────────┘
用表格来对比一下各个组件的作用:
| 组件 | 作用 | 通俗比喻 |
|---|---|---|
| Nacos | 服务注册与发现 | 通讯录,记录每个服务在哪 |
| OpenFeign | 服务间调用 | 打电话,服务A找服务B办事 |
| Gateway | API网关 | 前台接待,统一入口 |
| Sentinel | 熔断降级 | 保险丝,防止系统雪崩 |
| LoadBalancer | 负载均衡 | 排队叫号,均匀分配请求 |
实战项目:手把手搭建微服务
理论讲完了,我们开始动手!今天我们要搭建一个简单的电商微服务项目,包含以下模块:
cloud-demo(父工程)
├── cloud-common (公共模块)
├── cloud-gateway (网关服务)
├── cloud-user (用户服务)
├── cloud-order (订单服务)
└── cloud-product (商品服务)
第一步:创建父工程
打开IDEA,创建一个Maven项目作为父工程。
pom.xml(父工程):
<?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>com.example</groupId>
<artifactId>cloud-demo</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<!-- 子模块 -->
<modules>
<module>cloud-common</module>
<module>cloud-gateway</module>
<module>cloud-user</module>
<module>cloud-order</module>
<module>cloud-product</module>
</modules>
<!-- 版本管理 -->
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.7.14</spring-boot.version>
<spring-cloud.version>2021.0.8</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version>
</properties>
<!-- 依赖版本管理 -->
<dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring Cloud Alibaba -->
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</build>
</project>
版本对应关系很重要! Spring Boot、Spring Cloud、Spring Cloud Alibaba三者版本必须匹配,否则会出现各种奇怪的报错。我当初就是版本没对齐,调了一整天bug。
第二步:创建公共模块 cloud-common
公共模块用来存放各个服务共用的代码,比如实体类、工具类等。
cloud-common/pom.xml:
<?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">
<parent>
<artifactId>cloud-demo</artifactId>
<groupId>com.example</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-common</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
创建一个通用的返回结果类:
package com.example.common;
import lombok.Data;
@Data
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMessage("success");
result.setData(data);
return result;
}
public static <T> Result<T> error(String message) {
Result<T> result = new Result<>();
result.setCode(500);
result.setMessage(message);
return result;
}
}
第三步:创建用户服务 cloud-user
这是我们的第一个微服务,提供用户相关的接口。
cloud-user/pom.xml:
<?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">
<parent>
<artifactId>cloud-demo</artifactId>
<groupId>com.example</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-user</artifactId>
<dependencies>
<!-- 公共模块 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>cloud-common</artifactId>
<version>1.0.0</version>
</dependency>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Nacos服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- LoadBalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
</project>
application.yml 配置文件:
server:
port: 8081
spring:
application:
name: cloud-user
cloud:
nacos:
discovery:
server-addr: localhost:8848
启动类:
package com.example.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient // 开启服务发现
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
用户实体类:
package com.example.user.entity;
import lombok.Data;
@Data
public class User {
private Long id;
private String username;
private String email;
}
用户Controller:
package com.example.user.controller;
import com.example.common.Result;
import com.example.user.entity.User;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable Long id) {
// 模拟从数据库查询
User user = new User();
user.setId(id);
user.setUsername("张三");
user.setEmail("zhangsan@example.com");
return Result.success(user);
}
@GetMapping("/list")
public Result<String> getUserList() {
return Result.success("用户列表数据");
}
}
启动服务后,访问 http://localhost:8081/user/1,就能看到返回的用户信息了。
同时,打开Nacos控制台 http://localhost:8848/nacos,在「服务管理」-「服务列表」中,你应该能看到 cloud-user 服务已经注册成功了。
第四步:创建商品服务 cloud-product
商品服务提供商品查询接口,后续订单服务会调用它。
cloud-product/pom.xml:与cloud-user类似,不再重复。
application.yml:
server:
port: 8082
spring:
application:
name: cloud-product
cloud:
nacos:
discovery:
server-addr: localhost:8848
商品实体类:
package com.example.product.entity;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class Product {
private Long id;
private String name;
private BigDecimal price;
private Integer stock;
}
商品Controller:
package com.example.product.controller;
import com.example.common.Result;
import com.example.product.entity.Product;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
@RestController
@RequestMapping("/product")
public class ProductController {
@GetMapping("/{id}")
public Result<Product> getProductById(@PathVariable Long id) {
Product product = new Product();
product.setId(id);
product.setName("Java编程思想");
product.setPrice(new BigDecimal("99.00"));
product.setStock(100);
return Result.success(product);
}
}
第五步:创建订单服务 cloud-order(重点!)
订单服务需要调用用户服务和商品服务,这是微服务间通信的核心场景。
application.yml:
server:
port: 8083
spring:
application:
name: cloud-order
cloud:
nacos:
discovery:
server-addr: localhost:8848
订单实体类:
package com.example.order.entity;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class Order {
private Long id;
private Long userId;
private Long productId;
private String userName;
private String productName;
private BigDecimal totalPrice;
}
使用OpenFeign调用其他服务:
首先,定义Feign客户端接口来调用用户服务:
package com.example.order.feign;
import com.example.common.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Map;
@FeignClient(name = "cloud-user") // 指定要调用的服务名
public interface UserFeignClient {
@GetMapping("/user/{id}")
Result<Map<String, Object>> getUserById(@PathVariable("id") Long id);
}
同样,定义调用商品服务的Feign客户端:
package com.example.order.feign;
import com.example.common.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Map;
@FeignClient(name = "cloud-product")
public interface ProductFeignClient {
@GetMapping("/product/{id}")
Result<Map<String, Object>> getProductById(@PathVariable("id") Long id);
}
订单Controller:
package com.example.order.controller;
import com.example.common.Result;
import com.example.order.entity.Order;
import com.example.order.feign.ProductFeignClient;
import com.example.order.feign.UserFeignClient;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Map;
@RestController
@RequestMapping("/order")
public class OrderController {
@Resource
private UserFeignClient userFeignClient;
@Resource
private ProductFeignClient productFeignClient;
@GetMapping("/{id}")
public Result<Order> createOrder(@PathVariable Long id,
@RequestParam Long userId,
@RequestParam Long productId) {
// 1. 调用用户服务获取用户信息
Result<Map<String, Object>> userResult = userFeignClient.getUserById(userId);
Map<String, Object> userData = userResult.getData();
String userName = (String) userData.get("username");
// 2. 调用商品服务获取商品信息
Result<Map<String, Object>> productResult = productFeignClient.getProductById(productId);
Map<String, Object> productData = productResult.getData();
String productName = (String) productData.get("name");
BigDecimal price = new BigDecimal(productData.get("price").toString());
// 3. 组装订单信息
Order order = new Order();
order.setId(id);
order.setUserId(userId);
order.setProductId(productId);
order.setUserName(userName);
order.setProductName(productName);
order.setTotalPrice(price);
return Result.success(order);
}
}
启动类别忘了加@EnableFeignClients:
package com.example.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients // 开启Feign客户端
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
启动所有服务后,访问 http://localhost:8083/order/1?userId=1&productId=1,你会看到订单服务成功调用了用户服务和商品服务,并返回了完整的订单信息。
第六步:创建API网关 cloud-gateway
网关是所有请求的统一入口,负责路由转发。
cloud-gateway/pom.xml:
<?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">
<parent>
<artifactId>cloud-demo</artifactId>
<groupId>com.example</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-gateway</artifactId>
<dependencies>
<!-- Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- LoadBalancer -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
</project>
注意:Gateway模块不能引入
spring-boot-starter-web,因为Gateway基于WebFlux,和Web模块冲突!这是我踩过的一个大坑。
application.yml:
server:
port: 9000
spring:
application:
name: cloud-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
# 用户服务路由
- id: user-route
uri: lb://cloud-user
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1
# 商品服务路由
- id: product-route
uri: lb://cloud-product
predicates:
- Path=/api/product/**
filters:
- StripPrefix=1
# 订单服务路由
- id: order-route
uri: lb://cloud-order
predicates:
- Path=/api/order/**
filters:
- StripPrefix=1
路由配置解释:
| 配置项 | 含义 |
|---|---|
| id | 路由唯一标识 |
| uri | 目标服务地址,lb:// 表示使用负载均衡 |
| predicates | 匹配条件,Path=/api/user/** 表示匹配以/api/user/开头的请求 |
| filters | 过滤器,StripPrefix=1 表示去掉第一层路径 |
启动类:
package com.example.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
现在,你可以通过网关来访问所有服务了:
# 原来访问用户服务
http://localhost:8081/user/1
# 现在通过网关访问
http://localhost:9000/api/user/user/1
常见问题:新手踩坑指南
问题1:服务注册不上Nacos
现象:启动服务后,Nacos控制台看不到服务。
排查步骤:
- 检查Nacos是否启动成功
- 检查
application.yml中的server-addr是否正确 - 检查是否加了
@EnableDiscoveryClient注解 - 检查依赖是否引入正确
# 正确的配置
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # 注意是8848端口
问题2:OpenFeign调用报错
现象:Feign调用时报 No Feign Client for loadBalancing defined。
解决方案:引入LoadBalancer依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
Spring Cloud 2020版本之后,Ribbon被移除了,必须手动引入LoadBalancer。
问题3:Gateway路由不生效
现象:通过网关访问接口返回404。
排查步骤:
- 检查路由配置中的
Path是否正确 - 检查
StripPrefix的值是否正确 - 查看网关启动日志,确认路由是否加载成功
# 错误示例:路径不匹配
predicates:
- Path=/user/** # 请求路径是 /api/user/user/1,不匹配
# 正确示例
predicates:
- Path=/api/user/** # 匹配 /api/user/ 开头的路径
filters:
- StripPrefix=1 # 去掉 /api,变成 /user/user/1
问题4:版本冲突
现象:各种 ClassNotFoundException 或 NoSuchMethodError。
解决方案:严格按照版本对应关系来配置。
| Spring Boot | Spring Cloud | Spring Cloud Alibaba |
|---|---|---|
| 2.6.x | 2021.0.x | 2021.0.4.0 |
| 2.7.x | 2021.0.x | 2021.0.5.0 |
| 3.0.x | 2022.0.x | 2022.0.0.0 |
学习建议:下一步怎么走?
恭喜你!到这里你已经完成了一个基础的Spring Cloud微服务项目。但这只是开始,微服务的世界还有很多东西要学。
推荐学习路径
第一阶段(已完成):
├── 服务注册与发现(Nacos)
├── 服务间调用(OpenFeign)
├── API网关(Gateway)
└── 负载均衡(LoadBalancer)
第二阶段(建议接下来学):
├── 配置中心(Nacos Config)
├── 熔断降级(Sentinel)
├── 分布式事务(Seata)
└── 链路追踪(SkyWalking/Sleuth)
第三阶段(进阶):
├── Docker容器化部署
├── Kubernetes编排
├── CI/CD自动化部署
└── 微服务监控与运维
我的学习建议
先跑通再理解:不要一开始就死磕原理,先把项目跑起来,有成就感了再深入。
多看官方文档:虽然官方文档枯燥,但最权威。遇到不懂的,先查官方文档。
动手做项目:光看教程没用,一定要自己动手写。可以把我这个项目扩展一下,加上数据库、加上更多的业务逻辑。
学会看日志:遇到问题不要慌,仔细看报错日志,90%的问题都能自己解决。
加入社区:遇到问题可以去Spring中文社区、CSDN、掘金等平台提问,也可以看看别人的解决方案。
避坑总结
| 坑 | 建议 |
|---|---|
| 版本不对齐 | 严格按照版本对应表配置 |
| Gateway引入Web依赖 | Gateway基于WebFlux,不能引入spring-boot-starter-web |
| 忘记加注解 | @EnableDiscoveryClient、@EnableFeignClients别忘加 |
| Nacos端口搞错 | Nacos主端口是8848,不是8080 |
| Feign调用没引入LB | Spring Cloud 2020+必须手动引入LoadBalancer |
结语
微服务架构并不神秘,Spring Cloud也只是帮你把复杂的事情简单化了。作为初学者,不要被各种概念吓到,一步一步来,先跑通一个小项目,再慢慢深入。
我当初也是从零开始,踩了无数坑才走到今天。希望这篇教程能帮你少走一些弯路。如果有任何问题,欢迎在评论区留言,我会尽力解答。
最后送大家一句话:编程是一门手艺活,唯手熟尔。 多写代码,多思考,你一定能成为优秀的开发者!
加油!💪


评论 0