spring-cloud之openfeign

微信搜索【小二说码】关注,定期分享技术,共同进步

Feign

我来说Feign

  • 本文的cloud的版本为:Greenwich.RELEASE
  • Feign(不就是大粪么!!_ 这个东西可不简单哦)是什么?根据官网解释:
    • 客户端组件,提供客户端和服务端之间调用。
    • 可以通过@Configuration注解或者配置文件来加载配置
    • 支持SpringMVC注解,并使用HttpMessageConverters作为消息转换器,比如@GetMapping
    • 两大重量级护法:Ribbon来实现负载和重试;Hystrix来实现降级和熔断
      Feign组件
  • FeignClient通过JDK动态代理来构建Client对象,并封装方法和参数到对象Map<Method, MethodHandler>,调用的时候通过方法名和参数来调用

服务远程调用方式

  • 方式1:LoadBalanceClient的方式 http://ip:port/path(LoadBalanceClient+RestTemplate)
@Autowired
private LoadBalancerClient loadBalancerClient;
@Resource
private RestTemplate restTemplate;
// 调用 这里示例
restTemplate.getForObject(http:ip+port+path,xxx);
  • 方式2:Ribbon: http://servicaName/path(RestTemplate+Ribbon)
// 引入Bean
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
    
    
    return new RestTemplate();
}
// 直接在类中注入
@Resource
private RestTemplate restTemplate;

// 引用
// 调用 这里示例
restTemplate.getForObject(serviceId+path,xxx);
  • 方式3:本章的大杀器**Feign** 通过集成Ribbon,代理集成功能实现接口方式的调用,@FeignClient(“service-name”)
// 直接在类中注入标注了@FeignClient注解的 orderService
@Autowired 
private OrderService orderService;
  • 前面两种方式调用都比较麻烦,需要注入对象并且在调用的时候需要拼接http,而第三种Feign的方式是直接通过注入对象的方式来处理,使用起来相当方便,像使用本地的Service,所以重点讲解

Feign开发

入门使用
高级使用
自定义配置(日志、拦截器、编码和解码)
  • 日志bean配置方式
    • 在FeignClient中增加configuration @FeignClient(name = "storage", fallback = StorageFallback.class, configuration = FeignConfiguration.class)
    • 覆盖默认配置的方式1:FeignConfiguration配置bean,重写默认的Feign配置,这个时候类不要配置@Configuration注解,避免覆盖了其它Feign的默认配置
// 千万不要配置@Configuration注解
@Slf4j
public class FeignConfiguration {
    
    
	// 配置Bean
	@Bean
	public Logger.Level level() {
    
    
	    return Logger.Level.FULL;
	}
}

// 注意如果日志不生效需要配置指定client的日志级别到debug
logging:
  level:
    com.feign.XXXClient: debug
  • 覆盖默认配置的方式2:配置文件(yml)配置方式
feign:
  client:
    config:
      default:
        loggerLevel: full
        requestInterceptors:
          # 可以用来传递token,跟踪id等需要通过header传递的信息
          - com.frank.interceptor.StorageFeignInterceptor
  • 如果Bean配置方式和配置文件同时存在,配置文件的方式会优先
  • 拦截器、编码和解码的配置也同样支持上面两种方式
眼花缭乱的时间配置
  • 先来个求极限的算法吧!!相信很多:好汉都被难倒了,那么最大值怎么算出来的呢?咚咚咚。。。不卖关子了。最大时间=(ReadTimeout?+ConnectTimeout) * (MaxAutoRetries + 1) * (MaxAutoRetriesNextServer + 1)(?表示0次或者1次),是不是比数据的极限算法简单很多呢!高数各种极限求法眼花缭乱,早就还给老师了,哈哈!注意:ribbon默认的情况下是单台机器重试次数一次,重试一台机器
# ribbon超时时间 
# 每台机器最大重试次数,默认是一次,就是说如果没有配置情况下请求超时会重试一次
storage.ribbon.MaxAutoRetries=2
# 再重试几台机器
storage.ribbon.MaxAutoRetriesNextServer=2
# 连接超时,连接超时可设置较小
storage.ribbon.ConnectTimeout=1000
# 业务处理超时
storage.ribbon.ReadTimeout=2000
# 在所有HTTP Method进行重试,默认配置是false,生产环境如果配置
# 为true需要设置幂等
storage.ribbon.OkToRetryOnAllOperations=true
  • 上述配置的优先级别低于fegin.client.config.default.xxxTimeout的时间配置,这个配置会覆盖ribbon的时间配置,上面ribbon配置的时间和重试参数统统不生效
feign:
  client:
    config:
      # 指定自己的feign的名字,默认default对所有的feign生效
      default:
        connectTimeout: 5000
        readTimeout: 5000
  • 配置超时时间合理的情况下,建议hystix=connectTimeout+readTimeout,生效时间优先级 feign > hystrix > ribbon > http
降级&熔断
  • 降级的配置需要在client来实现
  • 开启配置
feign:
  hystrix:
    enabled: true
hystrix:
  command:
  	# 这里是全局配置,可以针对每个commandKey来配置,也可以通过类#方法
  	# 获取类#方法(args)的快捷方式:Feign.configKey(target.type(), method)
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
  • 降级配置:在@FeignClient增加fallBack配置,fallBack配置类实现client并加入到spring上下文中去,当然也可以实现factory接口
@FeignClient(name = "storage", fallback = StorageClientFallback.class)
protected interface StorageClient{
    
    
    @GettMapping("/fallback")
    String fallback(Integer flag);
}

// 内部类的不用添加Bean上下文注解,如果单独实现需要配置@Component注解
static class StorageClientFallback implements StorageClient{
    
    
    @Override
    public String fallback(Integer flag) {
    
    
        return "fallback";
    }
}
  • 如果在默认的5秒钟内出现20次错误的情况下,断路器会打开,所有请求直接熔断
  • hystrix command 配置command

源码阅读

Feign源码之@EnableFeignClients
  • 加载在启动类的配置给人的第一直觉就是初始化组件用的,没错这里的@EnableFeignClients的功能就是加载Feign的定义,具体定义了什么内容呢?
    • FeignClientsRegistrar类实现了ImportBeanDefinitionRegistrar接口,在方法中注册了Feign的配置以及FeignClient的定义
    • 画外音:很多组件都是通过这种方式来加载的,所以平常在组件开发的过程中也可以通过实现ImportBeanDefinitionRegistrar接口来实现自定义组件的加载
Feign源码之FeignClientFactoryBean
  • 看到XXXFactoryBean对象(画外音:创建spring对象的方式之一,可以想象对象创建的其它方式),可以猜测是通过工厂Bean的方式来创建对象。在Spring上下文初始化的时候,需要加载Client的时候,通过FactoryBean来创建Client对象的

  • 大致流程
    Client主要流程

  • 核心创建方法如下:

public <T> T newInstance(Target<T> target) {
    
    
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    // 方法和方法处理Handler的映射关系
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    for (Method method : target.type().getMethods()) {
    
    
      if (method.getDeclaringClass() == Object.class) {
    
    
        continue;
      } else if (Util.isDefault(method)) {
    
    
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
    
    
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    // 创建InvocationHandler
    InvocationHandler handler = factory.create(target, methodToHandler);
    // 创建动态代理对象
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {
    
    target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    
    
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }

你们的点赞和关注是我创作的最大动力,有什么不足和错误的地方欢迎留言,微信搜索关注小二说码

猜你喜欢

转载自blog.csdn.net/zlypdlx/article/details/113043954