文章目录
- 前言
- [进阶用法 - 日志](#进阶用法 - 日志)
- [进阶用法 - 超时控制](#进阶用法 - 超时控制)
- [进阶用法 - 配置文件](#进阶用法 - 配置文件)
- [进阶用法 - 重试机制](#进阶用法 - 重试机制)
- 进阶用法-拦截器
- [进阶用法 - Fallback](#进阶用法 - Fallback)

前言
前文介绍了OpenFeign的基本用法,而本文重点内容为OpenFeign的进阶用法,包括日志配置、超时控制、重试机制、拦截器、兜底回调等。
进阶用法 - 日志

配置文件
java
logging:
level:
com.qf.feign: debug
配置类
java
@Configuration
public class BeanConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
测试类
java
@SpringBootTest
public class LoadBalancerTest {
@Autowired
private TestFeignClient testFeignClient;
@Test
void Test(){
String details = testFeignClient.getDetails();
System.out.println("details = " + details);
}
}
打印openfeign相关日志
进阶用法 - 超时控制

连接超时默认10秒,读取超时默认60秒
通过让商品服务接口休眠(也可以在商品服务中打断点)超过60秒,来查看订单服务的报错信息
js
2025-09-11T22:57:59.629+08:00 DEBUG 38868 --- [qf-service-order] [nio-8080-exec-3] com.qf.feign.ProductFeignClient : [ProductFeignClient#getProductById] ---> GET http://qf-service-product/product/11 HTTP/1.1
2025-09-11T22:57:59.629+08:00 DEBUG 38868 --- [qf-service-order] [nio-8080-exec-3] com.qf.feign.ProductFeignClient : [ProductFeignClient#getProductById] ---> END HTTP (0-byte body)
2025-09-11T22:58:59.650+08:00 DEBUG 38868 --- [qf-service-order] [nio-8080-exec-3] com.qf.feign.ProductFeignClient : [ProductFeignClient#getProductById] <--- ERROR SocketTimeoutException: Read timed out (60020ms)
2025-09-11T22:58:59.650+08:00 DEBUG 38868 --- [qf-service-order] [nio-8080-exec-3] com.qf.feign.ProductFeignClient : [ProductFeignClient#getProductById] java.net.SocketTimeoutException: Read timed out
at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:283)
at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:309)
at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:350)
at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:803)
at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966)
at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:244)
at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:284)
at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:343)
at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:827)
at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:762)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1688)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1589)
at java.base/java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:529)
at feign.Client$Default.convertResponse(Client.java:111)
at feign.Client$Default.execute(Client.java:107)
at org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(LoadBalancerUtils.java:56)
at org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(LoadBalancerUtils.java:91)
at org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient.execute(FeignBlockingLoadBalancerClient.java:134)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:100)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:70)
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:99)
at jdk.proxy2/jdk.proxy2.$Proxy71.getProductById(Unknown Source)
at com.qf.service.impl.OrderServiceImpl.createOrder(OrderServiceImpl.java:31)
at com.qf.controller.OrderController.createOrder(OrderController.java:23)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:281)
at org.springframework.cloud.context.scope.GenericScope$LockedScopedProxyFactoryBean.invoke(GenericScope.java:482)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:768)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:720)
at com.qf.controller.OrderController$$SpringCGLIB$$0.createOrder(<generated>)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:483)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:344)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:384)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:905)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1190)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)
at java.base/java.lang.Thread.run(Thread.java:833)
以上日志中可以看到feign调用60秒后报错java.net.SocketTimeoutException: Read timed out
也可以在配置文件中配置
yml
spring:
cloud:
openfeign:
client:
config:
# 默认配置
default:
logger-level: full
connect-timeout: 1000
read-timeout: 2000
进阶用法 - 配置文件
单个配置文件中配置太多属性会很乱,因此可以写多个配置文件,在主配置文件中引入
添加application-feign.yml配置文件,专门用于配置openfeign相关属性
yml
spring:
cloud:
openfeign:
client:
config:
# 默认配置
default:
logger-level: full
connect-timeout: 1000
read-timeout: 2000
# 指定调用商品服务配置
qf-service-product:
logger-level: full
connect-timeout: 3000
read-timeout: 5000
在主配置文件中加入
yml
spring:
profiles:
active: prod
include: feign
openfeign客户端的名称主要可以用属性contextId来配置,不写的话会取value值配置。
java
@FeignClient(value = "qf-service-product",contextId = "qf-service-product") // feign客户端
public interface ProductFeignClient {
...
}
进阶用法 - 重试机制

配置类中加入
java
@Configuration
public class BeanConfig {
...
//超时重试
@Bean
Retryer retryer(){
//进入
return new Retryer.Default();
}
}
↓源码
java
public static class Default implements Retryer {
...
public Default() {
this(100L, TimeUnit.SECONDS.toMillis(1L), 5);
}
...
}
参数说明:
当第一次超时后,间隔100毫秒进行第二次请求,如果没有返回则在之前基础上time1.5发送第三次请求,如果没有返回则在time 1.5*1.5毫秒发送第四次请求,总共发送五次,最长间隔时间为1秒
商品服务中让接口进行Thread.sleep(10000),在日志中可以看到商品服务接口被调用了5次
js
2025-02-23 18:36:10 id:22
2025-02-23 18:36:16 id:22
2025-02-23 18:36:21 id:22
2025-02-23 18:36:26 id:22
2025-02-23 18:36:32 id:22
进阶用法-拦截器

请求拦截器
在配置文件中设置拦截器(对指定feign客户端生效,如订单服务)
java
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class XTokenRequestInterceptor implements RequestInterceptor {
/**
* 请求拦截器
* @param template 请求模板
*/
@Override
public void apply(RequestTemplate template) {
System.out.println("XTokenRequestInterceptor ....... ");
template.header("X-Token", UUID.randomUUID().toString());
}
}
配置文件
yml
spring:
cloud:
openfeign:
client:
config:
# 默认配置
default:
logger-level: full
connect-timeout: 1000
read-timeout: 2000
# 指定调用商品服务配置
qf-service-product:
logger-level: full
connect-timeout: 3000
read-timeout: 5000
request-interceptors:
- com.qf.interceptor.XTokenRequestInterceptor
进阶用法 - Fallback
注意:此功能需要整合 Sentinel 才能实现
如果没有设置兜底返回,如当关闭商品服务会直接报错。
相关代码
兜底回调
java
import com.qf.feign.ProductFeignClient;
import com.qf.entity.Product;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
@Component
public class ProductFeignClientFallback implements ProductFeignClient {
@Override
public Product getProductById(Long id) {
System.out.println("兜底回调....");
Product product = new Product();
product.setId(id);
product.setPrice(new BigDecimal("0"));
product.setProductName("未知商品");
product.setNum(0);
return product;
}
}
java
import com.qf.entity.Product;
import com.qf.feign.fallback.ProductFeignClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
//填写需要远程调用的服务
@FeignClient(value = "qf-service-product", fallback = ProductFeignClientFallback.class) // feign客户端
public interface ProductFeignClient {
//mvc注解的两套使用逻辑
//1、标注在Controller上,是接受这样的请求
//2、标注在FeignClient上,是发送这样的请求
@GetMapping("/product/{id}")
Product getProductById(@PathVariable("id") Long id);
}
此时因为没有加入sentinel依赖,所以发生报错时兜底回调并没有发生。
加入依赖
xml
<!-- 熔断器-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
配置文件开启熔断
yml
feign:
sentinel:
enabled: true
此时关闭商品服务,调用订单服务接口