day_1 分布式入门: OpenFeign
OpenFeign
依赖导入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
声明式实现与远程调用
要启用OpenFeignClient
的依赖注入,需要先在Application
启动类上加上@EnableFeignClients
注解。
用法示例:
@FeignClient(value = "service-order")
public interface FeignOrderClient {
@GetMapping("/order/{id}")
OrderData getOrder(@PathVariable("id") int id);
}
@FeignClient
注解中填入服务名称时,将自行查找在配置中心注册过的服务,可以通过MVC的Mapping注解发送请求;也可以再加上url
属性及对应的值,此时将向该地址发送请求,可以调用第三方API。在方法中也可以通过@RequestParam
、@RequestBody
、@RequestHeader
等对参数进行注解,声明对应的请求参数。
超时控制
FeignClient
默认的的读取超时为60s,连接超时为10s.也可以通过在配置文件中设置形如下方的配置示例进行调整:
spring:
cloud:
openfeign:
client:
config:
default:
logger-level: full
connectTimeout: 1000
readTimeout: 3000
service-order:
logger-level: full
connect-timeout: 2000
read-timeout: 5000
其中config
下的default
为可指定的FeignClient
的默认配置,也可以通过类似service-order
的键来对指定的client进行配置,这里与@FeignClient(value = "service-order")
中的value
一致,也可以是属性contextId
,那此时注解就变成了@FeignClient(value = "service-order", contextId="xxxx")
重试配置
OpenFeign
提供了重试器的配置,可以在配置文件中配置,也可以通过注册相应的Bean对象来为FeignClient
提供重试器。
通过配置文件:
spring.cloud.openfeign.client.config.default.retryer=feign.Retryer.Default
spring.cloud.openfeign.client.config.service-order.retryer=feign.Retryer.Default
通过@Bean
注解:
@Configuration
public class ProductConfiguration {
@LoadBalanced
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
Retryer retryer(){
return new Retryer.Default();
}
}
Retryer
提供的Default
类也可以传递重试的配置参数:
public Default() {
this(100, SECONDS.toMillis(1), 5);
}
public Default(long period, long maxPeriod, int maxAttempts) {
this.period = period;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}
拦截器
OpenFeign
也提供了对于请求或响应的不同拦截器。需实现RequestInterceptor
接口
public class XTokenRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("X-Token", "123456");
}
}
如果要投入使用,可以在配置文件中添加配置针对特定客户端启用:
spring:
cloud:
openfeign:
client:
config:
service-order:
request-interceptors:
- work.slhaf.product.interceptor.XTokenRequestInterceptor
也可以在拦截器的实现类上加上@Component
注解,容器会自动发现拦截器实例
Fallback兜底返回
当借助OpenFeign
进行的远程调用出错/超时时,有时我们会希望返回一些默认值以便出错时流程也能够正常进行,而不是输出错误信息,为此OpenFeign
也提供了Fallback
机制。我们可以通过实现声明式实现与远程调用一节中提到的用于发送请求的FeignClient
中的请求方法,在其中实现兜底逻辑并返回。
比如:
@Component
public class FeignOrderClientFallback implements FeignOrderClient {
@Override
public OrderData getOrder(int id) {
OrderData orderData = new OrderData();
orderData.setId(0);
orderData.setName("fail");
return orderData;
}
}
同样需要加上@component
注解,同时也需要在相应的FeignClient
中指定Fallback
用到的类:
@FeignClient(value = "service-order",fallback = FeignOrderClientFallback.class)
public interface FeignOrderClient {
@GetMapping("/order/{id}")
OrderData getOrder(@PathVariable("id") int id);
}
但这个行为需要配合sentinel
使用,需要在配置文件中开启熔断功能,即加上以下配置:
feign:
sentinel:
enabled: true
sentinel
翻译过来是哨兵来着,不过两个也确实都挺形象的
额外思考
在课程提到Fallback兜底返回
时,我想到的是应该也能够使用OpenFeign响应拦截器实现,或者说try-catch也能实现,但OpenFeign为什么又提供了一种机制?它的实现是封装了响应拦截器、还是异常捕获吗?还是说新增了一些东西使得这种兜底机制更合适、安全?
问了一下ChatGPT,这是它的回答:
非常棒的问题,问到了OpenFeign
Fallback
机制的设计本质,这也是你从“使用者”走向“理解者”的关键一步。我们来拆解这个问题,从多个维度看清楚:
🔍 1. 你提到的三种方式是并列的吗?
你提到的三种方案:
方法 能否做到兜底? 优点 局限 try-catch ✅ 灵活、细粒度控制 需要手动写所有异常处理逻辑,容易遗漏;不集中 响应拦截器( RequestInterceptor
、ResponseInterceptor
)❌(无法兜底返回) 可统一处理 header、日志等 无法替代 fallback,仅能拦截请求或响应 OpenFeign Fallback
✅ 自动触发,和调用接口解耦 需依赖容器注入、可能影响调试透明性 所以回答第一个小问题:
❗ OpenFeign 的
Fallback
并不是“多此一举”,而是“异常处理职责的一种优雅集中方案”。
⚙️ 2. 那它是怎么实现的?真的用了拦截器 or try-catch?
我们来看 OpenFeign 的机制实现,分为两个阶段:
✅ Feign 默认机制:异常抛出
Feign 的默认行为是:请求失败就抛异常,比如连接失败、5xx 等,会抛出如
RetryableException
、FeignException
。✅ Fallback 机制触发条件:
当你在
@FeignClient
注解中配置了fallback = Xxx.class
时:
- Spring Cloud 会用 Hystrix(旧)或 Sentinel(新)等“熔断框架”对 Feign 的代理对象进行包装。
- 如果远程调用失败、熔断器打开、异常触发(包括运行时异常/连接失败等),
它会自动走到 fallback 实例中定义的实现方法。✅ 底层实现原理(以 Sentinel 为例):
Feign 客户端通过
SentinelInvocationHandler
包装。内部逻辑大致为:
try { // 发起真正的 HTTP 请求 return methodHandler.invoke(...); } catch (Throwable t) { // 如果 fallback 存在,调用 fallback 实现的方法 return fallbackMethodHandler.invoke(...); }
fallback 方法由 Spring IOC 提供(即你
@Component
的那一类)
🔐 3. 为啥 OpenFeign 推荐使用 fallback,而不是 try-catch?
🔸 try-catch 的问题:
- 你要在每次调用都写 try-catch,重复又容易出错。
- 不利于全局熔断、指标收集(比如你想统计某服务的失败率?)
- 不容易统一处理不同接口的异常策略(兜底值?返回空?默认对象?提示?)
🔸 fallback 的优势:
- 集中处理,每个接口方法只写一次兜底逻辑
- 与接口定义在一起,调用端逻辑干净
- 更适合配合熔断框架自动接管失败流程,安全、可控、可扩展
📌 小结一句话
Fallback 是在“你无法预知服务是否正常”场景下的一种声明式安全网,它基于底层异常机制 + 动态代理封装 + 熔断框架的组合,不是简单的 try-catch 或拦截器替代品。
总结:
尽管我们可以用 try-catch 捕获远程调用异常,OpenFeign 仍推荐使用 fallback 机制。其原因在于 fallback 是基于 Spring Cloud + 熔断框架(如 Sentinel)的高级封装,它统一了异常处理逻辑、增强了系统健壮性,也更适合分布式架构下的失败恢复策略。
与 try-catch 相比,fallback 更集中、解耦,且支持自动熔断触发;与拦截器不同,它处理的是请求失败的“后果”,而不是请求本身。因此,fallback 是面向“异常兜底”的最佳实践。