【Spring Cloud总结】21.单个FeginClient禁用Hystrix

接上篇《20.Feign对Hystrix的支持》  Spring Cloud版本为Finchley.SR2版

上一篇我们简单介绍了如何开启Feign对Hystrix的支持,并演示了实现FeignClient接口以及FallbackFactory接口两种形式的降级服务。下面我们来学习一下,在存在多个FeignClient的情况下,如何禁用单个的Hystrix。
本部分官方文档:https://cloud.spring.io/spring-cloud-static/Finchley.SR2/single/spring-cloud.html

一、单个Feign Client禁用Hystrix原理

通过上一篇的学习我们可以知道,如果对Feign需要开启或者禁用Hystrix,需要在配置文件中设置feign.hystrix.enabled参数。但是要知道,feign.hystrix.enabled参数,是设置全局的Feign是否支持Hystrix,但是假如我们的应用中有多个Feign Cleint对象,想让其中一部分支持Hystrix,一部分不支持,该怎么办呢?这里就需要提到之前Feign的默认配置了。

回忆一下,Feign组件支持我们对其的核心参数进行自定义配置化,它的默认配置是FeignClientsConfiguretion,默认配置项有以下几个参数:
Decoder feignDecoder: ResponseEntityDecoder (为SpringDecoder的包装类)
Encoder feignEncoder: SpringEncoder
Logger feignLogger: Slf4jLogger
Contract feignContract: SpringMvcContract
Feign.Builder feignBuilder: HystrixFeign.Builder
Client feignClient: 如果启用的Ribbon,则为LoadBalancerFeignClient, 否则使用默认的FeignClient。
这里Decoder为请求信息的编码器,Encoder为请求信息的解码器,Logger为日志类, Contract为Feign的契约类(功能是将我们传入的接口进行解析验证,看注解的使用是否符合规范,然后返回给我们接口上各种相应的元数据),Builder为Feign的构造类(默认为HystrixFeign的Builder),然后就是Feign的Client。

我们要实现为单个FeignClient禁用Hystrix,就需要在自定义配置文件中修改其Feign.Builder参数,即修改Feign的构造类。官方文档提供的方法为:

那为什么要修改其构造类,添加上“@Scope("prototype")”注解,就能够实现禁用单个Feign的效果呢?
默认情况下,Feign是整合了Hystrix的,我们打开Spring Cloud中Feign组件的FeignClientsConfiguretion类查看其有关Budiler的配置项(在spring-cloud-openfeign-core-2.0.2.RELEASE.jar包下):

Budiler部分配置代码:

@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    @ConditionalOnProperty(name = "feign.hystrix.enabled")
    public Feign.Builder feignHystrixBuilder() {
        return HystrixFeign.builder();
    }
}

我们可以看到,在Feign的默认配置类中,有关于Feign构造器默认使用的就是HystrixFeign的builder,即支持Hystrix的构造器。如果我们不需要该FeignClient支持Hystrix,在自定义的配置类中,将Budiler定义为Feign自己的不支持Hystrix的构造类即可。

最后顺便说一下“@Scope("prototype")”注解,这个其实我们在之前在《Hystrix的CommandProperties配置》一章的“Hystrix线程隔离策略”小节中已经提到过,“Scope”就是Spring定义bean的生命周期的参数,常用的有singleton和prototype两种,其中:
(1)singleton:为“单一实例”的意思,一个容器中只存在一个实例,所有对该类型bean的依赖都引用这一单一实例。
(2)prototype:容器在接受到该类型的对象的请求的时候,会每次都重新生成一个新的对象给请求方。
很显然,Feign对于Builder的Bean的管理,是希望其接受到该类型对象的请求时,每次都生成新对象,

二、实例测试

理解了如何禁用翻个Feign组件的Hystrix后,我们在之前的工程上试验一下。之前Movie工程中只有一个UserFeignClient和自定义配置类TestFeignConfiguration:

我们再添加一个UserFeignClient2以及自定义配置类TestFeignConfiguration2:

UserFeignClient2代码:

package com.microserver.cloud;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;

import com.microserver.cloud.entity.User;
import com.microserver.config.TestFeignConfiguration2;

import feign.Param;
import feign.RequestLine;
import feign.hystrix.FallbackFactory;

/**
 * 当@FeignClient有name和url还有configuration时,取值为url的地址,name只是为一个名称而已(无意义)
 * 当@FeignClient只有name和configuration时,name的取值为eureka中的application项目的名称即虚拟地址
 *
 */
@FeignClient(name="microserver-provider-user2",url="http://localhost:7902",configuration = TestFeignConfiguration2.class,fallbackFactory=FeignClientFallbackFactory2.class)
public interface UserFeignClient2 {
    
    @RequestLine("GET /findById/{id}")
    public User findById(@Param("id") Long id);
    
    @RequestLine("POST /postUser")
    public String postUser(@RequestBody User user);
    
}

@Component
class FeignClientFallbackFactory2 implements FallbackFactory<UserFeignClient2>{

    //日志对象
    private static final Logger LOGGER = LoggerFactory.getLogger(FeignClientFallbackFactory2.class);

    @Override
    public UserFeignClient2 create(Throwable cause) {
        return new UserFeignClient2() {
            
            @Override
            public User findById(Long id) {
                // 日志最好放在各个fallback方法中,而不要直接放在create方法中。
                // 否则在引用启动时,就会打印该日志
                FeignClientFallbackFactory2.LOGGER.info("fallback2; reason was: ", cause);
                
                User user = new User();
                user.setId(0L);
                user.setName("FeignClient2 降级服务");
                return user;
            }

            @Override
            public String postUser(User user) {
                return null;
            }
        };
    }
}

需要注意的是,这里的@FeignClient注解的name一定不要和之前的一样,因为如果有多个@FeignClient注解使用了相同的name属性,则注解的configuration参数会被覆盖。至于谁覆盖谁要看Spring容器初始化Bean的顺序。所以为了避免混乱,这里我们随意定义了一个name,当然这个name对应的application实例是不存在的,直接会去取url的服务地址作为请求地址。

这里我们依然为其添加了fallbackFactory参数,是为了测试引用是否有效。如果禁用有效,就不会进入降级方法。配置类TestFeignConfiguration2代码:

package com.microserver.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import feign.Feign;

@Configuration
public class TestFeignConfiguration2 {
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    }
    
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }
    
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

这里设置Builder为Feign的原始Builder。然后在Controller中添加一个新的请求响应方法,来试验UserFeignClient2:

@Autowired
private UserFeignClient2 userFeignClient2;

@GetMapping("/movie2/{id}")
public User findUserById2(@PathVariable Long id){
    return userFeignClient2.findById(id);
}

然后我们启动之前的服务提供者user工程、服务消费者movie工程以及eureka Server注册中心,访问“movie/1”服务以及“movie2/1”是好的:


然后关掉服务提供者user工程,再次访问“movie/1”服务,可以看到此时进入了降级服务中:

然后我们再访问“movie2/1”服务,发现没有进入降级服务,而是进行了报错:


说明我们为单个FeignClient禁用Hystrix是成功的。

三、为单个FeignClient禁用Hystrix的意义

虽然我们上面实现了为单个FeignClient禁用Hystrix的效果,但是看起来好像没有什么实现的意义,貌似看起来有Hystrix的支持更加稳妥一些。那么究竟什么样的场景下,需要对单个FeignClient的Hystrix进行禁用呢?

spring cloud通过feign client进行服务之间调用的时候,默认不会进行重试,这样会有一个问题,比如你的服务在滚动升级重启的时候,feign的调用将直接失败,但其实滚动重启情况下,重启了一个服务实例,此时还有另外一个服务实例是可用的,应该允许自动均衡策略重试请求发送到另外一个可用的服务实例上去。

上面就是feign的重复请求策略,hystrix的熔断机制可能会导致fegin的重复请求策略无效(重复请求的超时时间必须小于hystrix的熔断时间)。所以在某些情况下就需要选择关闭fegin的hystrix。

参考:《51CTO学院Spring Cloud高级视频》
https://blog.csdn.net/xiao_jun_0820/article/details/79292461

转载请注明出处:https://blog.csdn.net/acmman/article/details/102069407

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章