Feign 是对于微服务调用的封装,基于接口式的开发,并提供服务降级功能,Feign 的负载均衡依赖于 Ribbon

Fegin 的使用

启动类加上 @EnableFeignClients

  1. @SpringBootApplication
  2. @EnableFeignClients
  3. public class AppUserClient {
  4. public static void main(String[] args) {
  5. SpringApplication.run(AppUserClient.class);
  6. }
  7. }

接口使用 @FeignClient

  1. @FeignClient(name = "server-power", fallback = PowerFeignFallback.class)
  2. public interface PowerFeignClient {
  3. @RequestMapping("/power/get/{id}")
  4. R getPower(@PathVariable("id") String id);
  5. }

服务降级

  1. @Component // 一定要加 @Component
  2. public class PowerFeignFallback implements PowerFeignClient {
  3. @Override
  4. public R getPower(String id) {
  5. return R.error("被降级了,暂时不可用");
  6. }
  7. }

Controller 调用

  1. // feign 方式调用
  2. @RequestMapping("/feign/power/get/{id}")
  3. public R getFeignPower(@PathVariable String id) {
  4. System.out.println("被调用了");
  5. return powerFeignClient.getPower(id);
  6. }

Feign 解析

Fegin 的底层实现机制,是基于动态代理,Fegin 要实现接口调用,要把我们写的接口首先进行动态代理加上发送Http 请求的逻辑,然后把生成后的代理类注入到 Spring 容器当中

通过 @EnableFeignClients 注解上的 @Import 注解, FeignClientsRegistrar 类在这个类里面,实现了 Spring 的支持手动注册 Bean 到容器中的逻辑,通过扫描项目(如果手动配置了 clients 参数的话,就不会扫描项目)把所有的 FeignClient 扫描到,并遍历每个 FeignClient,构建成一个 FeignClientFactoryBean 类,FeignClientFactoryBeangetObject 里面会返回当前这个 Bean 的实例,Feign 就是在 getObject 里面添加了动态代理的逻辑

Feign 会从 Ribbon 中获取真实的 IP 地址,来替换微服务名称,并通过 Ribbon 的负载均衡器,来实现负载均衡

  1. @Retention(RetentionPolicy.RUNTIME)
  2. @Target(ElementType.TYPE)
  3. @Documented
  4. @Import(FeignClientsRegistrar.class)
  5. public @interface EnableFeignClients {
  6. }

注册 Feign Client 对象

@EnableFeignClients 导入了一个 FeignClientsRegistrar.class 接口,并实现了 ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware 三个接口

  • ImportBeanDefinitionRegistrar:可以手动注册一个 Bean 到 Spring 容器中
  • ResourceLoaderAware:可以读取 classpath 路径下的文件
  • EnvironmentAware:可以读取 Spring 全局配置信息 ```java class FeignClientsRegistrar

    1. implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    @Override public void registerBeanDefinitions(AnnotationMetadata metadata,

          BeanDefinitionRegistry registry) {
      // 注册默认配置文件
      registerDefaultConfiguration(metadata, registry);
      // 注册 Fegin Client
      registerFeignClients(metadata, registry);
    

    }

    // 注册默认配置文件 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"));
      }
    

    }

    // 注册 Fegin Client public void registerFeignClients(AnnotationMetadata metadata,

          BeanDefinitionRegistry registry) {
      // 扫描器
      ClassPathScanningCandidateComponentProvider scanner = getScanner();
      scanner.setResourceLoader(this.resourceLoader);
    
      Set<String> basePackages;
    
      Map<String, Object> attrs = metadata
              .getAnnotationAttributes(EnableFeignClients.class.getName());
      // 过滤器
      AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
              FeignClient.class);
      // 判断 @EnableFeignClients 是否定义了 clients
      final Class<?>[] clients = attrs == null ? null
              : (Class<?>[]) attrs.get("clients");
      if (clients == null || clients.length == 0) {
          scanner.addIncludeFilter(annotationTypeFilter);
          // 扫描包
          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) {
          Set<BeanDefinition> candidateComponents = scanner
                  .findCandidateComponents(basePackage);
          for (BeanDefinition candidateComponent : candidateComponents) {
              if (candidateComponent instanceof AnnotatedBeanDefinition) {
                  // verify annotated class is an interface
                  AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                  AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                  Assert.isTrue(annotationMetadata.isInterface(),
                          "@FeignClient can only be specified on an interface");
    
                  Map<String, Object> attributes = annotationMetadata
                          .getAnnotationAttributes(
                                  FeignClient.class.getCanonicalName());
                  // 通过注解属性获取微服务名称
                  String name = getClientName(attributes);
                  registerClientConfiguration(registry, name,
                          attributes.get("configuration"));
    
                  // 注册 FeignClient 对象
                  registerFeignClient(registry, annotationMetadata, attributes);
              }
          }
      }
    

    }

    private void registerFeignClient(BeanDefinitionRegistry registry,

          AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
      String className = annotationMetadata.getClassName();
    
    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);
}

}


<a name="BIMp0"></a>
#### FeignClientFactoryBean
```java
class FeignClientFactoryBean
        implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {

    @Override
    public Object getObject() throws Exception {
        return getTarget();
    }

    <T> T getTarget() {
        FeignContext context = this.applicationContext.getBean(FeignContext.class);
        Feign.Builder builder = feign(context);

        if (!StringUtils.hasText(this.url)) {
            if (!this.name.startsWith("http")) {
                this.url = "http://" + this.name;
            }
            else {
                this.url = this.name;
            }
            this.url += cleanPath();
            // 获取负载均衡器
            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));
    }

    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?");
    }

}

返回动态代理类

代理逻辑是由 HystrixTargeter 来实现的

class HystrixTargeter implements Targeter {

    @Override
    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;
        String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
                : factory.getContextId();
        SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
        if (setterFactory != null) {
            builder.setterFactory(setterFactory);
        }
        Class<?> fallback = factory.getFallback();
        if (fallback != void.class) {
            return targetWithFallback(name, context, target, builder, fallback);
        }
        Class<?> fallbackFactory = factory.getFallbackFactory();
        if (fallbackFactory != void.class) {
            return targetWithFallbackFactory(name, context, target, builder,
                    fallbackFactory);
        }

        return feign.target(target);
    }

}


public abstract class Feign {

    public <T> T target(Target<T> target) {
        return this.build().newInstance(target);
    }

    public <T> T newInstance(Target<T> target) {
        Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
        Method[] var5 = target.type().getMethods();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method method = var5[var7];
            if (method.getDeclaringClass() != Object.class) {
                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 handler = this.factory.create(target, methodToHandler);
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
        Iterator var12 = defaultMethodHandlers.iterator();

        while(var12.hasNext()) {
            DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
            defaultMethodHandler.bindTo(proxy);
        }

        return proxy;
    }

}

Feign 调用逻辑

Feign 会创建一个 InvocationHandler handler = this.factory.create(target, methodToHandler); ,这里的实现类是 FeignInvocationHandler 里面由他的 invoke 方法

static class FeignInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("equals".equals(method.getName())) {
        try {
          Object otherHandler =
              args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
          return equals(otherHandler);
        } catch (IllegalArgumentException e) {
          return false;
        }
      } else if ("hashCode".equals(method.getName())) {
        return hashCode();
      } else if ("toString".equals(method.getName())) {
        return toString();
      }

      return dispatch.get(method).invoke(args);
    }

}
final class SynchronousMethodHandler implements MethodHandler {

    @Override
     public Object invoke(Object[] argv) throws Throwable {
        RequestTemplate template = buildTemplateFromArgs.create(argv);
        Options options = findOptions(argv);
        Retryer retryer = this.retryer.clone();
        while (true) {
            try {
                return executeAndDecode(template, options);
            } catch (RetryableException e) {
                try {
                    retryer.continueOrPropagate(e);
                } catch (RetryableException th) {
                    Throwable cause = th.getCause();
                    if (propagationPolicy == UNWRAP && cause != null) {
                        throw cause;
                    } else {
                        throw th;
                    }
                }
                if (logLevel != Logger.Level.NONE) {
                    logger.logRetry(metadata.configKey(), logLevel);
                }
                continue;
            }
          }
    }

    Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
        Request request = targetRequest(template);

        if (logLevel != Logger.Level.NONE) {
          logger.logRequest(metadata.configKey(), logLevel, request);
        }

        Response response;
        long start = System.nanoTime();
        try {
          response = client.execute(request, options);
        } catch (IOException e) {
          if (logLevel != Logger.Level.NONE) {
            logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
          }
          throw errorExecuting(request, e);
        }
        long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

        boolean shouldClose = true;
        try {
          if (logLevel != Logger.Level.NONE) {
            response =
                logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);
          }
          if (Response.class == metadata.returnType()) {
            if (response.body() == null) {
              return response;
            }
            if (response.body().length() == null ||
                response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {
              shouldClose = false;
              return response;
            }
            // Ensure the response body is disconnected
            byte[] bodyData = Util.toByteArray(response.body().asInputStream());
            return response.toBuilder().body(bodyData).build();
          }
          if (response.status() >= 200 && response.status() < 300) {
            if (void.class == metadata.returnType()) {
              return null;
            } else {
              Object result = decode(response);
              shouldClose = closeAfterDecode;
              return result;
            }
          } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
            Object result = decode(response);
            shouldClose = closeAfterDecode;
            return result;
          } else {
            throw errorDecoder.decode(metadata.configKey(), response);
          }
        } catch (IOException e) {
          if (logLevel != Logger.Level.NONE) {
            logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);
          }
          throw errorReading(request, response, e);
        } finally {
          if (shouldClose) {
            ensureClosed(response.body());
          }
        }
    }

}

执行动态代理

public class LoadBalancerFeignClient implements Client {

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        try {
            URI asUri = URI.create(request.url());
            String clientName = asUri.getHost();
            URI uriWithoutHost = cleanUrl(request.url(), clientName);
            FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
                    this.delegate, request, uriWithoutHost);

            IClientConfig requestConfig = getClientConfig(options, clientName);
            return lbClient(clientName)
                    .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
        }
        catch (ClientException e) {
            IOException io = findIOException(e);
            if (io != null) {
                throw io;
            }
            throw new RuntimeException(e);
        }
    }
}
public abstract class AbstractLoadBalancerAwareClient<S extends ClientRequest, T extends IResponse> extends LoadBalancerContext implements IClient<S, T>, IClientConfigAware {

    public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
        LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);

        try {
            return command.submit(
                new ServerOperation<T>() {
                    @Override
                    public Observable<T> call(Server server) {
                        URI finalUri = reconstructURIWithServer(server, request.getUri());
                        S requestForServer = (S) request.replaceUri(finalUri);
                        try {
                            return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
                        } 
                        catch (Exception e) {
                            return Observable.error(e);
                        }
                    }
                })
                .toBlocking()
                .single();
        } catch (Exception e) {
            Throwable t = e.getCause();
            if (t instanceof ClientException) {
                throw (ClientException) t;
            } else {
                throw new ClientException(e);
            }
        }
    }    

}
public class FeignLoadBalancer extends
        AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {

    @Override
    public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
            throws IOException {
        Request.Options options;
        if (configOverride != null) {
            RibbonProperties override = RibbonProperties.from(configOverride);
            options = new Request.Options(override.connectTimeout(this.connectTimeout),
                    override.readTimeout(this.readTimeout));
        }
        else {
            options = new Request.Options(this.connectTimeout, this.readTimeout);
        }
        // 完成方法调用
        Response response = request.client().execute(request.toRequest(), options);
        return new RibbonResponse(request.getUri(), response);
    }

}