深入浅出 Spring + Spring Boot + Nacos + Ribbon + Feign 五大微服务框架
深入浅出 Spring + Spring Boot + Nacos + Ribbon + Feign 五大微服务框架
面向 Java 后端开发,从 0 到 1 把 Spring / Spring Boot / Nacos / Ribbon / Feign 串起来讲清楚:每个框架解决什么问题、关键 API 怎么用、生产上怎么协作。所有示例基于 Java 17 + Spring Boot 3.x + Spring Cloud 2023 + Spring Cloud Alibaba 2023。
一、微服务全景图
一个完整的 Spring 系微服务体系,至少由这五大件组成:
┌──────────────────────┐
│ Nacos │ ← 注册中心 + 配置中心
│ (服务发现/动态配置) │
└──────────┬───────────┘
│ 注册 / 心跳 / 拉取
┌───────────────────┼─────────────────────┐
│ │ │
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ user-service │ │ order-service│ │ goods-svc │
│ │ │ │ │ │
│ Spring Boot │ │ Spring Boot │ │ Spring Boot │
│ + Spring │ │ + Spring │ │ + Spring │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
▲ │ ▲
│ Feign(声明式调用) │ Ribbon(负载均衡) │
└───────────────────┴────────────────────┘
| 框架 | 解决的问题 | 一句话定位 |
|---|---|---|
| Spring Framework | IoC / AOP / 事务 / 资源管理 | 整个 Java 后端世界的"操作系统" |
| Spring Boot | 快速启动、约定大于配置、嵌入式容器 | 让你 30 秒跑起一个 Web 服务 |
| Nacos | 注册中心 + 配置中心 | 服务怎么找到服务,配置怎么不重启更新 |
| Ribbon | 客户端负载均衡 | 一个服务名背后多个实例,怎么选一个 |
| Feign | 声明式 HTTP 客户端 | 把远程接口调用写成本地接口 |
重要提示:Spring Cloud 2020.x 起官方默认负载均衡组件已切换为 Spring Cloud LoadBalancer,Netflix Ribbon 进入维护期。本文为兼顾"老项目仍在用"和"新项目正确姿势",会同时讲两套,并明确何时该用哪种。
二、Spring Framework:万物的基石
2.1 IoC:把 new 的权力交出去
没有 IoC 时:
public class OrderService {
private UserService userService = new UserServiceImpl(); // 紧耦合
}
有了 IoC:
@Service
@RequiredArgsConstructor
public class OrderService {
private final UserService userService; // Spring 注入,构造器注入是首选
public Order create(Long uid) {
var user = userService.getById(uid);
return new Order(uid, user.getName());
}
}
三种注入方式优先级:构造器 > Setter > 字段。生产代码请只用构造器(@RequiredArgsConstructor + final),便于单测和不可变。
2.2 AOP:横切逻辑拦截
@Aspect
@Component
@Slf4j
public class TraceAspect {
@Around("@annotation(org.springframework.web.bind.annotation.RestController) " +
"|| within(@org.springframework.web.bind.annotation.RestController *)")
public Object trace(ProceedingJoinPoint pjp) throws Throwable {
long t = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
log.info("[{}] cost={}ms", pjp.getSignature(), System.currentTimeMillis() - t);
}
}
}
事务、日志、限流、权限——都是 AOP 的典型应用场景。
2.3 Bean 生命周期速查
实例化 → 属性注入 → BeanNameAware/BeanFactoryAware
→ BeanPostProcessor.before
→ @PostConstruct / InitializingBean.afterPropertiesSet / init-method
→ BeanPostProcessor.after
→ 业务使用
→ @PreDestroy / DisposableBean.destroy / destroy-method
记住这一串,面试和排查 Bean 启动问题都用得上。
三、Spring Boot:30 秒起一个服务
3.1 三件套 Starter
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
3.2 启动类
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
@SpringBootApplication 是个组合注解:
@SpringBootApplication = @SpringBootConfiguration + @EnableAutoConfiguration + @ComponentScan
3.3 Auto-Configuration 原理(一句话版)
启动时扫描 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(Spring Boot 3.x 的新位置),按 @Conditional 条件决定加载哪些自动配置类。这就是为什么"加个依赖,啥都不配,就能跑"。
3.4 配置文件分层
# application.yml 共有
spring:
application:
name: user-service
# application-dev.yml / application-prod.yml 环境差异化
server:
port: 8081
启动指定环境:--spring.profiles.active=prod。
3.5 第一个 Controller
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public UserVO get(@PathVariable Long id) {
return userService.getById(id);
}
}
启动后 curl http://localhost:8081/users/1 即可。
四、Nacos:服务注册中心 + 配置中心
4.1 启动 Nacos(单机)
docker run -d --name nacos -p 8848:8848 -p 9848:9848 \
-e MODE=standalone -e NACOS_AUTH_ENABLE=true \
-e NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789 \
nacos/nacos-server:v2.3.2
控制台:http://localhost:8848/nacos,默认 nacos / nacos。
4.2 服务注册:让 user-service 上线
依赖:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.1</version>
<type>pom</type><scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2023.0.1.0</version>
<type>pom</type><scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<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-bootstrap</artifactId>
</dependency>
</dependencies>
bootstrap.yml(在 application.yml 之前加载):
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: dev
group: DEFAULT_GROUP
config:
server-addr: 127.0.0.1:8848
namespace: dev
file-extension: yaml
refresh-enabled: true
启动类无需任何额外注解(Spring Cloud 2022+ 已经移除 @EnableDiscoveryClient 的强制要求,Starter 在就生效)。
启动后到 Nacos 控制台 → 服务列表,能看到 user-service 一条实例记录。
4.3 配置中心:动态刷新
在 Nacos 上新建 Data ID:user-service.yaml
biz:
welcome: "Hello from Nacos!"
vip-discount: 0.8
代码侧:
@RestController
@RefreshScope // 关键:让 Bean 支持配置热更新
public class ConfigController {
@Value("${biz.welcome}")
private String welcome;
@GetMapping("/config")
public String show() { return welcome; }
}
在 Nacos 上修改
biz.welcome并发布,不重启 服务,请求/config立刻看到新值。这就是 Nacos 配置中心最大的价值。
4.4 多环境 / 命名空间
| 概念 | 类比 | 用法 |
|---|---|---|
| Namespace | 数据库 | dev / test / prod 隔离 |
| Group | Schema | DEFAULT_GROUP / pay-group / order-group |
| Data ID | 表 | user-service.yaml |
生产推荐:namespace 隔离环境,group 隔离业务线。
4.5 Nacos 注册原理(一图流)
Client Nacos Server
│ register() │
│ ─────────────────────────────▶│
│ ✓ 写入注册表 │
│ heartbeat (5s/次) │
│ ─────────────────────────────▶│
│ ✓ 续约 │
│ subscribe(svc) │
│ ─────────────────────────────▶│
│ ◀──────── push 实例变更 │
│ (UDP/gRPC,秒级感知下线) │
Nacos 2.x 默认走 gRPC 长连接,比 1.x 的 HTTP 短轮询性能高一个量级。
五、Ribbon:客户端负载均衡
5.1 核心概念
服务消费者(如 order-service)拉到 user-service 的实例列表 [10.0.0.1:8081, 10.0.0.2:8081] 后,在自己进程内选一个发起调用——这就是客户端负载均衡。
order-service 进程
┌─────────────────────────────┐
│ RestTemplate / Feign │
│ ↓ │
│ Ribbon: 选实例 (轮询/随机) │
│ ↓ │
│ http://10.0.0.2:8081 │
└─────────────────────────────┘
5.2 老项目:Ribbon + RestTemplate
<!-- 仅在 Spring Cloud Hoxton 及更早版本可用 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
@Configuration
public class RestConfig {
@Bean
@LoadBalanced // 关键注解:让 RestTemplate 走客户端负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Service
@RequiredArgsConstructor
public class OrderService {
private final RestTemplate restTemplate;
public UserVO loadUser(Long uid) {
// 注意:host 是「服务名」,不是 IP
return restTemplate.getForObject(
"http://user-service/users/" + uid, UserVO.class);
}
}
Ribbon 会在底层把 user-service 翻译成具体的 10.0.0.x:8081,并在多个实例间负载均衡。
5.3 配置负载均衡策略
user-service: # 服务名
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
常见策略:
| 策略类 | 说明 |
|---|---|
RoundRobinRule |
轮询(默认) |
RandomRule |
随机 |
WeightedResponseTimeRule |
响应时间加权 |
BestAvailableRule |
选并发最小的 |
ZoneAvoidanceRule |
区域感知 + 轮询 |
5.4 新项目:Spring Cloud LoadBalancer 替代 Ribbon
Spring Cloud 2020.x 起 Ribbon 进入维护期,官方推荐 LoadBalancer:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
代码完全不变(同样用 @LoadBalanced + 服务名),只是底层换成了 LoadBalancer。spring-cloud-starter-alibaba-nacos-discovery 已经传递依赖了 LoadBalancer,多数情况下你什么都不用做。
切换策略:
@Configuration
@LoadBalancerClient(name = "user-service",
configuration = RandomLoadBalancerConfig.class)
public class LbConfig { }
class RandomLoadBalancerConfig {
@Bean
ReactorLoadBalancer<ServiceInstance> randomLB(
LoadBalancerClientFactory factory, Environment env) {
String name = env.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(
factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
结论:新项目直接上 LoadBalancer;老项目用 Ribbon 也无伤大雅,但要清楚它已不再演进。
六、Feign:把远程调用写成本地接口
6.1 为什么需要 Feign
用 RestTemplate 调用远程:
restTemplate.getForObject("http://user-service/users/" + uid + "?withProfile=true",
UserVO.class);
每多一个接口就要拼一次 URL、转一次 JSON、捕一遍异常——很快就退化成 5000 行 if/else。
Feign 把这件事接口化:
@FeignClient(name = "user-service")
public interface UserApi {
@GetMapping("/users/{id}")
UserVO get(@PathVariable Long id, @RequestParam boolean withProfile);
}
注入直接用:
@RestController
@RequiredArgsConstructor
public class OrderController {
private final UserApi userApi;
@GetMapping("/orders/{uid}")
public Object create(@PathVariable Long uid) {
UserVO user = userApi.get(uid, true); // 像本地方法一样
return Map.of("user", user, "orderId", UUID.randomUUID());
}
}
底层全自动:服务发现(Nacos)→ 负载均衡(LoadBalancer/Ribbon)→ HTTP 调用 → JSON 反序列化。
6.2 启用 Feign
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@SpringBootApplication
@EnableFeignClients(basePackages = "com.example.order.api")
public class OrderApplication { ... }
6.3 超时、重试、日志
spring:
cloud:
openfeign:
client:
config:
default: # 全局
connect-timeout: 2000
read-timeout: 5000
logger-level: BASIC # NONE / BASIC / HEADERS / FULL
user-service: # 针对单个客户端覆盖
read-timeout: 10000
feign:
circuitbreaker:
enabled: true # 集成 Resilience4j 熔断
@Bean
Logger.Level feignLogger() { return Logger.Level.FULL; }
想看到 FULL 日志,还要把对应包日志级别调到 DEBUG:
logging: level: com.example.order.api: debug
6.4 拦截器(透传 Header / Token)
@Bean
RequestInterceptor authInterceptor() {
return tpl -> {
String token = MDC.get("traceId");
if (token != null) tpl.header("X-Trace-Id", token);
ServletRequestAttributes attrs =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attrs != null) {
String auth = attrs.getRequest().getHeader("Authorization");
if (auth != null) tpl.header("Authorization", auth);
}
};
}
链路追踪、用户态透传都靠这里。
6.5 Fallback 降级
@FeignClient(name = "user-service", fallback = UserApiFallback.class)
public interface UserApi {
@GetMapping("/users/{id}")
UserVO get(@PathVariable Long id, @RequestParam boolean withProfile);
}
@Component
class UserApiFallback implements UserApi {
@Override
public UserVO get(Long id, boolean withProfile) {
return new UserVO(id, "guest", "降级数据"); // 兜底
}
}
需要开启 feign.circuitbreaker.enabled: true 并引入 Resilience4j。
七、综合实战:三服务协作
下面把五大件整合,实现:order-service 收到下单请求 → 通过 Feign 调用 user-service 验证用户 → 调用 goods-service 锁库存 → 返回订单。
7.1 user-service(提供方)
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); }
}
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public UserVO get(@PathVariable Long id) {
return new UserVO(id, "wanghao", "VIP");
}
}
public record UserVO(Long id, String name, String level) {}
bootstrap.yml:
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8081
7.2 goods-service(提供方)
@RestController
@RequestMapping("/goods")
public class GoodsController {
@PostMapping("/{id}/lock")
public Boolean lock(@PathVariable Long id, @RequestParam int qty) {
// 真正逻辑:扣减库存
return qty <= 100;
}
}
spring:
application:
name: goods-service
server:
port: 8082
7.3 order-service(消费方)
Feign 接口:
@FeignClient(name = "user-service")
public interface UserApi {
@GetMapping("/users/{id}")
UserVO get(@PathVariable Long id);
}
@FeignClient(name = "goods-service", fallback = GoodsApiFallback.class)
public interface GoodsApi {
@PostMapping("/goods/{id}/lock")
Boolean lock(@PathVariable Long id, @RequestParam int qty);
}
@Component
class GoodsApiFallback implements GoodsApi {
@Override public Boolean lock(Long id, int qty) { return false; }
}
控制器:
@RestController
@RequestMapping("/orders")
@RequiredArgsConstructor
public class OrderController {
private final UserApi userApi;
private final GoodsApi goodsApi;
@PostMapping
public Map<String, Object> create(@RequestParam Long uid,
@RequestParam Long goodsId,
@RequestParam int qty) {
UserVO user = userApi.get(uid);
if (!"VIP".equals(user.level())) {
throw new IllegalStateException("仅 VIP 可下单(演示)");
}
Boolean locked = goodsApi.lock(goodsId, qty);
if (!Boolean.TRUE.equals(locked)) {
throw new IllegalStateException("库存不足");
}
return Map.of(
"orderId", UUID.randomUUID().toString(),
"user", user,
"qty", qty
);
}
}
启动类:
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); }
}
bootstrap.yml:
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
server:
port: 8083
spring:
cloud:
openfeign:
client:
config:
default:
connect-timeout: 2000
read-timeout: 5000
logger-level: BASIC
feign:
circuitbreaker:
enabled: true
启动顺序:Nacos → user-service → goods-service → order-service。
测试:
curl -X POST "http://localhost:8083/orders?uid=1&goodsId=2&qty=5"
返回:
{
"orderId": "5b21...",
"user": { "id": 1, "name": "wanghao", "level": "VIP" },
"qty": 5
}
控制台日志能清楚看到:
[OrderController] create...
[Feign:user-service] GET http://user-service/users/1 -> 200
[Feign:goods-service] POST http://goods-service/goods/2/lock?qty=5 -> 200
八、生产化建议(强烈建议)
| 维度 | 建议 |
|---|---|
| Nacos 高可用 | 至少 3 节点 + MySQL 集群存储;开启鉴权 nacos.core.auth.enabled=true |
| 配置中心 | 敏感信息(密码、密钥)用 加密配置 或独立的 vault,不要明文 |
| 服务发现 | 心跳间隔与失效阈值要匹配你的发布节奏(默认 5s/15s 即可) |
| 负载均衡 | 新项目统一用 LoadBalancer;老项目维持 Ribbon 不要混用 |
| Feign | 必加:超时、重试、熔断、日志、拦截器透传 traceId |
| 链路追踪 | 接入 SkyWalking 或 Spring Cloud Sleuth + Zipkin |
| 熔断 / 限流 | Resilience4j(Spring Cloud 官方)或 Sentinel(Alibaba 系,与 Nacos 配合更紧密) |
| 网关 | 前面挂一个 Spring Cloud Gateway,统一鉴权、限流、灰度 |
| 可观测性 | Actuator + Prometheus + Grafana;management.endpoints.web.exposure.include=* |
| 优雅停机 | server.shutdown=graceful;Nacos 主动下线后再 kill |
九、容易踩的坑(亲测)
- bootstrap.yml 不加载:Spring Cloud 2020+ 必须显式引入
spring-cloud-starter-bootstrap,否则 Nacos 配置中心拉不到。 @RefreshScope不生效:注解必须打在 Controller / 直接 @Value 的 Bean 上,且对应字段不能用final。- Feign 调用 404:检查
@FeignClient(name=)的服务名是否与 Nacos 上完全一致(区分大小写)。 - POJO 反序列化失败:服务方返回字段名与消费方 DTO 不一致;建议两边共用一个 api-jar。
- 超时时间冲突:Feign 自身超时 + LoadBalancer 重试 + Hystrix/Resilience4j 超时三处都要校准,否则诡异 timeout。
- Ribbon 缓存导致刚下线的实例还在被调:
ribbon.ServerListRefreshInterval=2000,或新项目直接用 LoadBalancer 的事件驱动模型。 - 生产用 Spring Cloud LoadBalancer 缓存默认开:发布期可短暂关闭
spring.cloud.loadbalancer.cache.enabled=false,灰度更平滑。 - 配置加密:明文存数据库密码到 Nacos 是常见安全事故,必须用
jasypt或 KMS。
十、知识脉络总结
Spring (IoC/AOP)
└→ Spring Boot (Auto-Config + Embedded Tomcat)
└→ Spring Cloud
├── Nacos ← 服务注册 + 配置中心
├── LoadBalancer ← 客户端负载均衡(替代 Ribbon)
├── OpenFeign ← 声明式调用
├── Gateway ← 网关
└── Resilience4j ← 熔断 / 限流
| 框架 | 类比 | 一句话 |
|---|---|---|
| Spring | 操作系统 | 容器 + AOP |
| Spring Boot | 装好了驱动的桌面发行版 | 约定大于配置 |
| Nacos | DNS + ZooKeeper + Apollo | 找服务 + 拿配置 |
| Ribbon | DNS 客户端解析器 | 多实例选一个 |
| Feign | 远程方法调用代理 | 像本地一样调远程 |
结语
学微服务最忌**“看了 5 个框架,但说不清它们怎么协作”**。这五大件其实就是一条流水线:
Spring 提供能力 → Spring Boot 启动它们 → Nacos 让它们找到彼此 → Ribbon/LoadBalancer 决定调用哪个实例 → Feign 让调用看起来像本地方法。
把上面 7 章的实战 demo 跑一遍,对照日志看清楚每一步到底发生了什么,比看 100 篇文章都有效。下一步建议加上 Spring Cloud Gateway(统一入口)+ Sentinel/Resilience4j(熔断限流)+ Sleuth + Zipkin(链路追踪),就是一套生产级 Java 微服务骨架。
祝你构建出第一套真正在跑的微服务系统 🚀。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)