在微服务架构中,服务间的远程调用是核心环节。Spring Cloud 提供了两大组件完美解决这个问题:
- Ribbon:客户端负载均衡工具,确保请求均匀分发到多个服务实例
- Feign:声明式 HTTP 客户端,简化远程调用代码,天然集成 Ribbon
一、Ribbon 负载均衡详解
1.1 核心概念
- 作用 :消费端负载均衡,Nacos 已默认集成 Ribbon,无需额外引入依赖
- 核心能力 :
- 内置多种负载均衡策略(轮询、随机、权重等)
- 配合
@LoadBalanced注解,直接通过服务名调用服务,无需手动管理 IP + 端口
- 核心接口 :
IRule(负载均衡策略接口)
1.2 内置负载均衡策略
| 策略类 | 说明 |
|---|---|
| RoundRobinRule | 默认轮询,依次请求服务实例 |
| RandomRule | 随机选择服务实例 |
| RetryRule | 重试机制,先轮询,失败则重试 |
| WeightedResponseTimeRule | 根据响应时间分配权重,响应越快权重越高 |
1.3 Ribbon 实战代码
1.3.1 配置 RestTemplate(开启负载均衡)
java
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.eureka.IRule;
import org.springframework.cloud.netflix.ribbon.eureka.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
* Ribbon 配置类
*/
@Configuration
public class RibbonConfig {
/**
* 注入 RestTemplate
* @LoadBalanced 核心注解:开启负载均衡
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
/**
* 自定义负载均衡策略(可选)
* 这里配置为 随机策略
*/
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
}
1.3.2 服务消费端调用
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
/**
* 通过 Ribbon 调用远程服务
* 直接使用服务名:ribbon-provider
*/
@GetMapping("/getUser/{id}")
public User getUserById(@PathVariable Integer id) {
String url = "http://ribbon-provider/provider/getUser/" + id;
return restTemplate.getForObject(url, User.class);
}
}
二、Feign 声明式调用详解
2.1 核心概念
- 作用 :声明式 HTTP 客户端,替代 RestTemplate,让远程调用像调用本地方法一样简单
- 优势 :
- 代码简洁,无需拼接 URL、处理参数
- 天然集成 Ribbon,自带负载均衡
- 支持 SpringMVC 注解,学习成本极低
2.2 Feign 使用步骤
- 引入 Feign 依赖
- 启动类添加
@EnableFeignClients开启功能 - 编写 Feign 接口(绑定服务提供者)
- 直接注入接口调用方法
2.3 Feign 实战代码
2.3.1 引入依赖(Maven)
XML
<!-- Feign 核心依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Nacos 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.3.2 启动类开启 Feign
java
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 // 注册到 Nacos
@EnableFeignClients // 开启 Feign 功能(核心注解)
public class FeignConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(FeignConsumerApplication.class, args);
}
}
2.3.4 业务层调用
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/consumer")
public class FeignConsumerController {
// 直接注入 Feign 接口,像本地方法一样调用
@Autowired
private UserFeignService userFeignService;
@GetMapping("/getUser/{id}")
public User getUser(@PathVariable Integer id) {
// 一行代码完成远程调用!
return userFeignService.getUserById(id);
}
}
2.3.5 Feign 超时配置(application.yml)
Feign 底层依赖 Ribbon,超时配置使用 Ribbon 参数:
java
ribbon:
# 请求连接超时时间(毫秒)
ConnectTimeout: 5000
# 请求处理超时时间(毫秒)
ReadTimeout: 5000
三、核心原理(面试常问)
3.1 Ribbon 负载均衡原理
@LoadBalanced给 RestTemplate 添加拦截器- 拦截请求,解析 URL 中的服务名
- 从 Nacos 获取服务的所有实例列表
- 根据负载均衡策略选择一个实例
- 将服务名替换为真实 IP + 端口,发起请求
3.2 Feign 执行原理
@EnableFeignClients扫描所有@FeignClient接口- 为接口生成动态代理对象,注入 Spring 容器
- 调用方法时,代理对象组装 HTTP 请求
- 结合 Ribbon 负载均衡,发送请求并返回结果
四、高频易错点(避坑指南)
- Feign 注解必须写全
@PathVariable("id")必须指定 value 值,否则报错 - 服务名区分大小写Feign/Ribbon 调用的服务名必须和 Nacos 注册名完全一致
- 包扫描问题 Feign 接口不在启动类包下,需指定
@EnableFeignClients(basePackages = "xxx.xxx") - 超时配置 Feign 超时不使用 feign 前缀,统一用
ribbon.xxx配置
五、总结
- Ribbon 是客户端负载均衡工具,配合
@LoadBalanced实现服务调用 - Feign 是声明式调用组件,简化代码,天然集成 Ribbon
- 实际开发中,优先使用 Feign,代码更简洁、维护更方便
- 核心注解:
@LoadBalanced、@EnableFeignClients、@FeignClient