【八】Spring Cloud Feign核心原理及源码分析

此文为《Spring Cloud、Nginx高并发核心编程_尼恩 (作者) _机械工业出版社》 关于FEIGN部分的读书笔记,不太清楚的地方又在网上另外找了资料

一、简介

Feign是Spring Cloud 中用于 RPC调用的模块。

Feign的作用

在应用启动的初始化过程中: 

(1)对于每一个RPC远程调用Java接口,Feign根据@FeignClient注解生成本地JDK动态代理实例。

(2)对于Java接口中的每一个RPC远程调用方法,Feign首先根据Spring MVC(如@GetMapping)类型注解生成方法处理器MethodHandler实例,该实例内部包含一个请求模板RequestTemplate实例。

在远程调用REST请求执行的过程中:

(1)Feign使用远程方法调用的实际参数替换掉RequestTemplate模板实例中的参数,生成最终的HTTP请求。

(2)将HTTP请求通过feign.Client客户端实例发送到Provider服务端。

Feign整体运行流程

1.通过应用启动类上的@EnableFeignClients注解开启Feign的装配和远程代理实例创建。

在@EnableFeignClients注解源码中可以看到导入了FeignClientsRegistrar类,该类用于扫描@FeignClient注解过的RPC接口

2.通过对@FeignClient注解RPC接口扫描创建远程调用的动态代理实例。

FeignClientsRegistrar类会进行包扫描,扫描所有包下@FeignClient注解过的接口,创建RPC接口的FactoryBean工厂类实例,并将这些FactoryBean注入Spring IOC容器中。

如果应用某些地方需要注入RPC接口的实例(比如被@Resource引用),Spring就会通过注册的FactoryBean工厂类实例的getObject()方法获取RPC接口的动态代理实例。

在创建RPC接口的动态代理实例时,Feign会为每一个RPC接口创建一个调用处理器InvocationHandler,也会为接口的每一个RPC方法创建一个方法处理器MethodHandler,并且将方法处理器缓存在调用处理器InvocationHandlerdispatch映射成员中。

在创建动态代理实例时,Feign也会通过RPC方法的注解为每一个RPC方法生成一个RequesTemplate请求模板实例,RequestTemplate中包含请求的所有信息,如请求URL、请求类型(如GET)、请求参数等。

3.发生RPC调用时,通过动态代理实例类完成远程Provider的HTTP调用。

当动态代理实例类的方法被调用时,Feign会根据RPC方法的反射实例从调用处理器InvocationHandlerdispatch成员中取得方法处理器MethodHandler,然后由MethodHandler方法处理器开始HTTP请求处理。

MethodHandler会结合实际的调用参数,通过RequesTemplate模板实例生成Request请求实例。最后,将Request请求实例交给feign.Client客户端实例进一步完成HTTP请求处理。

4.在完成远程HTTP调用前需要进行客户端负载均衡的处理。

在Spring Cloud微服务架构中,同一个Provider微服务一般都会运行多个实例,所以说客户端的负载均衡能力其实是必选项,而不是可选项。

生产环境下,Feign必须和Ribbon结合在一起使用,所以方法处理器MethodHandler的客户端client成员必须是具备负载均衡能力的LoadBalancerFeignClient类型,而不是完成HTTP请求提交的ApacheHttpClient等类型。

只有在负载均衡计算出最佳的Provider实例之后,才能开始HTTP请求的提交。

在LoadBalancerFeignClient内部有一个delegate委托成员,其类型可能为feign.client.Default、ApacheHttpClient、OkHttpClient等,最终由该delegate客户端委托成员完成HTTP请求的提交。

Feign远程调用的完整流程

注意:Feign的远程调用是同步的 

二、Feign运作流程的每一个环节详细介绍 

1.启动、扫描、注册

springboot自动装配 

用于测试的服务调用者假定叫一个order服务,它需要RPC调用Item服务。

order服务中有一个RPC远程调用Java接口

@FeignClient(value = "app-item",fallback = ItemServiceFallback.class)
public interface ItemFeignClient {
    @RequestMapping(value = "/item/{id}", method = RequestMethod.GET)
    Item queryItemById(@PathVariable("id") Long id);

    @RequestMapping(value = "/item/hello", method = RequestMethod.GET)
    String hello(@PathVariable("echo") String echo);
}

order项目的启动类

有一个@EnableFeignClients注解

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableHystrix
public class OrderApp {
    public static void main(String[] args) {
        SpringApplication.run(OrderApp.class, args);
    }
    /**
     * 向Spring容器中定义RestTemplate对象
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
    }

}

@EnableFeignClients注解

它里面@Import了FeignClientsRegistrar.class类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

	......省略
}

 FeignClientsRegistrar类

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
		ResourceLoaderAware, EnvironmentAware {

	// patterned after Spring Integration IntegrationComponentScanRegistrar
	// and RibbonClientsConfigurationRegistgrar

	private ResourceLoader resourceLoader;

	private Environment environment;

	public FeignClientsRegistrar() {
	}

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		registerDefaultConfiguration(metadata, registry);
		registerFeignClients(metadata, registry);
	}

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}
 
    ......还有一些内部调用的方法
}

可以看到它 实现了ImportBeanDefinitionRegistrar类。

其中registerBeanDefinitions方法会在spring启动的时候被调用

具体该方法是在什么时候被调用呢?

AbstractApplicationContext.refresh()-------->

AbstractApplicationContext.invokeBeanFactoryPostProcessors()-------->

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()-------->

PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors()-------->

ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()-------->

ConfigurationClassPostProcessor.processConfigBeanDefinitions()-------->

ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()-------->

ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass()-------->

ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars()-------->

FeignClientsRegistrar.registerBeanDefinitions()

 FeignClientsRegistrar.registerBeanDefinitions方法的执行内容

1.扫描所有包下 @FeignClient注解过的接口

比如这里就是扫描ItemFeignClient接口

2.为扫描到的每一个RPC客户端接口注册一个beanDefinition实例(bean的),其中beanClass为FeignClientFactoryBean。有兴趣可以看一下这个类。

3.并将这些FactoryBean注入Spring IOC容器中。

此时已经把@FeignClient(value = "app-item",fallback = ItemServiceFallback.class)中的attributes解析出来了

2.创建代理

前置调用链:

当有地方@Resource注入这个远程调用接口ItemFeignClient时,会进行依赖注入,getBean()实例化这个ItemFeignClient接口

AbstractBeanFactory.getBean()-------->AbstractBeanFactory.doGetBean()-------->AbstractAutowireCapableBeanFactory.getObjectForBeanInstance()-------->

AbstractBeanFactory.getObjectForBeanInstance()-------->FactoryBeanRegistrySupport.getObjectFromFactoryBean()-------->

FactoryBeanRegistrySupport.doGetObjectFromFactoryBean()-------->FeignClientFactoryBean.getObject()

创建代理具体的方法调用链: 

1.当FeignClientFactoryBean.getObject()方法被调用时

其调用的getTarget()方法首先从IOC容器获取配置好的Feign.Builder建造者容器实例

然后通过Feign.Builder.target()方法完成RPC动态代理实例的创建。

然而Feign.Builder的实例是怎么来的呢?

Feign.Builder建造者容器实例在自动配置类FeignClientsConfiguration中完成配置,通过其源码可以看到,配置类的feignBuilder(...)方法通过调用Feign.builder()静态方法创建了一个Feign.Builder建造者容器实例。

2.Feign.Builder.target()实例方法

首先调用内部的build()方法创建一个Feign实例(默认情况下是ReflectiveFeign(反射式Feign)实例

然后通过ReflectiveFeign.newInstance(...)方法创建远程接口最终的JDK动态代理实例。

ReflectiveFeign.newInstance()方法内部创建代理的具体逻辑

源码:

  @Override
  public <T> T newInstance(Target<T> target) {

    //1.方法解析和创建方法处理器MethodHandler
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);

    //2.创建方法反射实例和方法处理器映射
    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)));
      }
    }

    //3.创建一个JDK调用处理器FeignInvocationHandler
    InvocationHandler handler = factory.create(target, methodToHandler);

    //4.创建一个动态代理对象
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);

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

做了几件事: 

1.方法解析和创建方法处理器MethodHandler

调用了ParseHandlersByName.apply()方法,解析RPC接口中的所有RPC方法配置(通过Contract解析),为每个RPC方法创建一个对应的MethodHandler方法处理器。

然后进行方法名称和方法处理器的Key-Value键值映射nameToHandler。

ParseHandlersByName是方法解析器,它是ReflectiveFeign类的一个内部类。

那么ParseHandlersByName是怎么创建方法处理器MethodHandler的呢?

方法解析器ParseHandlersByName创建方法处理器的过程是通过方法处理器工厂类实例factory的create()方法完成的。

而默认的方法处理器工厂类Factory定义在SynchronousMethodHandler类中

这里创建的方法处理器就是同步方法处理器SynchronousMethodHandler的实例。

2.创建方法反射实例和方法处理器映射

通过方法名称和方法处理器的映射nameToHandler创建一个方法反射实例到方法处理器的Key-Value映射methodToHandler,

作为方法远程调用时的分发处理映射实例。

3.创建一个JDK调用处理器FeignInvocationHandler

主要以methodToHandler为参数,创建一个InvocationHandler调用处理器实例。

它的创建也是通过工厂模式完成的。默认的InvocationHandler实例是通过InvocationHandlerFactory工厂类完成的。

4.创建一个动态代理对象。

调用JDK的Proxy.newProxyInstance()方法创建一个动态代理实例,

它的参数有3个:RPC远程接口的类装载器、RPC远程接口的Class实例以及上一步创建的FeignInvocationHandler调用处理器实例。

 流程图

Contract远程调用协议规则类

那么ParseHandlersByName是怎么解析方法及方法配置的呢?

ParseHandlersByName.apply()方法源码

 public Map<String, MethodHandler> apply(Target key) {

       //1.通过Contract协议规则类将远程调用Feign接口中的所有方法配置和注解解析成一个List<MethodMetadata>方法元数据列表
      List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
      Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();

      //2.为每个方法生成对应的RequestTemplate.Factory
      for (MethodMetadata md : metadata) {
        BuildTemplateByResolvingArgs buildTemplate;
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
          buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
        } else if (md.bodyIndex() != null) {
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
        } else {
          buildTemplate = new BuildTemplateByResolvingArgs(md);
        }
        result.put(md.configKey(),
                   factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
      }
      return result;
    }

做了几件事

1.通过Contract协议规则类将远程调用Feign接口中的所有方法配置和注解解析成一个List<MethodMetadata>方法元数据列表

2.为每个方法生成对应的RequestTemplate.Factory

Contract协议规则类与方法解析器、调用处理器的关系图

关于RPC接口的配置解析类,Spring Cloud Feign中有两个协议规则解析类:

一个为Feign默认协议规则解析类(DefaultContract),用于解析使用Feign默认的配置注解比如@RequestLine注解。

另一个为SpringMvcContract协议规则解析类,用于解析使用了Spring MVC规则配置的RPC方法。 

 Feign 默认的协议规范

注解接口Target使用说明
@RequestLine方法上定义HttpMethod 和 UriTemplate. UriTemplate 中使用{} 包裹的表达式,可以通过在方法参数上使用@Param 自动注入
@Param方法参数定义模板变量,模板变量的值可以使用名称的方式使用模板注入解析
@Headers类上或者方法上定义头部模板变量,使用@Param 注解提供参数值的注入。如果该注解添加在接口类上,则所有的请求都会携带对应的Header信息;如果在方法上,则只会添加到对应的方法请求上
@QueryMap方法上定义一个键值对或者 pojo,参数值将会被转换成URL上的 query 字符串上
@HeaderMap方法上定义一个HeaderMap, 与 UrlTemplate 和HeaderTemplate 类型,可以使用@Param 注解提供参数值

3.RPC调用执行流程

1.与默认的调用处理器FeignInvocationHandler相关的RPC执行流程

FeignInvocationHandler是默认的调用处理器,如果没有进行特殊的配置,那么Feign将默认使用此调用处理器。

时序图

 具体流程

(1)通过Spring IOC容器实例完成动态代理实例的装配。

前文讲到,Feign在启动时会为加上了@FeignClient注解的所有远程接口(包括DemoClient接口)创建一个FactoryBean工厂实例,并注册到Spring IOC容器。

然后在uaa-provider的DemoRPCController控制层类中,通过@Resource注解从Spring IOC容器找到FactoryBean工厂实例,通过其getObject()方法获取到动态代理实例,装配给DemoRPCController实例的成员变量demoClient。

在需要进行hello()远程调用时,直接通过demoClient成员变量调用JDK动态代理实例的hello()方法。

(2)执行InvocationHandler调用处理器的invoke(...)方法。

前面讲到,JDK动态代理实例的方法调用过程是通过委托给InvocationHandler调用处理器完成的,故在调用demoClient的hello()方法时,会调用到它的调用处理器FeignInvocationHandler实例的invoke(...)方法。

FeignInvocationHandler实例内部保持了一个远程调用方法反射实例和方法处理器的dispatch映射。

FeignInvocationHandle在它的invoke(...)方法中会根据hello()方法的Java反射实例在dispatch映射对象中找到对应的MethodHandler方法处理器,然后由后
者完成实际的HTTP请求和结果的处理。

(3)执行MethodHandler方法处理器的invoke(...)方法。

feign默认的方法处理器为SynchronousMethodHandler同步调用处理器,它的invoke(...)方法主要通过内部feign。

Client类型的client成员实例完成远程URL请求执行和获取远程结果。

feign.Client客户端有多种类型,不同的类型完成URL请求处理的具体方式不同。

(4)通过feign.Client客户端成员完成远程URL请求执行和获取远程结果。

如果MethodHandler方法处理器client成员实例是默认的feign.Client.Default实现类,就通过JDK自带的HttpURLConnnection类完成远程URL请求执行和获取远程结果。

如果MethodHandler方法处理器实例的client客户端是ApacheHttpClient客户端实现类,就使用ApacheHttpClient开源组件完成远程URL请求执行和获取远程结果。

如果MethodHandler方法处理器实例的client客户端是LoadBalancerFeignClient负载均衡客户端实现类,就使用Ribbon结算出最佳的Provider节点,然后由内部的delegate委托客户端成员去请求Provider服务,完成URL请求处理。

FeignInvocationHandler源码

 static class FeignInvocationHandler implements InvocationHandler {

    private final Target target;
    private final Map<Method, MethodHandler> dispatch;

    FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
      this.target = checkNotNull(target, "target");
      this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
    }

    @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);
    }

  }

其实就做了一件事:从dispatch中得到当前要远程调用的method对应的MethodHandler,然后调用MethodHandler的.invoke方法 

 SynchronousMethodHandler源码

final class SynchronousMethodHandler implements MethodHandler {

  private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L;

  private final MethodMetadata metadata;
  private final Target<?> target;
  private final Client client;
  private final Retryer retryer;
  private final List<RequestInterceptor> requestInterceptors;
  private final Logger logger;
  private final Logger.Level logLevel;
  private final RequestTemplate.Factory buildTemplateFromArgs;
  private final Options options;
  private final Decoder decoder;
  private final ErrorDecoder errorDecoder;
  private final boolean decode404;

  @Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        return executeAndDecode(template);
      } catch (RetryableException e) {
        retryer.continueOrPropagate(e);
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

}

做了几件事:

1.使用该方法对应的RequestTemplate.Factory创建RequestTemplate

2.调用executeAndDecode方法执行远程调用请求并且对response解码

如果捕获到可重试的异常,则根据Retryer(Feign 内置的重试器)的重试规则重试

这里可以看到 SynchronousMethodHandler的invoke方法中是有retryer重试机制的。

重试器有如下几个控制参数:

重试参数 说明默认值
period初始重试时间间隔,当请求失败后,重试器将会暂停 初始时间间隔(线程 sleep 的方式)后再开始,避免强刷请求,浪费性能 100ms
maxPeriod    当请求连续失败时,重试的时间间隔将按照:long interval = (long) (period * Math.pow(1.5, attempt - 1)); 计算,按照等比例方式延长,但是最大间隔时间为 maxPeriod, 设置此值能够避免 重试次数过多的情况下执行周期太长 1000ms
maxAttempts    最大重试次数 5

executeAndDecode(…) 请求执行和结果解码方法源码

Object executeAndDecode(RequestTemplate template) throws Throwable {

    //1.通过 RequestTemplate 请求模板实例,生成远程URL请求实例 request;
    Request request = targetRequest(template);

    //在发送和接收请求的时候,Feign定义了统一的日志门面来输出日志信息
    if (logLevel != Logger.Level.NONE) {
      logger.logRequest(metadata.configKey(), logLevel, request);
    }

    Response response;
    long start = System.nanoTime();
    try {

      //2.用自己的 feign 客户端client成员,excecute(…) 执行请求,并且获取 response 响应;
      response = client.execute(request, options);
      // ensure the request is set. TODO: remove in Feign 10
      response.toBuilder().request(request).build();
    } 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);
        // ensure the request is set. TODO: remove in Feign 10
        response.toBuilder().request(request).build();
      }
      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 {
          return decode(response);
        }
      } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
        return decode(response);
      } 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());
      }
    }
  }

该方法的工作步骤:

(1)targetRequest(RequestTemplate template)

通过 RequestTemplate 请求模板实例,生成远程URL请求实例 request;

这个方法里面会先把List<RequestInterceptor>每个请求连接器的apply方法都执行一遍(看起来是做请求鉴权和encode的)

(2)调用自己的 feign 客户端client成员,excecute(…) 执行请求,并且获取 response 响应;

这里我们细看一下LoadBalancerFeignClient.execute 生产上肯定是用这个,跟ribbon结合做负载的

(3)对response 响应进行结果解码。

日志的输出的四个等级 

在发送和接收请求的时候,Feign定义了统一的日志门面来输出日志信息

级别说明
NONE不做任何记录
BASIC只记录输出Http 方法名称、请求URL、返回状态码和执行时间
HEADERS记录输出Http 方法名称、请求URL、返回状态码和执行时间 和 Header 信息
FULL记录Request 和Response的Header,Body和一些请求元数据

编解码器

Encoder/ Decoder 实现说明
JacksonEncoder,JacksonDecoder基于 Jackson 格式的持久化转换协议
GsonEncoder,GsonDecoder基于Google GSON 格式的持久化转换协议
SaxEncoder,SaxDecoder基于XML 格式的Sax 库持久化转换协议
JAXBEncoder,JAXBDecoder基于XML 格式的JAXB 库持久化转换协议
ResponseEntityEncoder,ResponseEntityDecoderSpring MVC 基于 ResponseEntity< T > 返回格式的转换协议
SpringEncoder,SpringDecoder基于Spring MVC HttpMessageConverters 一套机制实现的转换协议 ,应用于Spring Cloud 体系中

 LoadBalancerFeignClient.execute源码

@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);
		}
	}

做了几件事:

1.组装URI

2.新建FeignLoadBalancer.RibbonRequest,它里面持有真正的HTTPCLIENT,默认是JDK的HTTPCLIENT

3.组装请求配置,即ConnectTimeout、ReadTimeout

4.调用符合负载策略的服务提供方(这里另外一章讲ribbon的时候细说)

2.与Hystrix调用处理器HystrixInvocationHandler相关的RPC执行流程

它带有熔断的、隔离、回退的能力,看一下HystrixInvocationHandler源码中的Invoke方法吧

源码解读

final class HystrixInvocationHandler implements InvocationHandler {

  private final Target<?> target;
  private final Map<Method, MethodHandler> dispatch;
  private final FallbackFactory<?> fallbackFactory; // Nullable
  private final Map<Method, Method> fallbackMethodMap;
  private final Map<Method, Setter> setterMethodMap;

  HystrixInvocationHandler(Target<?> target, Map<Method, MethodHandler> dispatch,
                           SetterFactory setterFactory, FallbackFactory<?> fallbackFactory) {
    this.target = checkNotNull(target, "target");
    this.dispatch = checkNotNull(dispatch, "dispatch");
    this.fallbackFactory = fallbackFactory;
    this.fallbackMethodMap = toFallbackMethod(dispatch);
    this.setterMethodMap = toSetters(setterFactory, target, dispatch.keySet());
  }

  static Map<Method, Method> toFallbackMethod(Map<Method, MethodHandler> dispatch) {
    Map<Method, Method> result = new LinkedHashMap<Method, Method>();
    for (Method method : dispatch.keySet()) {
      method.setAccessible(true);
      result.put(method, method);
    }
    return result;
  }

  static Map<Method, Setter> toSetters(SetterFactory setterFactory, Target<?> target,
                                       Set<Method> methods) {
    Map<Method, Setter> result = new LinkedHashMap<Method, Setter>();
    for (Method method : methods) {
      method.setAccessible(true);
      result.put(method, setterFactory.create(target, method));
    }
    return result;
  }

  @Override
  public Object invoke(final Object proxy, final Method method, final Object[] args)
      throws Throwable {
    // early exit if the invoked method is from java.lang.Object
    // code is the same as ReflectiveFeign.FeignInvocationHandler
    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();
    }

    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 (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();
  }

}

invoke(...)方法会创建hystrixCommand命令实例,

对从dispatch获取的SynchronousMethodHandler实例进行封装,

然后对RPC方法实例method进行判断,判断是直接返回hystrixCommand命令实例,还是立即执行其execute()方法。

默认情况下,都是立即执行它的execute()方法。

如果MethodHandler内的RPC调用出现异常,比如远程server宕机、网络延迟太大而导致请求超时、远程server来不及响应等,hystrixCommand命令器就会调
用失败回调方法getFallback()返回回退结果。

而hystrixCommand的getFallback()方法最终会调用配置在RPC接口@FeignClient注解的fallback属性上的失败回退类中对应的回退方法,执行业务级别的失败回退处理。

时序图

总体来说,使用HystrixInvocationHandler处理器的执行流程与使用FeignInvocationHandler默认的调用处理器相比大致是相同的。

不同的是,HystrixInvocationHandler增加了RPC的保护机制。 至于是怎么增加保护机制的,这个另外一篇讲Hystrix的再细说

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值