Spring Cloud中,如何解决Feign/Ribbon第一次请求失败的问题?

在Spring Cloud中,Feign和Ribbon整合Hystrix后可能出现首次请求失败的情况,主要原因是Hystrix默认1秒超时。解决办法包括:延长Hystrix超时时间、禁用Hystrix超时、全局禁用Feign的Hystrix、配置Ribbon饥饿加载和模拟请求预热。其中,Ribbon的饥饿加载是最佳实践,但在某些特定场景可能存在风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring Cloud中,Feign和Ribbon在整合了Hystrix后,可能会出现首次调用失败的问题,要如何解决该问题呢?

2019-01-28  16:19:46.074    INFO  3740  ---  [nio-9790-exec-2]  s.c.a.AnnotationConfigApplicationContext  :  Refreshing  SpringClientFactory-ms-dyh-manufacturer:  startup  date  [Mon  Jan  28  16:19:46  CST  2019];  parent:  org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@15b986cd
2019-01-28  16:19:46.411    INFO  3740  ---  [nio-9790-exec-2]  f.a.AutowiredAnnotationBeanPostProcessor  :  JSR-330  'javax.inject.Inject'  annotation  found  and  supported  for  autowiring
2019-01-28  16:19:46.671    INFO  3740  ---  [nio-9790-exec-2]  c.netflix.config.ChainedDynamicProperty    :  Flipping  property:  ms-dyh-manufacturer.ribbon.ActiveConnectionsLimit  to  use  NEXT  property:  niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit  =  2147483647
2019-01-28  16:19:46.715    INFO  3740  ---  [nio-9790-exec-2]  c.n.u.concurrent.ShutdownEnabledTimer        :  Shutdown  hook  installed  for:  NFLoadBalancer-PingTimer-ms-dyh-manufacturer
2019-01-28  16:19:46.776    INFO  3740  ---  [nio-9790-exec-2]  c.netflix.loadbalancer.BaseLoadBalancer    :  Client:  ms-dyh-manufacturer  instantiated  a  LoadBalancer:  DynamicServerListLoadBalancer:{NFLoadBalancer:name=ms-dyh-manufacturer,current  list  of  Servers=[],Load  balancer  stats=Zone  stats:  {},Server  stats:  []}ServerList:null
2019-01-28  16:19:46.783    INFO  3740  ---  [nio-9790-exec-2]  c.n.l.DynamicServerListLoadBalancer            :  Using  serverListUpdater  PollingServerListUpdater
2019-01-28  16:19:46.810    INFO  3740  ---  [nio-9790-exec-2]  c.netflix.config.ChainedDynamicProperty    :  Flipping  property:  ms-dyh-manufacturer.ribbon.ActiveConnectionsLimit  to  use  NEXT  property:  niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit  =  2147483647
2019-01-28  16:19:46.811    INFO  3740  ---  [nio-9790-exec-2]  c.n.l.DynamicServerListLoadBalancer            :  DynamicServerListLoadBalancer  for  client  ms-dyh-manufacturer  initialized:  DynamicServerListLoadBalancer:{NFLoadBalancer:name=ms-dyh-manufacturer,current  list  of  Servers=[192.168.0.114:8360],Load  balancer  stats=Zone  stats:  {unknown=[Zone:unknown;	Instance  count:1;	Active  connections  count:  0;	Circuit  breaker  tripped  count:  0;	Active  connections  per  server:  0.0;]
},Server  stats:  [[Server:192.168.0.114:8360;	Zone:UNKNOWN;	Total  Requests:0;	Successive  connection  failure:0;	Total  blackout  seconds:0;	Last  connection  made:Thu  Jan  01  08:00:00  CST  1970;	First  connection  made:  Thu  Jan  01  08:00:00  CST  1970;	Active  Connections:0;	total  failure  count  in  last  (1000)  msecs:0;	average  resp  time:0.0;	90  percentile  resp  time:0.0;	95  percentile  resp  time:0.0;	min  resp  time:0.0;	max  resp  time:0.0;	stddev  resp  time:0.0]
]}ServerList:ConsulServerList{serviceId='ms-dyh-manufacturer',  tag=null}

造成该问题的原因

Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码。而首次请求往往会比较慢(由于Ribbon是懒加载的,在首次请求时,才会开始初始化相关类),这个响应时间可能就大于1秒了。知道原因后,我们来总结一下解决方案。以feign为例,解决方案有如下四种。

方法一、将Hystrix超时设长

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000

该配置是让Hystrix的超时时间改为5秒,这是最容易想到的办法,不过有点治标不治本。

方法二、禁用Hystrix超时

hystrix.command.default.execution.timeout.enabled: false


方法三、为Feign禁用Hystrix该配置,用于禁用Hystrix的超时时间,一般不建议使用。

全局禁用

feign.hystrix.enabled: false


局部禁用索性禁用feign的hystrix,该做法比较极端,除非一些特殊场景,不推荐使用。

为名为microservice-provider-user 的Feign Client禁用Hystrix

@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
  @GetMapping("/users/{id}")
  User findById(@PathVariable("id") Long id);
}
class FooConfiguration {
  @Bean
  @Scope("prototype")
  public Feign.Builder feignBuilder(){
    return Feign.builder();
  }
}

方法四、Ribbon配置饥饿加载(最佳)
 

从Dalston开始,Ribbon支持配置eager load实现在启动时就初始化Ribbon相关类。

然后在调用方服务yml增加下面的配置(A调用B, 就在A配置):

ribbon:
  eager-load:
    enabled: true
    clients: client1, client2, client3

Dalson之前版本可以通过一些Hack的机制实现eager load,不过成本略高,不建议,这里就不贴了。

但是这种方法肯存在一定的风险,feign的contributor有提到他们之所以用lazy creation是因为不这么做的话在某些特定场景下会存在问题。

在这里插入图片描述

 

方法五、模拟请求进行warm up

基本思路:在spring容器初始化后,找到所有实现了FeignClient 的bean,主动发起任意请求,该请求会导致feign client的真正初始化。

step1. 对feign client的接口添加方法

@GetMapping("/actuator/health")
String heartbeat();

step2. 添加ApplicationListener在spring context加载完后,找到所有的feign client,并通过反射执行一次heart beat,此时便会取巧地触发feign client的初始化。

@Component
public class EarlyInitFeignClientOnContextRefresh implements
        ApplicationListener<ContextRefreshedEvent> {
    Logger logger = LoggerFactory.getLogger(EarlyInitFeignClientOnContextRefresh.class);

    @Autowired()
    @Qualifier("cachingLBClientFactory")
    CachingSpringLoadBalancerFactory factory;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(FeignClient.class);
        for (Map.Entry<String, Object> entry :
                beans.entrySet()) {
            Class<?> clazz = entry.getValue().getClass();
            try {
                Method method = null;
                method = clazz.getMethod("heartbeat");
                method.invoke(entry.getValue());
                logger.warn("init feign client:  " + clazz.getName());
            } catch (NoSuchMethodException e) {
                logger.warn("init feign client fail: no method of heartbeat in " + clazz.getName());
            } catch (IllegalAccessException e) {
                logger.warn("init feign client fail: IllegalAccessException of " + clazz.getName());
            } catch (InvocationTargetException e) {
                logger.warn("init feign client fail: InvocationTargetException of " + clazz.getName());
            } catch (Exception e){
                logger.error(e.getMessage());
            }
        }

        logger.info("init feign client done!");
    }
}

扩展思考


1.FeignClient的初始化过程;
2.FeignClient初始化各阶段的消耗时长,进而具体哪一步耗时最长(是否是注册和发现);
3.当FeignClient调用的服务不在线时,能否保证方法三仍旧有效;
4.切入到FeignClient真实创建过程的初始化,而非通过使用client发起请求模拟地达到这个目的。
 

 参考文章链接:

https://www.itmuch.com/spring-cloud-feign-ribbon-first-request-fail/

https://blog.youkuaiyun.com/qq83833224/article/details/86680664

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值