SpringCloudAlibaba_Nacos_Feign详解
Spring Cloud Alibaba:Nacos + Feign 详解
课程位置:第四阶段「微服务项目 + AI 应用」第 2 课
前置知识:微服务体系架构、Spring Boot、Maven、RESTful、HTTP、Spring MVC
本课主题:Spring Cloud Alibaba 组件——Nacos|Feign
下一课:Spring Cloud Alibaba——Sentinel
学习目标:从两个普通 Spring Boot 服务开始,亲自体验“写死 URL”的问题,再逐步完成 Nacos 服务注册、服务发现、Spring Cloud LoadBalancer、OpenFeign 声明式远程调用、多实例负载均衡、Namespace/Group/DataId、Nacos 配置中心、动态刷新、超时配置和常见错误排查。
一、这一课到底要解决什么问题
上一课我们已经知道:
Nacos
= 找服务 + 管配置
Feign
= 调服务
Sentinel
= 保护服务
Gateway
= 统一入口
这一课只做前两个:
Nacos
+
Feign
学习顺序非常重要:
先不用 Nacos
↓
先写死 URL 调用
↓
亲自看到地址写死的问题
↓
再引入 Nacos
↓
服务注册
↓
服务发现
↓
LoadBalancer
↓
再引入 Feign
↓
把 HTTP 调用封装成 Java 接口
这样你会真正理解:
为什么需要这些组件
而不是只会复制配置。
二、我们今天搭什么项目
创建父工程:
spring-cloud-demo
下面两个服务:
spring-cloud-demo
├─ user-service
└─ order-service
职责:
user-service
负责用户信息
order-service
负责订单信息
业务需求:
查询订单详情
↓
订单服务拿到 userId
↓
order-service 调用 user-service
↓
获取用户姓名
↓
组装订单详情
最终浏览器访问:
GET /orders/1001
返回类似:
{
"orderId": 1001,
"userId": 1,
"userName": "用户-1",
"productName": "机油套餐"
}
三、先确定版本,不要随便复制旧教程
微服务版本不能乱混。
本课使用 Spring Boot 3 的一条明确教学基线:
JDK 17
Spring Boot 3.5.x
Spring Cloud 2025.0.x
Spring Cloud Alibaba 2025.0.x
Nacos 3.x
Spring Cloud Alibaba 官方兼容表明确给出:
Spring Cloud Alibaba 2025.0.0.0
→ Spring Cloud 2025.0.0
→ Spring Boot 3.5.0
→ Nacos Client 3.0.3
所以课程示例可以使用:
Spring Boot 3.5.0
Spring Cloud 2025.0.0
Spring Cloud Alibaba 2025.0.0.0
JDK17
注意:
这是教学基线
不是说以后永远只能用这些 patch 版本
真正项目应该:
先确定 Boot
↓
查官方兼容矩阵
↓
使用 BOM
↓
让 BOM 管理组件版本
不要:
Nacos 一个版本
Feign 一个版本
Cloud 一个版本
全部手工乱填
四、BOM 是什么
BOM:
Bill of Materials
你可以理解为:
一整套经过搭配的依赖版本清单
微服务组件很多:
Spring Cloud Commons
OpenFeign
LoadBalancer
Nacos
Sentinel
Gateway
如果每个都自己写版本:
非常容易出现版本冲突
五、父工程 pom.xml
创建 Maven 父工程:
spring-cloud-demo
<?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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2025.0.0</spring-cloud.version>
<spring-cloud-alibaba.version>2025.0.0.0</spring-cloud-alibaba.version>
</properties>
<modules>
<module>user-service</module>
<module>order-service</module>
</modules>
<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>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>
</project>
六、为什么 packaging 是 pom
父工程:
主要负责:
聚合模块
管理版本
它自己不作为:
Spring Boot 服务
所以:
<packaging>pom</packaging>
七、创建 user-service
IDEA:
右键父工程
→ New
→ Module
→ Maven
模块:
user-service
第一阶段只加 Web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
八、user-service 配置
application.yml:
server:
port: 8081
spring:
application:
name: user-service
这里:
spring.application.name
以后非常重要。
它相当于:
微服务逻辑名字
Nacos、Feign、Gateway 都会围绕它工作。
九、UserVO
课程先不接数据库:
public class UserVO {
private Long id;
private String name;
public UserVO() {
}
public UserVO(
Long id,
String name
) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(
String name
) {
this.name = name;
}
}
十、UserController
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public UserVO getById(
@PathVariable
Long id
) {
return new UserVO(
id,
"用户-" + id
);
}
}
启动:
http://localhost:8081/users/1
应该返回:
{
"id": 1,
"name": "用户-1"
}
十一、创建 order-service
模块:
order-service
第一阶段同样:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置:
server:
port: 8082
spring:
application:
name: order-service
十二、先用最原始方式远程调用
为了真正理解 Nacos 的必要性,我们先写:
固定地址
order-service 创建:
@Configuration
public class RestConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
十三、OrderVO
public class OrderVO {
private Long orderId;
private Long userId;
private String userName;
private String productName;
// getter / setter
}
十四、固定 localhost 调用
@RestController
@RequestMapping("/orders")
public class OrderController {
private final RestTemplate
restTemplate;
public OrderController(
RestTemplate restTemplate
) {
this.restTemplate =
restTemplate;
}
@GetMapping("/{id}")
public OrderVO getById(
@PathVariable Long id
) {
Long userId = 1L;
UserVO user =
restTemplate.getForObject(
"http://localhost:8081/users/"
+ userId,
UserVO.class
);
OrderVO vo =
new OrderVO();
vo.setOrderId(id);
vo.setUserId(userId);
vo.setUserName(
user.getName()
);
vo.setProductName(
"机油套餐"
);
return vo;
}
}
十五、现在为什么能运行
启动:
user-service : 8081
order-service : 8082
访问:
http://localhost:8082/orders/1001
调用链:
Browser
↓
order-service
↓
RestTemplate
↓
localhost:8081
↓
user-service
十六、既然能运行,为什么还需要 Nacos
因为这里:
"http://localhost:8081"
被写死了。
如果 user-service:
8081 → 8085
order-service:
也必须改代码/配置
十七、真实生产更麻烦
user-service 可能扩成:
10.0.0.11:8081
10.0.0.12:8081
10.0.0.13:8081
并且实例会:
扩容
缩容
重启
故障
不能要求程序员:
手工维护所有服务 IP
十八、Nacos Discovery
Nacos 服务发现就是为了解决:
服务在哪里?
核心:
Provider 注册
↓
Nacos 保存实例
↓
Consumer 根据服务名发现实例
十九、安装 Nacos
当前 Nacos 已进入:
3.x
官方站点截至本笔记整理时显示稳定最新版:
3.2.4
但课程更重要的规则是:
按 Spring Cloud Alibaba
兼容矩阵选择 Nacos 3.x
而不是只追:
最新版
二十、Windows 启动 Nacos
解压后进入:
nacos/bin
执行:
startup.cmd -m standalone
standalone:
单机模式
适合:
本地学习
Linux / macOS:
sh startup.sh -m standalone
二十一、Nacos 安全提醒
官方 Quick Start 的 standalone:
只适合测试
生产应考虑:
集群
认证
网络隔离
不要:
无认证 Nacos 直接暴露公网
新版 Nacos 3.x 的认证初始化方式可能与老教程不同,因此:
不要死记老教程默认账号密码
以当前 Nacos 版本控制台提示为准。
二十二、Nacos 常见端口
本地常用:
8848
控制台通常访问:
http://127.0.0.1:8848/nacos
具体以当前版本启动日志为准。
二十三、user-service 接入 Discovery
pom 加:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>
spring-cloud-starter-alibaba-nacos-discovery
</artifactId>
</dependency>
注意:
不要写 version
因为:
父 BOM 已经管理
二十四、user-service application.yml
server:
port: 8081
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
二十五、@EnableDiscoveryClient
课程中按官方快速入门写:
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(
String[] args
) {
SpringApplication.run(
UserServiceApplication.class,
args
);
}
}
现代 Spring Cloud 自动配置能力较强,部分组合中即使不显式写该注解也能自动注册。
但是课程阶段保留它更容易理解:
这个应用参与服务发现
二十六、启动后验证
Nacos 服务列表应该看到:
user-service
进入详情可以观察:
IP
Port
健康状态
权重
集群
Metadata
二十七、Nacos 注册的到底是什么
不是:
/users/{id}
这样的 Controller 路径。
Nacos 主要注册:
服务实例
例如:
serviceName:
user-service
ip:
127.0.0.1
port:
8081
Controller 路径仍由:
Spring MVC
处理。
二十八、order-service 接入 Discovery
同样添加:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>
spring-cloud-starter-alibaba-nacos-discovery
</artifactId>
</dependency>
配置:
server:
port: 8082
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
启动后:
Nacos
├─ user-service
└─ order-service
二十九、现在是不是已经会自动远程调用
不是。
Nacos 解决的是:
找到服务实例
并不会自动替你:
调用 Controller
三十、DiscoveryClient
Spring Cloud 提供:
DiscoveryClient
可以手工查询实例。
private final DiscoveryClient
discoveryClient;
public DemoController(
DiscoveryClient discoveryClient
) {
this.discoveryClient =
discoveryClient;
}
三十一、查询实例
List<ServiceInstance> instances =
discoveryClient.getInstances(
"user-service"
);
打印:
for (
ServiceInstance instance
:
instances
) {
System.out.println(
instance.getHost()
+
":"
+
instance.getPort()
);
}
三十二、手工选择实例的问题
可以:
ServiceInstance instance =
instances.get(0);
然后拼:
String url =
"http://"
+ instance.getHost()
+ ":"
+ instance.getPort()
+ "/users/"
+ userId;
但是这意味着:
你自己负责选实例
如果有:
5 个实例
到底选哪个?
于是引出:
LoadBalancer
三十三、Spring Cloud LoadBalancer
加入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>
spring-cloud-starter-loadbalancer
</artifactId>
</dependency>
有时其他 Starter 会间接带入,但课程建议:
显式加入
更容易知道它的职责。
三十四、@LoadBalanced
@Configuration
public class RestConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
三十五、URL 从 IP 变服务名
原来:
http://localhost:8081/users/1
现在:
http://user-service/users/1
代码:
UserVO user =
restTemplate.getForObject(
"http://user-service/users/"
+ userId,
UserVO.class
);
三十六、user-service 是域名吗
不是普通 DNS 域名。
这里:
user-service
是:
服务名
三十七、背后调用链
RestTemplate
↓
@LoadBalanced
↓
识别 user-service
↓
DiscoveryClient
↓
Nacos 返回实例列表
↓
LoadBalancer 选择实例
↓
真实 IP:Port
↓
HTTP 请求
三十八、Nacos 和 LoadBalancer 分工
一定记:
Nacos:
有哪些实例?
LoadBalancer:
选哪个实例?
三十九、多实例实验
启动:
user-service:8081
再复制 Run Configuration:
user-service:8083
第二个启动参数:
--server.port=8083
两者都保持:
spring:
application:
name: user-service
四十、为什么服务名必须相同
你要的是:
同一个 user-service
的两个实例
不是:
两个不同服务
四十一、Nacos 中应该看到
user-service
下面两个实例:
127.0.0.1:8081
127.0.0.1:8083
四十二、为了观察实例
UserController 加:
@Value("${server.port}")
private Integer port;
返回:
return new UserVO(
id,
"用户-" + id
+ "-来自端口-"
+ port
);
反复访问:
order-service
观察响应来自:
8081 / 8083
四十三、什么叫客户端负载均衡
实例选择发生在:
Consumer
也就是:
order-service
这一侧。
所以叫:
客户端负载均衡
四十四、现在为什么还要 Feign
虽然已经可以:
restTemplate.getForObject(
"http://user-service/users/" + id,
UserVO.class
)
但业务多了以后还是很麻烦:
拼 URL
指定 Method
拼查询参数
处理 Body
处理响应类型
所以我们需要:
OpenFeign
四十五、OpenFeign 是什么
Feign 是:
声明式 HTTP 客户端
核心思想:
定义 Java 接口
↓
使用注解描述远程 HTTP API
↓
Spring 生成代理对象
↓
调用接口方法
↓
自动发 HTTP 请求
四十六、当前官方状态要知道
Spring 官方目前将 Spring Cloud OpenFeign 定位为:
feature-complete
也就是:
功能已经比较成熟
后续主要以维护和 Bug 修复为主
Spring 官方新方向更推荐:
Spring HTTP Service Clients
但是:
OpenFeign 在大量现有 Spring Cloud
和若依 Cloud 项目里仍然非常常见
所以:
这门课依然必须掌握 Feign
四十七、order-service 引入 Feign
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>
spring-cloud-starter-openfeign
</artifactId>
</dependency>
同样:
不要自己写版本
交给:
Spring Cloud BOM
管理。
四十八、启动类开启 Feign 扫描
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
public static void main(
String[] args
) {
SpringApplication.run(
OrderServiceApplication.class,
args
);
}
}
四十九、创建 UserClient
@FeignClient(
name = "user-service"
)
public interface UserClient {
@GetMapping(
"/users/{id}"
)
UserRemoteDTO getById(
@PathVariable("id")
Long id
);
}
五十、@FeignClient name 到底是什么
name = "user-service"
这里必须对应 Provider:
spring:
application:
name: user-service
也就是说:
Feign 用服务名寻找 Provider
五十一、内部服务不要随便写 url
错误示例:
@FeignClient(
name = "user-service",
url = "http://localhost:8081"
)
这样:
地址又写死了
会绕过:
基于服务名的发现式调用
五十二、什么时候 url 合理
例如:
第三方天气 API
第三方地图 API
第三方短信 API
它们:
根本不会注册到你的 Nacos
这时:
固定 URL / 配置 URL
很合理。
五十三、发现式 Feign
内部服务:
@FeignClient(
name = "user-service"
)
不要固定 URL。
Spring Cloud 会结合:
服务发现
+
LoadBalancer
选择真实实例。
五十四、OrderService 改成 Feign
@Service
public class OrderService {
private final UserClient
userClient;
public OrderService(
UserClient userClient
) {
this.userClient =
userClient;
}
public OrderVO getById(
Long orderId
) {
Long userId = 1L;
UserRemoteDTO user =
userClient.getById(
userId
);
OrderVO vo =
new OrderVO();
vo.setOrderId(orderId);
vo.setUserId(userId);
vo.setUserName(
user.getName()
);
vo.setProductName(
"机油套餐"
);
return vo;
}
}
Controller:
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService
orderService;
public OrderController(
OrderService orderService
) {
this.orderService =
orderService;
}
@GetMapping("/{id}")
public OrderVO getById(
@PathVariable Long id
) {
return orderService
.getById(id);
}
}
五十五、为什么这样分层
推荐:
Controller
↓
Service
↓
Feign Client
Controller:
负责 HTTP
Service:
负责业务协调
Feign Client:
负责远程接口契约
不要把:
大量 Feign 调用
直接堆在 Controller 里。
五十六、Feign 看起来像本地方法
现在:
userClient.getById(id);
非常像:
userService.getById(id);
但本质完全不同。
本地:
JVM 方法调用
Feign:
网络 HTTP 调用
所以 Feign 仍然可能:
超时
连接失败
服务宕机
404
500
网络抖动
五十七、Feign 大致执行过程
调用 UserClient.getById
↓
Feign 动态代理
↓
读取 @GetMapping
↓
读取 @PathVariable
↓
根据 user-service 找服务实例
↓
LoadBalancer 选实例
↓
发送 HTTP GET
↓
Provider 返回 JSON
↓
反序列化 UserRemoteDTO
↓
返回调用方
五十八、Feign 和 MyBatis Mapper 很像
MyBatis:
public interface UserMapper
没有自己写实现类,
框架:
创建代理
Feign:
public interface UserClient
也没有自己写实现类,
Spring:
创建 HTTP 代理
可以记:
Mapper Interface
→ SQL Proxy
Feign Interface
→ HTTP Proxy
五十九、@PathVariable
Provider:
@GetMapping("/users/{id}")
public UserVO getById(
@PathVariable Long id
) {
}
Feign:
@GetMapping("/users/{id}")
UserRemoteDTO getById(
@PathVariable("id")
Long id
);
课程推荐:
显式写参数名
减少编译参数名差异造成的问题。
六十、@RequestParam
Provider:
@GetMapping("/users/search")
public List<UserVO> search(
@RequestParam String name
) {
}
Feign:
@GetMapping("/users/search")
List<UserRemoteDTO> search(
@RequestParam("name")
String name
);
六十一、@RequestBody
Provider:
@PostMapping("/users")
public UserVO create(
@RequestBody UserCreateDTO dto
) {
}
Feign:
@PostMapping("/users")
UserRemoteDTO create(
@RequestBody
UserCreateDTO dto
);
六十二、为什么远程 DTO 很重要
不要让 order-service:
直接依赖 user-service 数据库 Entity
例如 User Entity 可能有:
password
deleted
status
createTime
updateTime
内部字段
order-service 根本:
不应该知道
六十三、创建 UserRemoteDTO
public class UserRemoteDTO {
private Long id;
private String name;
// getter / setter
}
只暴露:
远程调用真正需要的数据
六十四、远程 DTO 是接口契约
它表达:
user-service 愿意给其他服务什么数据
不是:
user 表所有字段
六十五、项目大以后可以抽 api 模块
例如:
spring-cloud-demo
├─ user-api
├─ user-service
└─ order-service
user-api:
UserClient
UserRemoteDTO
远程接口公共常量
六十六、user-api 不应该放什么
不要放:
UserMapper
UserServiceImpl
数据库连接
User Entity 内部实现
API 模块目标:
只描述“别人怎么调用我”
六十七、为什么很多若依 Cloud 项目有 remote 模块
后面你会看到:
RemoteUserService
RemoteFileService
RemoteLogService
本质就是:
跨服务接口契约
学完 Feign 后就很好理解。
六十八、@EnableFeignClients 扫描问题
如果 UserClient 不在:
启动类默认包扫描范围
可能:
Spring 找不到 Feign Bean
可以:
@EnableFeignClients(
basePackages =
"com.example.api"
)
六十九、常见错误
No qualifying bean of type UserClient
排查:
1. 是否引入 OpenFeign Starter
2. 是否 @EnableFeignClients
3. Client 是否被扫描
4. Client 是否加 @FeignClient
七十、Feign 404
如果:
服务实例已经找到
但是返回:
404
通常先检查:
Feign 路径
Controller 路径
例如 Provider:
@RequestMapping("/users")
@GetMapping("/{id}")
完整:
/users/{id}
Feign 如果写:
/user/{id}
就:
404
七十一、Feign 405
HTTP Method 不一致。
例如 Provider:
POST
Feign:
GET
会:
405 Method Not Allowed
七十二、Feign 400
通常检查:
@RequestParam
@PathVariable
@RequestBody
JSON 格式
参数名
七十三、Feign 503 / 无实例
先看:
Nacos 是否有该服务
服务名是否一致
Namespace 是否一致
实例是否健康
LoadBalancer 是否存在
七十四、服务名拼写是高频 Bug
Provider:
spring:
application:
name: user-service
Feign:
@FeignClient(
name = "users-service"
)
只多一个:
s
就:
找不到服务
七十五、服务命名建议
统一:
全小写
使用短横线
业务语义清楚
例如:
user-service
order-service
travel-service
七十六、Feign 超时
远程调用必须有:
时间边界
当前 Spring Cloud OpenFeign 支持:
connectTimeout
readTimeout
七十七、全局默认超时
spring:
cloud:
openfeign:
client:
config:
default:
connectTimeout: 3000
readTimeout: 5000
loggerLevel: basic
七十八、指定 user-service
spring:
cloud:
openfeign:
client:
config:
user-service:
connectTimeout: 2000
readTimeout: 3000
七十九、connectTimeout
简单理解:
建立连接允许等待多久
八十、readTimeout
连接建立后:
等待远程响应允许多久
八十一、为什么不能超时设置特别大
例如:
60 秒
如果下游挂了:
大量线程都等 60 秒
调用方很容易:
被拖垮
八十二、为什么也不能特别小
例如:
20ms
正常网络波动:
都可能超时
正确做法:
根据接口历史延迟和业务要求
确定。
课程:
先用几秒级测试
八十三、超时实验
Provider:
@GetMapping("/slow/{id}")
public UserVO slow(
@PathVariable Long id
)
throws InterruptedException {
Thread.sleep(5000);
return new UserVO(
id,
"slow-user"
);
}
Feign:
readTimeout = 1000
调用:
应该触发读取超时
八十四、Feign 重试不能死背
不同 Feign / Spring Cloud 版本:
默认 Retryer 行为可能不同
不要永久记:
“Feign 默认一定重试 N 次”
正确工程思路:
如果要重试
先分析接口是否幂等
八十五、为什么写操作重试危险
例如:
POST /orders
第一次:
服务端已经创建成功
但网络响应:
丢了
Consumer 认为失败:
再次重试
如果没有幂等:
可能创建两个订单
八十六、查询接口更容易重试
例如:
GET /users/1
一般:
没有写副作用
相比 POST:
更容易安全重试
八十七、幂等手段
常见:
业务唯一号
数据库 UNIQUE
状态机
幂等 Token
请求去重
八十八、Feign 日志
调试时可以:
spring:
cloud:
openfeign:
client:
config:
user-service:
loggerLevel: full
logging:
level:
com.example.order.client.UserClient: DEBUG
八十九、日志级别
常见:
NONE
BASIC
HEADERS
FULL
九十、FULL 适合本地调试
生产不建议:
长期全部 FULL
原因:
日志很多
可能输出请求 Header
可能输出敏感数据
九十一、远程 N+1
一个非常重要的微服务性能问题。
错误:
for (
Order order
:
orders
) {
UserRemoteDTO user =
userClient.getById(
order.getUserId()
);
}
如果订单:
100 条
就:
100 次 Feign 请求
九十二、为什么远程 N+1 更危险
数据库 N+1:
多次 SQL
远程 N+1:
多次网络请求
+
下游服务压力
+
序列化
+
连接
成本更高。
九十三、优化成批量接口
user-service:
POST /users/batch
请求:
[1,2,3,4]
返回:
[
{
"id": 1,
"name": "张三"
},
{
"id": 2,
"name": "李四"
}
]
九十四、Consumer 组装 Map
Map<Long, UserRemoteDTO>
userMap =
users.stream()
.collect(
Collectors.toMap(
UserRemoteDTO::getId,
Function.identity()
)
);
然后:
按 userId O(1) 匹配
九十五、批量接口也要有限制
不要:
一次传 100 万 ID
可以限制:
100
500
1000
根据业务决定。
九十六、Nacos Namespace
Namespace 可以理解:
大隔离空间
最常见:
dev
test
prod
九十七、为什么环境隔离非常重要
不希望:
本地 order-service
误调用 production user-service
所以开发、测试、生产:
应该隔离
九十八、Discovery Namespace 配置
spring:
cloud:
nacos:
discovery:
namespace: YOUR_NAMESPACE_ID
注意:
通常填写 Namespace ID
不是:
控制台显示名称
九十九、这是高频错误
控制台:
名称:
dev
ID:
83f3xxxx-xxxx
配置通常应写:
83f3xxxx-xxxx
一百、Group
Namespace 内还可以继续:
分组
默认常见:
DEFAULT_GROUP
例如:
Namespace:
dev
Group:
TRAVEL_GROUP
一百零一、Namespace 和 Group
简单记:
Namespace
= 大隔离
Group
= 大隔离里面继续分组
一百零二、Cluster
服务发现还有:
cluster-name
例如:
BEIJING
SHANGHAI
课程现在:
先认识
以后做:
就近调用
灰度
时会用到更多。
一百零三、Metadata
实例可以携带:
metadata
例如:
spring:
cloud:
nacos:
discovery:
metadata:
version: v1
以后可以用于:
灰度路由
版本识别
实例筛选
一百零四、Nacos Discovery 不等于 Sentinel
Nacos 官方当前文档强调:
Service Discovery
主要回答:
“这个服务当前有哪些可用实例?”
它不是:
完整流量治理引擎
所以:
服务发现 → Nacos
流量保护 → Sentinel
一百零五、现在进入 Nacos Config
Nacos 除了:
服务注册发现
还可以:
集中配置
一百零六、为什么需要配置中心
一个单体:
1 个 application.yml
20 个微服务:
20 个 application.yml
例如数据库地址改变:
每个服务都改
非常麻烦。
一百零七、Nacos Config Starter
加入:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>
spring-cloud-starter-alibaba-nacos-config
</artifactId>
</dependency>
一百零八、最重要版本变化:spring.config.import
当前 Spring Cloud Alibaba 2025.x 推荐:
Spring Boot Config Data
也就是:
spring.config.import
一百零九、不要把 bootstrap.yml 当新项目主线
旧教程常见:
bootstrap.yml
然后:
spring.cloud.nacos.config...
但当前 SCA 已经向:
spring.config.import
迁移。
特别是:
2025.1.x
已经正式取消 Spring Cloud Bootstrap 的 Nacos 接入方式。
一百一十、application.yml 示例
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
group: DEFAULT_GROUP
config:
import:
- optional:nacos:user-service.yml?refreshEnabled=true
一百一十一、这里最关键
spring:
config:
import:
不是:
spring.cloud.config.import
注意层级。
一百一十二、DataId
user-service.yml
就是:
一份 Nacos 配置名称
一百一十三、Group
例如:
DEFAULT_GROUP
一百一十四、Namespace
例如:
dev Namespace ID
一百一十五、一份配置可以理解为
Namespace
+
Group
+
DataId
共同定位。
一百一十六、在 Nacos Console 新建配置
Data ID:
user-service.yml
Group:
DEFAULT_GROUP
配置内容:
user:
greeting: Hello from Nacos
一百一十七、Java 读取
@RestController
@RefreshScope
public class ConfigController {
@Value(
"${user.greeting}"
)
private String greeting;
@GetMapping(
"/config/greeting"
)
public String greeting() {
return greeting;
}
}
一百一十八、refreshEnabled
?refreshEnabled=true
表示:
监听配置变化
一百一十九、修改配置
Nacos:
user:
greeting: Hello V2
发布。
再访问:
/config/greeting
观察:
是否无需重启更新
一百二十、如果动态刷新没生效
排查:
refreshEnabled=true
DataId
Group
Namespace
配置是否发布
应用是否成功 import
Bean 是否需要 @RefreshScope
一百二十一、optional:nacos 是什么意思
配置:
spring:
config:
import:
- optional:nacos:user-service.yml
optional 表示:
外部配置不存在或暂时无法读取时
应用允许继续启动
如果写:
spring:
config:
import:
- nacos:user-service.yml
则:
配置加载失败
可能直接导致应用启动失败
一百二十二、什么时候用 optional
本地开发:
可以方便一些
但是关键生产配置:
是否允许缺失
需要按业务决定
例如数据库连接配置如果必须来自 Nacos:
就不应该随意 optional
一百二十三、optional 的调试陷阱
如果:
DataId 写错
但你用了:
optional:nacos
应用可能:
仍然启动成功
然后你误以为:
Nacos 配置已经加载
所以调试关键配置时可以:
暂时去掉 optional
让错误尽早暴露。
一百二十四、为什么现在不建议依赖 application.name 自动加载配置
Spring Cloud Alibaba 当前文档已经说明:
2023.0.1.3+
旧的:
shared-configs
extension-configs
默认 application.name 配置加载
已逐步废弃。
推荐:
spring.config.import
显式声明。
一百二十五、显式 import 的好处
一眼能看到:
当前服务依赖哪些配置
例如:
spring:
config:
import:
- nacos:common.yml
- nacos:user-service.yml
一百二十六、公共配置
多个服务都需要:
日志级别
公共业务开关
某些共享参数
可以放:
common.yml
一百二十七、服务专属配置
user-service 专属:
user-service.yml
order-service 专属:
order-service.yml
一百二十八、不要同一个 Key 到处定义
例如:
server.port
同时存在:
application.yml
common.yml
user-service.yml
很容易:
自己都不知道最终谁覆盖谁
一百二十九、配置原则
建议:
本地 application.yml
→ 启动 Nacos 必需的最小配置
common.yml
→ 多服务真正共享配置
service.yml
→ 单服务业务配置
一百三十、为什么 Nacos 地址通常本地保留
因为如果:
Nacos 地址本身
也只存放在 Nacos 中:
应用根本不知道去哪里找 Nacos
这是:
鸡生蛋问题
一百三十一、Discovery 与 Config Namespace 是两套配置
Discovery:
spring:
cloud:
nacos:
discovery:
namespace: YOUR_NAMESPACE_ID
Config:
spring:
cloud:
nacos:
config:
namespace: YOUR_NAMESPACE_ID
一百三十二、为什么经常忘一边
例如:
Discovery 在 dev
Config 还在 public
结果:
服务注册发现正常
但配置读取失败
反过来也可能发生。
一百三十三、建议统一环境变量
spring:
cloud:
nacos:
discovery:
namespace: ${NACOS_NAMESPACE:}
config:
namespace: ${NACOS_NAMESPACE:}
这样:
不同环境统一从外部注入
一百三十四、Nacos 服务发现常见配置
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: YOUR_NAMESPACE_ID
group: DEFAULT_GROUP
cluster-name: DEFAULT
weight: 1
一百三十五、weight 是什么
实例权重。
例如:
实例 A:100
实例 B:20
某些实例选择策略可以:
更倾向选择 A
课程先理解:
权重用于服务实例选择倾向
一百三十六、不要把 Nacos Weight 当完整灰度发布
完整灰度还可能需要:
版本 Metadata
Gateway 路由
调用策略
用户分流
这里只学基础。
一百三十七、多网卡问题
你的电脑可能同时有:
Wi-Fi
VMware
WSL
Docker
VPN
Nacos Client 自动选择本机 IP 时:
可能注册成错误网卡地址
一百三十八、多网卡问题表现
Nacos 中看到:
192.168.xxx.xxx
但是 Consumer:
访问不到
一百三十九、本地练习可指定 IP
spring:
cloud:
nacos:
discovery:
ip: 127.0.0.1
仅适合:
同一台电脑本地练习
一百四十、生产为什么不能随便 127.0.0.1
其他服务器调用:
127.0.0.1
表示:
它自己
不是 Provider。
生产必须注册:
其他服务真正可达的 IP/地址
一百四十一、Nacos HealthIndicator
Spring Cloud Alibaba 2025.0.x 起:
Nacos Config / Discovery HealthIndicator
默认关闭
需要时:
显式启用
例如 Discovery:
spring:
cloud:
nacos:
discovery:
health-indicator:
enabled: true
一百四十二、为什么不建议新手现在就全开
健康检查本身:
也会访问 Nacos
生产如果错误地把外部依赖健康作为:
Kubernetes liveness
可能放大故障。
课程本地:
先理解,不需要先开启
一百四十三、Nacos 挂了是不是所有服务马上全挂
不能简单回答:
是
客户端已经获取的实例信息:
通常会有本地缓存/订阅状态
已有服务在一段时间内:
可能仍能继续调用
但是:
新实例注册
实例变更
配置发布
配置刷新
会受影响。
所以生产 Nacos:
仍需要高可用
一百四十四、Feign 固定 URL 与服务发现调用
固定 URL
@FeignClient(
name = "weather-client",
url = "${weather.url}"
)
适合:
第三方 API
一百四十五、服务发现模式
@FeignClient(
name = "user-service"
)
适合:
内部注册中心服务
一百四十六、为什么不要混淆
如果内部服务设置固定:
url
你可能发现:
Nacos 明明两个实例
却永远只调用一个地址
因为:
你绕过了服务发现式 URL
一百四十七、完整 order-service 依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>
spring-boot-starter-web
</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>
spring-cloud-starter-alibaba-nacos-discovery
</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>
spring-cloud-starter-alibaba-nacos-config
</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>
spring-cloud-starter-loadbalancer
</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>
spring-cloud-starter-openfeign
</artifactId>
</dependency>
</dependencies>
一百四十八、为什么没有 version
父工程:
BOM
已经负责版本管理。
一百四十九、如果 Maven 提示 version missing
先检查:
父 POM dependencyManagement
模块 parent
BOM 是否真正加载
Maven 是否 Reload
不要第一反应:
自己给每个 Starter 填版本
一百五十、Maven 刷新
IDEA:
右侧 Maven
↓
Reload All Maven Projects
命令:
mvn clean package
一百五十一、依赖树
排查版本:
mvn dependency:tree
微服务里:
必须会
因为大量:
NoSuchMethodError
ClassNotFoundException
其实都是依赖问题。
一百五十二、NoSuchMethodError
含义常常是:
编译时看到一个版本的方法
运行时加载了另一个不兼容版本
优先排:
BOM
手动覆盖版本
依赖冲突
不要先:
重装 IDEA
一百五十三、ClassNotFoundException
重点检查:
缺 Starter
依赖被 exclude
组件在新版本拆包
版本不兼容
一百五十四、不要混入老 Ribbon
旧教程可能:
Ribbon
现代 Spring Cloud 主线:
Spring Cloud LoadBalancer
所以当前课程:
不再以 Ribbon 为主线
一百五十五、也不要混老 Hystrix
旧教程常见:
Hystrix
本课程 Spring Cloud Alibaba:
Sentinel
作为流量保护重点。
一百五十六、为什么旧教程特别危险
你搜索:
Nacos Feign 教程
可能搜到:
2019
Spring Boot 2.1
Ribbon
Hystrix
bootstrap.yml
然后放进:
Spring Boot 3.5
非常容易:
启动报错
一百五十七、看教程第一件事
先看:
发布日期
JDK
Spring Boot
Spring Cloud
Spring Cloud Alibaba
一百五十八、Feign RequestInterceptor
以后做微服务身份传递时经常遇到:
RequestInterceptor
例如:
@Bean
public RequestInterceptor
requestInterceptor() {
return template -> {
// 添加受控 Header
};
}
一百五十九、为什么现在不写“透传全部请求头”
因为:
客户端 Header 不一定全部可信
内部调用应该明确:
哪些 Header 可以传
哪些身份信息应该重新校验
后面若依 Cloud:
会看到完整做法
一百六十、服务发现不是授权
一定区分:
Discovery
=
我知道 user-service 在哪里
Authorization
=
我是否有权调用某个业务能力
Nacos:
不会自动替你解决 RBAC
一百六十一、Feign 不会自动解决权限
order-service 能调用:
user-service
不代表它可以:
绕过所有用户权限
业务服务仍然需要:
认证
授权
内部调用安全
一百六十二、Feign 不会自动解决事务
@Transactional
public void createOrder() {
orderMapper.insert(...);
userClient.xxx();
}
order-service 本地事务可以控制:
order_db
但不能自动控制:
user-service 的数据库事务
一百六十三、跨服务事务例子
user-service:
先成功修改了数据
随后 order-service:
最后抛异常
order-service:
只能回滚自己的本地事务
user-service 已提交的:
不会自动跟着回滚
一百六十四、这就是为什么后面要学
分布式事务
最终一致性
MQ
一百六十五、Feign 返回 null 的错误写法
错误:
try {
return userClient.getById(id);
} catch (Exception e) {
return null;
}
后面:
NullPointerException
而且:
根因被吞掉
一百六十六、正确异常处理思想
至少记录:
远程服务
接口
业务 ID
异常类型
然后判断:
这个业务能不能降级
下一课 Sentinel 会继续处理。
一百六十七、Nacos Config 安全
生产配置中心可能存:
数据库地址
Redis 地址
业务配置
如果包含:
密码
SecretKey
Token
需要更加严格:
认证
权限
Secret 管理
不要认为:
放进 Nacos 就自动安全
一百六十八、Nacos 生产网络
官方明确建议:
Nacos 作为内部基础设施
部署在隔离网络
不要:
直接暴露公共互联网
一百六十九、配置变更也需要审计
为什么?
因为生产事故不一定来自:
代码发布
也可能来自:
改错 Nacos 配置
所以企业里配置也要:
权限
Review
审计
变更记录
一百七十、哪些配置适合动态刷新
例如:
业务开关
推荐数量
超时阈值
展示文案
一百七十一、哪些不要随便热改
例如:
数据库 Schema
核心线程模型
复杂连接池参数
是否能安全热更新:
要看对应 Bean 实现
一百七十二、@RefreshScope 不是魔法
它解决:
Spring Bean 配置刷新
但不代表:
所有第三方连接和业务状态
都能安全无缝热更新
一百七十三、完整调试顺序
远程调用失败时:
第一步:
Provider 自己能不能访问
第二步:
Provider 有没有注册到 Nacos
第三步:
Consumer 能不能发现 Provider
第四步:
LoadBalancer 有没有实例
第五步:
Feign 路径和参数是否正确
第六步:
下游业务自己是否报错
一百七十四、排错口诀
先 Provider
再 Nacos
再 Consumer
再 Feign
不要:
四层一起乱改
一百七十五、测试一:Provider 独立访问
GET
http://localhost:8081/users/1
必须:
先成功
一百七十六、测试二:关闭 Provider
再调:
order-service
亲自观察:
远程调用失败
建立:
网络调用不可靠
意识。
一百七十七、测试三:Nacos 注册
启动两个服务。
控制台:
user-service
order-service
都应该出现。
一百七十八、测试四:DiscoveryClient
discoveryClient.getInstances(
"user-service"
);
确认:
实例列表不为空
一百七十九、测试五:LoadBalancer
将 URL 从:
localhost:8081
改成:
user-service
成功。
一百八十、测试六:两个 Provider
8081
8083
确认 Nacos:
同服务 2 个实例
一百八十一、测试七:Feign
移除业务里的:
RestTemplate 拼 URL
改:
userClient.getById(id);
一百八十二、测试八:服务名故意写错
@FeignClient(
name = "users-service"
)
观察:
无实例异常
再改回来。
一百八十三、测试九:路径故意写错
Provider:
/users/{id}
Feign 故意:
/user/{id}
观察:
404
一百八十四、测试十:Method 写错
Provider POST,
Feign GET。
观察:
405
一百八十五、测试十一:Read Timeout
Provider sleep:
5 秒
Feign readTimeout:
1 秒
观察:
Read timed out
一百八十六、测试十二:Namespace 隔离
user-service:
dev
order-service:
test
观察:
互相发现不到
一百八十七、测试十三:Nacos Config
创建:
user-service.yml
配置:
user:
greeting: Hello
读取成功。
一百八十八、测试十四:动态刷新
修改:
Hello
→
Hello V2
验证:
无需重启
一百八十九、测试十五:关一个实例
两个 user-service:
关闭 8081
确认:
8083 仍能继续响应
一百九十、常见 Bug 总表
1. Nacos Console 看不到服务
检查:
Discovery Starter
server-addr
application.name
Namespace
启动日志
服务是否真的启动
2. Feign Bean 不存在
检查:
@EnableFeignClients
扫描包
OpenFeign Starter
3. Feign 404
检查:
路径
4. Feign 400
检查:
参数注解和 JSON
5. Feign 405
检查:
HTTP Method
6. Feign 503
检查:
服务发现 / 实例
7. Read timed out
检查:
下游慢
readTimeout
SQL
阻塞
8. 服务名 URL 无法解析
RestTemplate:
是否 @LoadBalanced
9. Config 不加载
检查:
spring.config.import
DataId
Group
Namespace
10. bootstrap.yml 失效
新版本:
使用 application.yml
+
spring.config.import
11. Namespace 不生效
检查:
Namespace ID
12. 多网卡注册错 IP
检查:
Nacos 实例地址
13. NoSuchMethodError
检查:
版本冲突 / BOM
14. 动态刷新不生效
检查:
refreshEnabled
@RefreshScope
15. 只有一个实例接请求
检查:
另一个是否注册/健康
一百九十一、IDEA 多服务运行技巧
打开:
View
→ Tool Windows
→ Services
建议 Run Configuration 命名:
user-service-8081
user-service-8083
order-service-8082
不要全部叫:
Application
否则控制台非常乱。
一百九十二、端口冲突
Windows:
netstat -ano | findstr 8081
或 Nacos:
netstat -ano | findstr 8848
再查:
tasklist | findstr PID
确认是什么进程后再处理。
一百九十三、不要因为端口冲突就直接杀所有 Java
可能:
其他服务也在跑
一百九十四、Maven 模块学习结构
spring-cloud-demo
├─ pom.xml
├─ user-service
│ ├─ pom.xml
│ └─ src
└─ order-service
├─ pom.xml
└─ src
一百九十五、后续可以扩
spring-cloud-demo
├─ common
├─ user-api
├─ user-service
├─ order-service
└─ gateway
但:
不要第一天就建 20 个模块
一百九十六、为什么学习要逐层增加
如果一开始:
Nacos
Feign
Sentinel
Gateway
Redis
MQ
Security
一起上,
报错后:
根本不知道哪层错
一百九十七、最好的学习方式
每加入一个组件
先验证它解决的问题
一百九十八、Cursor 提示词:父工程
当前环境是 JDK17。
请按 Spring Boot 3 技术栈创建一个学习用 Spring Cloud Alibaba 父 Maven 工程。
要求:
1. 使用官方兼容矩阵中明确的 Boot 3.5.x / Spring Cloud 2025.0.x / Spring Cloud Alibaba 2025.0.x 组合
2. 父工程 packaging=pom
3. 使用 Spring Cloud BOM
4. 使用 Spring Cloud Alibaba BOM
5. 创建 user-service 和 order-service
6. 子模块 Starter 不手动覆盖版本
7. 暂时不要引入 Sentinel、Gateway、MQ
一百九十九、Cursor 提示词:固定 URL
先不要使用 Nacos 和 Feign。
实现:
user-service:8081
order-service:8082
user-service:
GET /users/{id}
order-service:
GET /orders/{id}
order-service 使用普通 RestTemplate
固定调用:
http://localhost:8081/users/{id}
目的:
让我先看到服务地址写死的问题。
不要提前优化。
二百、Cursor 提示词:接 Nacos
现在将 user-service 和 order-service 接入 Nacos Discovery。
要求:
1. spring-cloud-starter-alibaba-nacos-discovery
2. Nacos 地址 127.0.0.1:8848
3. application.name 分别为 user-service、order-service
4. 使用当前 Spring Boot 3 兼容方式
5. 不使用 Ribbon
6. 启动后 Nacos 能看到两个服务
7. 输出验证与失败排查步骤
二百零一、Cursor 提示词:LoadBalancer
请给 order-service 增加 Spring Cloud LoadBalancer。
要求:
1. 明确引入 spring-cloud-starter-loadbalancer
2. RestTemplate Bean 添加 @LoadBalanced
3. URL 从 localhost 改成 http://user-service/users/{id}
4. 解释服务名如何转换为真实实例
5. 不使用 Ribbon
二百零二、Cursor 提示词:Feign
请把 order-service 的 RestTemplate 远程调用改成 OpenFeign。
要求:
1. 引入 spring-cloud-starter-openfeign
2. @EnableFeignClients
3. @FeignClient(name="user-service")
4. 不填写固定 url
5. GET /users/{id}
6. @PathVariable 显式写参数名
7. 返回 UserRemoteDTO
8. 不依赖 user-service 数据库 Entity
9. Controller → Service → FeignClient 分层
二百零三、Cursor 提示词:Feign 超时
请使用当前 Spring Cloud OpenFeign 配置方式增加超时:
spring.cloud.openfeign.client.config
要求:
1. connectTimeout
2. readTimeout
3. 创建 sleep 测试接口
4. 解释两个超时区别
5. 不要把超时简单改得特别大
二百零四、Cursor 提示词:Nacos Config
请按 Spring Cloud Alibaba 2025.x 当前方式接入 Nacos Config。
要求:
1. spring-cloud-starter-alibaba-nacos-config
2. application.yml
3. 使用 spring.config.import
4. 不以 bootstrap.yml 作为新项目主方案
5. DataId=user-service.yml
6. Group=DEFAULT_GROUP
7. refreshEnabled=true
8. 用 @Value + @RefreshScope 演示动态刷新
9. 解释 optional:nacos
二百零五、Cursor 提示词:Namespace
请检查 Nacos Namespace。
分别检查:
1. Discovery Namespace
2. Config Namespace
3. 是否误填 Namespace Name 而不是 Namespace ID
4. Provider / Consumer 是否在同一 Discovery Namespace
5. Config DataId 是否处于正确 Namespace
6. Group 是否一致
先分析,不修改。
二百零六、Cursor 提示词:版本冲突
当前微服务项目出现:
NoSuchMethodError / ClassNotFoundException
请分析 pom.xml 和 mvn dependency:tree。
重点检查:
1. Spring Boot
2. Spring Cloud BOM
3. Spring Cloud Alibaba BOM
4. Nacos Client
5. OpenFeign
6. LoadBalancer
7. 是否手工覆盖 BOM 版本
8. 是否混入 Spring Boot 2 老组件
不要默认“全部升级最新版”,先找到冲突链。
二百零七、Cursor 提示词:远程 N+1
请检查 order-service 是否存在远程 N+1。
如果订单列表循环:
userClient.getById(userId)
请优化:
1. 收集 userIds
2. 去重
3. user-service 提供批量接口
4. 一次 Feign 请求
5. 返回 List<UserRemoteDTO>
6. 转 Map<Long, UserRemoteDTO>
7. 再组装订单 VO
8. 限制批量请求数量
二百零八、面试题 1:Nacos 是什么
答:
Nacos 是一个动态服务发现、配置管理和服务管理平台。
在 Spring Cloud Alibaba 中,
常用 Nacos Discovery 完成服务注册发现,
使用 Nacos Config 做集中配置管理。
调用方可以通过服务名动态获得可用实例,
避免写死 IP 和端口。
二百零九、面试题 2:服务注册和发现区别
答:
服务注册是 Provider 启动后把服务名、IP、端口等实例信息注册到 Nacos。
服务发现是 Consumer 根据服务名查询或订阅可用实例列表。
一个相当于“告诉别人我在哪里”,
一个相当于“找到我要调用的人”。
二百一十、面试题 3:Nacos 和 LoadBalancer 区别
答:
Nacos 负责提供有哪些可用服务实例。
LoadBalancer 负责从多个实例中选择一个进行实际调用。
可以记成:
Nacos 负责“有哪些”,
LoadBalancer 负责“选哪个”。
二百一十一、面试题 4:Feign 是什么
答:
OpenFeign 是声明式 HTTP 客户端。
开发者通过 Java 接口和 Spring MVC 注解描述远程 API,
Spring Cloud 运行时创建代理对象并发送 HTTP 请求。
Feign 简化了服务调用代码,
但本质仍然是网络请求。
二百一十二、面试题 5:为什么 Feign 内部服务不要固定 url
答:
内部服务已经注册到 Nacos 时,
写固定 url 会绕过基于服务名的动态发现和负载均衡。
使用 @FeignClient(name="user-service")
可以适应服务扩缩容和实例地址变化。
二百一十三、面试题 6:@EnableFeignClients 做什么
答:
它开启 Feign Client 接口扫描。
Spring 会找到 @FeignClient 接口并创建代理 Bean。
如果接口不在默认扫描范围,
可以通过 basePackages 指定扫描路径。
二百一十四、面试题 7:什么是客户端负载均衡
答:
Consumer 先通过服务发现获得多个 Provider 实例,
再在自己这一侧选择一个实例发送请求。
实例选择发生在调用方,
所以叫客户端负载均衡。
现代 Spring Cloud 常使用 Spring Cloud LoadBalancer。
二百一十五、面试题 8:为什么不再以 Ribbon 为主
答:
Ribbon 属于较老的 Netflix Spring Cloud 技术体系。
现代 Spring Cloud 已转向 Spring Cloud LoadBalancer。
Spring Boot 3 新项目不应机械复制老 Ribbon 教程。
二百一十六、面试题 9:Namespace 做什么
答:
Namespace 用于较大粒度的资源隔离。
最常见场景是隔离开发、测试、生产环境。
不同 Namespace 内的服务和配置可以互相隔离。
二百一十七、面试题 10:为什么 Namespace 经常配错
答:
客户端配置通常需要 Namespace ID,
而不是控制台显示名称。
另外 Discovery 和 Config 有独立的 Namespace 配置,
只配一边会造成一部分功能正常、另一部分失败。
二百一十八、面试题 11:DataId 和 Group 是什么
答:
DataId 可以理解为配置文件名称,例如 user-service.yml。
Group 是 Namespace 内进一步分组的维度。
配置中心可以通过 Namespace + Group + DataId
定位一份具体配置。
二百一十九、面试题 12:为什么使用 spring.config.import
答:
Spring Cloud Alibaba 新版本已经迁移到 Spring Boot Config Data 机制。
2023.0.1.3 及以上推荐使用 spring.config.import 显式导入 Nacos 配置,
2025.1.x 更取消了 bootstrap.yml 的 Nacos Bootstrap 接入方式。
新项目应优先使用 application.yml + spring.config.import。
二百二十、面试题 13:optional:nacos 是什么
答:
optional:nacos 表示 Nacos 配置缺失或暂时拉取失败时,
应用允许继续启动。
非 optional 的 nacos: 导入失败时,
应用可能直接启动失败。
是否 optional 应根据配置是否属于强依赖决定。
二百二十一、面试题 14:connectTimeout 和 readTimeout 区别
答:
connectTimeout 限制建立连接的等待时间。
readTimeout 限制连接成功后等待远程响应的时间。
它们对应网络调用的不同阶段。
二百二十二、面试题 15:为什么重试要考虑幂等
答:
服务端可能已经执行成功,
只是响应因为网络问题没有到达 Consumer。
Consumer 重试后同一写操作可能执行第二次。
所以订单、支付等接口需要通过业务唯一号、
状态机和数据库唯一约束保证幂等。
二百二十三、面试题 16:什么是远程 N+1
答:
查询一批业务数据后,
循环为每条数据调用一次远程服务,
就会产生远程 N+1。
例如 100 个订单调用 100 次 user-service。
常见优化是批量远程接口、缓存或合理的数据冗余。
二百二十四、面试题 17:Nacos 和 Sentinel 区别
答:
Nacos 主要负责服务发现、实例管理和配置管理。
Sentinel 主要负责限流、熔断降级和系统保护。
Nacos 解决“服务在哪里”,
Sentinel 解决“服务调用如何更稳定”。
二百二十五、面试题 18:为什么注册成功不代表调用一定成功
答:
注册中心只表示存在服务实例。
真正 HTTP 调用仍可能因为路径错误、参数错误、
网络故障、超时、数据库异常等失败。
服务发现只是调用链中的一个环节。
二百二十六、面试题 19:Nacos 挂了业务是否立刻全挂
答:
不能简单这样判断。
客户端通常有已经获取到的实例信息,
已有服务在一定情况下仍可短时间继续通信。
但新实例注册、实例变化同步和配置中心都会受影响,
因此生产仍需要高可用 Nacos。
二百二十七、面试题 20:为什么使用 BOM
答:
Spring Cloud 和 Spring Cloud Alibaba 有大量相互依赖组件。
如果手工指定不同版本,
容易出现 NoSuchMethodError、ClassNotFoundException 等兼容问题。
BOM 可以统一管理一套兼容版本组合。
二百二十八、本章知识树
Nacos + Feign
│
├─ Maven
│ ├─ Parent
│ ├─ Spring Cloud BOM
│ └─ SCA BOM
│
├─ Basic HTTP
│ ├─ RestTemplate
│ └─ Fixed URL
│
├─ Nacos Discovery
│ ├─ Service Registration
│ ├─ Service Discovery
│ ├─ DiscoveryClient
│ ├─ ServiceInstance
│ ├─ Namespace
│ ├─ Group
│ ├─ Cluster
│ └─ Metadata
│
├─ LoadBalancer
│ ├─ @LoadBalanced
│ ├─ Service Name
│ └─ Multiple Instances
│
├─ Feign
│ ├─ @EnableFeignClients
│ ├─ @FeignClient
│ ├─ PathVariable
│ ├─ RequestParam
│ ├─ RequestBody
│ ├─ Remote DTO
│ ├─ Timeout
│ ├─ Logging
│ └─ Remote N+1
│
├─ Nacos Config
│ ├─ Config Starter
│ ├─ spring.config.import
│ ├─ DataId
│ ├─ Group
│ ├─ Namespace
│ ├─ optional:nacos
│ ├─ refreshEnabled
│ └─ @RefreshScope
│
└─ Debug
├─ 400
├─ 404
├─ 405
├─ 503
├─ Timeout
├─ Wrong Namespace
└─ Version Conflict
二百二十九、最重要的调用链
OrderService
↓
UserClient
↓
Feign Proxy
↓
LoadBalancer
↓
Nacos Discovery
↓
user-service 实例列表
↓
选择一个实例
↓
HTTP
↓
UserController
二百三十、最重要配置链
application.yml
↓
Nacos Server
↓
spring.config.import
↓
Namespace
↓
Group
↓
DataId
↓
Spring Environment
↓
@Value / ConfigurationProperties
二百三十一、本章最重要的 20 条原则
1. 先体验写死 URL,再学习 Nacos
2. spring.application.name 是微服务逻辑身份
3. Nacos 负责告诉你有哪些服务实例
4. LoadBalancer 负责从实例里选一个
5. Feign 简化 HTTP,不会把远程调用变成本地调用
6. 内部注册服务不要随便在 FeignClient 写固定 url
7. Provider 和 Consumer 服务名必须完全一致
8. FeignClient 必须正确扫描
9. Discovery 与 Config 是两个不同 Starter
10. Namespace 最常用于环境隔离
11. Namespace 通常配置 ID,不是显示名称
12. 新版 Nacos Config 优先 spring.config.import
13. 新项目不要继续以 bootstrap.yml 老教程为主
14. 远程调用必须设置合理超时
15. 自动重试前必须判断接口幂等性
16. 跨服务使用 Remote DTO,不共享数据库 Entity
17. 循环 Feign 调用要警惕远程 N+1
18. BOM 负责统一微服务依赖版本
19. NoSuchMethodError 优先检查依赖冲突
20. 服务发现、调用、流量治理、权限是不同问题
二百三十二、本章最终验收
学完以后,你应该能够:
1. 创建父 Maven 微服务工程
2. 配置 Spring Cloud BOM
3. 配置 Spring Cloud Alibaba BOM
4. 创建 user-service
5. 创建 order-service
6. 使用固定 URL 做远程调用
7. 安装启动 Nacos
8. 注册服务到 Nacos
9. 使用 DiscoveryClient
10. 获取 ServiceInstance
11. 配置 Spring Cloud LoadBalancer
12. 使用 @LoadBalanced
13. 使用服务名调用 Provider
14. 启动同服务多个实例
15. 验证负载均衡
16. 引入 OpenFeign
17. 使用 @EnableFeignClients
18. 使用 @FeignClient
19. 使用 @PathVariable
20. 使用 @RequestParam
21. 使用 @RequestBody
22. 设计 Remote DTO
23. 配置 connectTimeout
24. 配置 readTimeout
25. 调试 Feign 日志
26. 理解 Namespace
27. 理解 Group
28. 理解 DataId
29. 接入 Nacos Config
30. 使用 spring.config.import
31. 理解 optional:nacos
32. 使用 refreshEnabled
33. 使用 @RefreshScope
34. 排查服务注册失败
35. 排查 Feign 404/400/405/503
36. 排查多网卡 IP
37. 排查 Namespace 错误
38. 使用 mvn dependency:tree
39. 排查 NoSuchMethodError
40. 识别远程 N+1
二百三十三、当前官方版本提醒
截至本笔记整理时:
Nacos 官方站点最新稳定版显示:
3.2.4
发布时间:
2026-08-27
但是 Spring Cloud Alibaba 项目选择 Nacos 时:
不要只追 Nacos Server 最新版
还要检查:
Spring Cloud Alibaba 组件兼容矩阵
Spring Cloud Alibaba 官方 2025.0.x 基线:
SCA 2025.0.0.0
Spring Cloud 2025.0.0
Spring Boot 3.5.0
Nacos Client 3.0.3
而:
2025.1.x
已经进入:
Spring Boot 4.0.x
所以你当前:
JDK17 + Spring Boot 3
学习时不要误把:
Boot 4 示例
复制进 Boot 3 项目。
二百三十四、OpenFeign 当前官方提醒
Spring Cloud OpenFeign 当前仍有稳定版本维护。
但是 Spring 官方已明确表示:
OpenFeign 处于 feature-complete 状态
并建议新方向:
Spring HTTP Service Clients
你当前课程继续学 Feign 的原因:
1. Spring Cloud Alibaba 传统项目大量使用
2. 若依 Cloud 等项目大量能看到
3. 面试和维护旧/现有系统仍很常见
4. Feign 能很好帮助理解声明式远程调用
所以正确态度:
会 Feign
但知道 Spring 生态正在继续演进
二百三十五、下一课
按照课程表,下一篇进入:
《Spring Cloud Alibaba:Sentinel 详解》
下一课不再只是:
服务能不能找到
而是解决:
找到以后调用失败怎么办?
流量太大怎么办?
下游越来越慢怎么办?
我们会真正制造故障:
user-service 人为 sleep
↓
order-service 大量等待
↓
观察风险
↓
接入 Sentinel
继续学习:
QPS 限流
线程数限流
流控规则
BlockException
熔断降级
慢调用比例
异常比例
异常数
Fallback
热点参数限流
系统保护
Feign + Sentinel
也就是正式进入:
微服务稳定性治理
阶段。