美文网首页SpringBoot系列
9) Feign服务客户端

9) Feign服务客户端

作者: 涣涣虚心0215 | 来源:发表于2021-03-08 23:57 被阅读0次

服务之间如果需要相互访问,可以使用RestTemplate,也可以使用Feign客户端访问,它会默认使用Ribbon来实现负载均衡。
Feign是一个声明式的伪Http客户端,它使得编写Http客户端变得更加简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,支持可插拔的编码器和解码器,整合了Ribbo和Hystrix以及与Eureka结合实现负载均衡和断路器等效果。

为什么还要Feign

Ribbon已经实现了对RestTemplate请求的拦截,增加了负载均衡的实现,而RestTemplate已经简化了HTTP请求,而只需要提供一些模板代码。
Feign在此基础上做了进一步封装,这样只需要创建一个接口并用注解方式来配置它,即可完成对服务提供方的接口绑定,简化了Ribbon+RestTemplate调用客户端的开发量。

Feign的主要功能
  • 可插拔的HTTP客户端实现
  • 可插拔的编码/解码器
  • 对HTTP请求进行gzip压缩,且可设定压缩的阈值
  • 自带负载均衡实现Ribbon
  • 自带熔断器Hystrix
  • 请求日志详细信息
  • 自带重试处理器
Feign工作原理

Feign是一个伪Java HTTP客户端,它不做任何的请求处理,而只是通过处理注解生成Request模板,从而简化了HTTP API的开发。开发人员只需通过注解方式定制Request API模板。
@FeignClient注解过的接口方法被调用的时候,会通过JDK的动态代理生成具体的RequestTemplate模板对象,然后根据这个RequestTemplate生成HTTP的Request对象,最后该请求被LoadBalanceClient通过Ribbon做到负载均衡。

Feign使用
@FeignClient(value = "user-service", configuration = FeignConfiguration.class)
public interface UserFeignClient {
  
    @GetMapping(value = "/simple/{id}")
    public User findById(@PathVariable("id") Long id); 

    @PostMapping(value = "/user")
    public User postUser(@RequestBody User user);

    // 该请求不会成功,只要参数是复杂对象,即使指定了是GET方法,feign依然会以POST方法进行发送请求。
    @GetMapping(value = "/get-user")
    public User getUser(User user);

    @GetMapping("list-all")
    public List<User> listAll() ; 
}

@Configuration
public class FeignConfiguration {
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}
//利用spring-security获取credentials,并添加到请求头上
@Component
public class UserFeignClientInterceptor implements RequestInterceptor {
  private static final String AUTHORIZATION_HEADER = "Authorization";
  private static final String BEARER_TOKEN_TYPE = "Bearer";
  @Autowired
  private AuthenticationUtil authenticationUtil;

  @Override
  public void apply(RequestTemplate template) {
    SecurityContext securityContext = SecurityContextHolder.getContext();
    Authentication authentication = securityContext.getAuthentication();
    if (authentication == null) {
      //for job
      authentication = authenticationUtil.getAuthentication();
    }

    if (authentication != null) {
      template.header(AUTHORIZATION_HEADER, String.format("%s %s", BEARER_TOKEN_TYPE, authentication.getCredentials()));
    }
  }
}
源码解析

与Ribbon不一样,Feign需要使用@EnableFeignClients来开启feign的功能(Ribbon提供了auto-configuration类),@EnableFeignClients注解主要救赎import了FeignClientsRegistrar。
因为FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar,所以主要关注它的registerBeanDefinitions()方法,这个方法在处理Configuration类的时候会被ConfigurationClassPostProcessor通过ConfigurationClassBeanDefinitionReader调用。

class FeignClientsRegistrar
    implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    。。。省略代码
  @Override
  public void registerBeanDefinitions(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
    //注册默认的Configuration Bean
    registerDefaultConfiguration(metadata, registry);
    //注册FeignClient相关bean
    registerFeignClients(metadata, registry);
  }
  //注册默认Configuration
  private void registerDefaultConfiguration(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
    Map<String, Object> defaultAttrs = metadata
        .getAnnotationAttributes(EnableFeignClients.class.getName(), true);

    if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
      String name;
      if (metadata.hasEnclosingClass()) {
        name = "default." + metadata.getEnclosingClassName();
      }
      else {
        name = "default." + metadata.getClassName();
      }
      registerClientConfiguration(registry, name,
          defaultAttrs.get("defaultConfiguration"));
    }
  }

  //主要代码
  public void registerFeignClients(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
    ClassPathScanningCandidateComponentProvider scanner = getScanner();
    scanner.setResourceLoader(this.resourceLoader);

    Set<String> basePackages;
    //获取EnableFeignClients注解的元数据
    Map<String, Object> attrs = metadata
        .getAnnotationAttributes(EnableFeignClients.class.getName());
    AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
        FeignClient.class);
    final Class<?>[] clients = attrs == null ? null
        : (Class<?>[]) attrs.get("clients");
    if (clients == null || clients.length == 0) {
      //scanner添加exclude filter,指定注解是@FeignClient
      scanner.addIncludeFilter(annotationTypeFilter);
      //获取配置的basepackages
      basePackages = getBasePackages(metadata);
    }
    else {
      final Set<String> clientClasses = new HashSet<>();
      basePackages = new HashSet<>();
      for (Class<?> clazz : clients) {
        basePackages.add(ClassUtils.getPackageName(clazz));
        clientClasses.add(clazz.getCanonicalName());
      }
      AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
        @Override
        protected boolean match(ClassMetadata metadata) {
          String cleaned = metadata.getClassName().replaceAll("\\$", ".");
          return clientClasses.contains(cleaned);
        }
      };
      scanner.addIncludeFilter(
          new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
    }

    for (String basePackage : basePackages) {
      //通过scanner获取basepackag下面满足条件的组件
      Set<BeanDefinition> candidateComponents = scanner
          .findCandidateComponents(basePackage);
      for (BeanDefinition candidateComponent : candidateComponents) {
        //如果是AnnotatedBeanDefinition,进行处理
        if (candidateComponent instanceof AnnotatedBeanDefinition) {
          // verify annotated class is an interface
          AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
          //获取BeanDefinition的元数据
          AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
          Assert.isTrue(annotationMetadata.isInterface(),
              "@FeignClient can only be specified on an interface");
          //获取@FeignClient注解的元数据
          Map<String, Object> attributes = annotationMetadata
              .getAnnotationAttributes(
                  FeignClient.class.getCanonicalName());
          //获取客户端名称
          String name = getClientName(attributes);
          //注册客户端所持有的Configuration配置
          registerClientConfiguration(registry, name,
              attributes.get("configuration"));
          //开始注册Feign客户端
          registerFeignClient(registry, annotationMetadata, attributes);
        }
      }
    }
  }
  //组装Feign客户端BeanDefinition
  private void registerFeignClient(BeanDefinitionRegistry registry,
      AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    //根据@FeignClient注解的元数据组装FeignClient需要的一些property
    String className = annotationMetadata.getClassName();
    //注意这里注册的是FeignClientFactoryBean,是一个FactoryBean
    BeanDefinitionBuilder definition = BeanDefinitionBuilder
        .genericBeanDefinition(FeignClientFactoryBean.class);
    validate(attributes);
    definition.addPropertyValue("url", getUrl(attributes));
    definition.addPropertyValue("path", getPath(attributes));
    String name = getName(attributes);
    definition.addPropertyValue("name", name);
    String contextId = getContextId(attributes);
    definition.addPropertyValue("contextId", contextId);
    definition.addPropertyValue("type", className);
    definition.addPropertyValue("decode404", attributes.get("decode404"));
    definition.addPropertyValue("fallback", attributes.get("fallback"));
    definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

    String alias = contextId + "FeignClient";
    AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

    boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
                                // null

    beanDefinition.setPrimary(primary);

    String qualifier = getQualifier(attributes);
    if (StringUtils.hasText(qualifier)) {
      alias = qualifier;
    }
    
    BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
        new String[] { alias });
    BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
  }
FeignClientFactoryBean
@Override
//factoryBean听过的方法,获取目标对象bean
public Object getObject() throws Exception {
  return getTarget();
}
//getObject()实际调用方法
<T> T getTarget() {
  //从容器中获取FeignContext类型的bean
  FeignContext context = this.applicationContext.getBean(FeignContext.class);
  //根据FeignContext创建Feign.builder对象
  Feign.Builder builder = feign(context);
  //开始拼接URL
  if (!StringUtils.hasText(this.url)) {
    if (!this.name.startsWith("http")) {
      this.url = "http://" + this.name;
    }
    else {
      this.url = this.name;
    }
    this.url += cleanPath();
    //如果是http打头的,直接调用loadBalance()方法
    return (T) loadBalance(builder, context,
        new HardCodedTarget<>(this.type, this.name, this.url));
  }
  if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
    this.url = "http://" + this.url;
  }
  String url = this.url + cleanPath();
  Client client = getOptional(context, Client.class);
  if (client != null) {
    if (client instanceof LoadBalancerFeignClient) {
      // not load balancing because we have a url,
      // but ribbon is on the classpath, so unwrap
      client = ((LoadBalancerFeignClient) client).getDelegate();
    }
    builder.client(client);
  }
  Targeter targeter = get(context, Targeter.class);
  return (T) targeter.target(this, builder, context,
      new HardCodedTarget<>(this.type, this.name, url));
}
//根据FeignContext创建Feign.builder对象
protected Feign.Builder feign(FeignContext context) {
  FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
  Logger logger = loggerFactory.create(this.type);

  // @formatter:off
  //从congtext中获取所需要的instance,最后生成Feign.builder对象
  Feign.Builder builder = get(context, Feign.Builder.class)
      // required values
      .logger(logger)
      .encoder(get(context, Encoder.class))
      .decoder(get(context, Decoder.class))
      .contract(get(context, Contract.class));
  // @formatter:on

  //配置feign
  configureFeign(context, builder);

  return builder;
}
//配置feign
protected void configureFeign(FeignContext context, Feign.Builder builder) {
  //从容器中获取FeignClientProperties对象
  FeignClientProperties properties = this.applicationContext
      .getBean(FeignClientProperties.class);
  if (properties != null) {
    if (properties.isDefaultToProperties()) {
      configureUsingConfiguration(context, builder);
      configureUsingProperties(
          properties.getConfig().get(properties.getDefaultConfig()),
          builder);
      configureUsingProperties(properties.getConfig().get(this.contextId),
          builder);
    }
    else {
      configureUsingProperties(
          properties.getConfig().get(properties.getDefaultConfig()),
          builder);
      configureUsingProperties(properties.getConfig().get(this.contextId),
          builder);
      configureUsingConfiguration(context, builder);
    }
  }
  else {
    configureUsingConfiguration(context, builder);
  }
}
//通过Configuration类配置Feign
protected void configureUsingConfiguration(FeignContext context,
    Feign.Builder builder) {
  //设置Retryer重试机制
  Retryer retryer = getOptional(context, Retryer.class);
  if (retryer != null) {
    builder.retryer(retryer);
  }
  //配置Error解码器
  ErrorDecoder errorDecoder = getOptional(context, ErrorDecoder.class);
  if (errorDecoder != null) {
    builder.errorDecoder(errorDecoder);
  }
  //设置Request.Options
  Request.Options options = getOptional(context, Request.Options.class);
  if (options != null) {
    builder.options(options);
  }
  //配置RequestInterceptor,我们在这里这是Header
  Map<String, RequestInterceptor> requestInterceptors = context
      .getInstances(this.contextId, RequestInterceptor.class);
  if (requestInterceptors != null) {
    builder.requestInterceptors(requestInterceptors.values());
  }
  //设置decode404
  if (this.decode404) {
    builder.decode404();
  }
}
//与上面从Configuration类配置feign类似
protected void configureUsingProperties(
    FeignClientProperties.FeignClientConfiguration config,
    Feign.Builder builder) {
  if (config == null) {
    return;
  }
  //
  if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
    builder.options(new Request.Options(config.getConnectTimeout(),
        config.getReadTimeout()));
  }
  if (config.getRetryer() != null) {
    Retryer retryer = getOrInstantiate(config.getRetryer());
    builder.retryer(retryer);
  }
  if (config.getErrorDecoder() != null) {
    ErrorDecoder errorDecoder = getOrInstantiate(config.getErrorDecoder());
    builder.errorDecoder(errorDecoder);
  }
  if (config.getRequestInterceptors() != null
      && !config.getRequestInterceptors().isEmpty()) {
    // this will add request interceptor to builder, not replace existing
    for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
      RequestInterceptor interceptor = getOrInstantiate(bean);
      builder.requestInterceptor(interceptor);
    }
  }
  if (config.getDecode404() != null) {
    if (config.getDecode404()) {
      builder.decode404();
    }
  }
  if (Objects.nonNull(config.getEncoder())) {
    builder.encoder(getOrInstantiate(config.getEncoder()));
  }
  if (Objects.nonNull(config.getDecoder())) {
    builder.decoder(getOrInstantiate(config.getDecoder()));
  }
  if (Objects.nonNull(config.getContract())) {
    builder.contract(getOrInstantiate(config.getContract()));
  }
}
//通过Ribbon使请求负载均衡
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
    HardCodedTarget<T> target) {
  Client client = getOptional(context, Client.class);
  if (client != null) {
    builder.client(client);
    Targeter targeter = get(context, Targeter.class);
    return targeter.target(this, builder, context, target);
  }

  throw new IllegalStateException(
      "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}
loadBalance与Targeter调用

上面代码中显示FactoryBean获得target的实现,在创建Feign.builder之后,走LoadBalance方法。

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
    HardCodedTarget<T> target) {
    //这里获得LoadBalancerFeignClient
  Client client = getOptional(context, Client.class);
  if (client != null) {
    //将Client放入Feign.builder中
    builder.client(client);
    //获取target,这里是HystrixTarget
    Targeter targeter = get(context, Targeter.class);
    return targeter.target(this, builder, context, target);
  }

  throw new IllegalStateException(
      "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
}

//hystrixTarger内部会获得是否提供fallback
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
    FeignContext context, Target.HardCodedTarget<T> target) {
  if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
    return feign.target(target);
  }
  feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
  SetterFactory setterFactory = getOptional(factory.getName(), context,
      SetterFactory.class);
  if (setterFactory != null) {
    builder.setterFactory(setterFactory);
  }
  Class<?> fallback = factory.getFallback();
  if (fallback != void.class) {
    return targetWithFallback(factory.getName(), context, target, builder,
        fallback);
  }
  Class<?> fallbackFactory = factory.getFallbackFactory();
  if (fallbackFactory != void.class) {
    return targetWithFallbackFactory(factory.getName(), context, target, builder,
        fallbackFactory);
  }

  return feign.target(target);
}
//feign.target()实现
public <T> T target(Target<T> target) {
  return build().newInstance(target);
}
//这段代码就是核心,会创建代理类
public <T> T newInstance(Target<T> target) {
  Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
  Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
  List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
  //根据需要处理的name转化成methodName
  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;
}
//创建InvocationHandler,类型时HystrixInvocationHandler
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
  super.invocationHandlerFactory(new InvocationHandlerFactory() {
    @Override
    public InvocationHandler create(Target target,
                                    Map<Method, MethodHandler> dispatch) {
      return new HystrixInvocationHandler(target, dispatch, setterFactory,
          nullableFallbackFactory);
    }
  });
  super.contract(new HystrixDelegatingContract(contract));
  return super.build();
}
//InvocationHandler的invoke代码,核心实现
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args)
  。。。省略平常代码
  //封装
  HystrixCommand<Object> hystrixCommand =
      new HystrixCommand<Object>(setterMethodMap.get(method)) {
        @Override
        protected Object run() throws Exception {
          try {
            return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
          } catch (Exception e) {
            throw e;
          } catch (Throwable t) {
            throw (Error) t;
          }
        }

        @Override
        protected Object getFallback() {
          if (fallbackFactory == null) {
            return super.getFallback();
          }
          try {
            Object fallback = fallbackFactory.create(getExecutionException());
            Object result = fallbackMethodMap.get(method).invoke(fallback, args);
            if (isReturnsHystrixCommand(method)) {
              return ((HystrixCommand) result).execute();
            } else if (isReturnsObservable(method)) {
              // Create a cold Observable
              return ((Observable) result).toBlocking().first();
            } else if (isReturnsSingle(method)) {
              // Create a cold Observable as a Single
              return ((Single) result).toObservable().toBlocking().first();
            } else if (isReturnsCompletable(method)) {
              ((Completable) result).await();
              return null;
            } else {
              return result;
            }
          } catch (IllegalAccessException e) {
            // shouldn't happen as method is public due to being an interface
            throw new AssertionError(e);
          } catch (InvocationTargetException e) {
            // Exceptions on fallback are tossed by Hystrix
            throw new AssertionError(e.getCause());
          }
        }
      };
  //各种方法调用分派
  if (Util.isDefault(method)) {
    return hystrixCommand.execute();
  } else if (isReturnsHystrixCommand(method)) {
    return hystrixCommand;
  } else if (isReturnsObservable(method)) {
    // Create a cold Observable
    return hystrixCommand.toObservable();
  } else if (isReturnsSingle(method)) {
    // Create a cold Observable as a Single
    return hystrixCommand.toObservable().toSingle();
  } else if (isReturnsCompletable(method)) {
    return hystrixCommand.toObservable().toCompletable();
  }
  return hystrixCommand.execute();
}

相关文章

网友评论

    本文标题:9) Feign服务客户端

    本文链接:https://www.haomeiwen.com/subject/rmreqltx.html