FeignClient引起ApplicationListener.onApplicationEvent()多次执行

场景

Spring cloud 或者 Spring boot项目中,使用FeignClient 实现客户端调用。项目中有通过ApplicationListener初始化的方法。

@Component
@Slf4j
public class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        log.info("--------- 执行 监听器,event:{}",event.getApplicationContext().getDisplayName());
    }
}

问题现象

  • 项目启动时,ApplicationListener.onApplicationEvent() 执行两次。
  • FeignClient第一次调用超时,也会触发执行ApplicationListener.onApplicationEvent() 。
  • 调用服务异常时,触发ApplicationListener.onApplicationEvent()执行一次。

通过debug 代码,发现启动时执行的两次,第一次的事件是 FeignContext :

 第二次执行:

事件是spring boot启动加载事件

 服务接口调用异常,触发fallback 熔断器时,也会执行:

 

github上有人提交issues:https://github.com/spring-cloud/spring-cloud-openfeign/issues/119

但到目前还没有解决该bug.

可是换做其他启动init方式,spring boot 还可以使用以下方式初始化,避免与Feign 冲突。

方法一:

@Component
@Slf4j
public class MyPostConstruct {

    @Value("${spring.application.name}")
    private String name;

    @PostConstruct
    private void init() {
        log.info("appName:{}",name);
    }
}

注意:使用方法一时,不能同时使用@ConfigurationProperties 注解,否则也会导致加载两次。

方法二:

@Component
@Slf4j
public class MyApplicationRunner implements ApplicationRunner {
    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("MyApplicationRunner run...");
    }
}

方法三:

@Component
@Slf4j
public class MyCommandLineRunner implements CommandLineRunner {

    @Value("${spring.application.name}")
    private String name;

    @Override
    public void run(String... args) throws Exception {
        log.info("MyStartRunner run...,appName:{}",name);
    }
}

以上三种方式,都不会出现启动加载两次的现象。

如果必须要使用ApplicationListener方式,可以使用以下方式,避免重复加载:

@Component
@Slf4j
public class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        log.info("--------- 执行 监听器,event:{}",event.getApplicationContext().getDisplayName());
        
        String displayName = event.getApplicationContext().getDisplayName();
        if(displayName.contains("FeignContext") || displayName.contains("SpringClientFactory")) {
            return;
        }
        //todo semo code
    }

}

关于第一次访问超时的问题,目前没有太好的解决方式,只能是配置较长的超时时间。

配置如下:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 60000
feign:
  hystrix:
    enabled: true
  ribbon:
    warmup: true

ribbon:
  ConnectTimeout: 20000
  ReadTimeout: 20000

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值