OpenFeign原理

版本: OpenFeign 2.2.10.RELEASE

原理

  1. 容器启动时,通过 @EnableFeignCleints 注解启动 Feign Starter 组件
  2. Feign Starter 在项目启动过程中注册全局配置,扫描包下所有的 @FeignClient 接口类,并进行注册 IOC 容器
  3. @FeignClient 接口类被注入时,通过 FeignClientFactoryBean#getObject 返回动态代理类
  4. 接口被调用时被动态代理类逻辑拦截,将 @FeignClient 请求信息通过编码器生成 Request
  5. 通过 Client 携带 Request 调用远端服务返回请求响应(原请求的请求头不会再次携带)
  6. 通过解码器生成 Response 返回客户端,将信息流解析成为接口返回数据

image-20220218090649768

启动原理

  1. 容器启动时,通过 @EnableFeignCleints 注解启动 Feign Starter 组件
  2. Feign Starter 在项目启动过程中注册全局配置,扫描包下所有的 @FeignClient 接口类,并进行注册 IOC 容器
  3. @FeignClient 接口类被注入时,通过 FeignClientFactoryBean#getObject 返回动态代理类
    image-20220218090840108

从 @EnableFeignClients 入手

@EnableFeignClients

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// ***导入注册类(注册类会将当前路径及子路径下的包全部扫描并注入到spring中)
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {...}

FeignClientsRegistrar类

  1. 资源加载,扫描路径下的全部类,获取全部标识了 @FeignClient 的接口
  2. 将注解的属性赋予并由 FactoryBean 通过反射创建bean,并放入 SpringIOC 容器内
class FeignClientsRegistrar
		implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware

ResourceLoaderAware、EnvironmentAware 为 FeignClientsRegistrar 中两个属性 resourceLoader、environment 赋值(Spring做的)

image-20220214104134980

ImportBeanDefinitionRegistrar 负责动态注入 IOC Bean,分别注入 Feign 配置类、FeignClient Bean

image-20220102231758244

class FeignClientsRegistrar
		implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
// 资源加载器,可以加载 classpath 下的所有文件
private ResourceLoader resourceLoader;
// 上下文,可通过该环境获取当前应用配置属性等
private Environment environment;

@Override
public void setEnvironment(Environment environment) {
    this.environment = environment;
}

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

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   // 注册 @EnableFeignClients 提供的自定义配置类中的相关 Bean 实例
    registerDefaultConfiguration(metadata,registry);
    // ***扫描 packge,注册被 @FeignClient 修饰的接口类为 IOC Bean
    registerFeignClients(metadata, registry);
}
    
}

registerFeignClients方法

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

		Set<String> basePackages;
		// 获取到全部标注了 @FeignClient 注解的接口
		Map<String, Object> attrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName());
        // clients 为启动类上的@EnableFeignClients 的属性
		AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
				FeignClient.class);
		final Class<?>[] clients = attrs == null ? null
				: (Class<?>[]) attrs.get("clients");
		if (clients == null || clients.length == 0) { // true
            // 添加所有 @FeignClient 注解的接口
			scanner.addIncludeFilter(annotationTypeFilter);
            // 当前包路径
			basePackages = getBasePackages(metadata);
		}
		else {
			// ...
		}

		for (String basePackage : basePackages) {
            // 拿到所有标注了  @FeignClient 注解
			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的属性赋予BeanDefinitionBuilder(FeignClientFactoryBean)并注入IOC bean
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}

registerFeignClient方法

最终注入的是 FeignClientFactoryBean,实现了FactoryBean,Spring-refresh()-doGetBean() 获取bean的时候会通过FactoryBean#getObject

eg:扫描@Controller注入bean的时候,会先处理该类中@Resource的类,此时在获取的时候会直接调用 getObject方法(因为是FactoryBean)

image-20220216211042254

FeignClientFactoryBean

  1. 实现了InitializingBean ,会先执行afterPropertiesSet()
  2. 重点在 FactoryBean#getObject
class FeignClientFactoryBean
		implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
    	@Override
	public Object getObject() throws Exception {
		return getTarget();
	}
    
    <T> T getTarget() {
        // FeignContext是用于创建和管理Feign Client所依赖的各种类的工厂类。
        // springboot自动装配进去的 \META-INF\spring.factories  FeignAutoConfiguration
		FeignContext context = this.applicationContext.getBean(FeignContext.class);
        // 获取日志工厂、编码、解码等bean
		Feign.Builder builder = feign(context);
	
        // 配置了name,没配置url,走负载均衡
		if (!StringUtils.hasText(this.url)) {
            // 拼接http eg:name= gulimall-third-party 则url=http://gulimall-third-party
			if (!this.name.startsWith("http")) {
				this.url = "http://" + this.name;
			}
			else {
				this.url = this.name;
			}
            // 格式化url(去除掉空格、前后的/)
			this.url += cleanPath();
            // ***执行负载均衡创建逻辑
			return (T) loadBalance(builder, context,
                                   // 将type name、url包装成 HardCodedTarget
					new HardCodedTarget<>(this.type, this.name, this.url));
		}
        // 配置了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));
	}

}

loadBalance()

image-20220216215915116

target()

public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
      FeignContext context, Target.HardCodedTarget<T> target) {
    // 未配置 HystrixFeign,直接走的此分支
   if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
      return feign.target(target);
   }
    //**其他代码
}

public <T> T target(Target<T> target) {
    // ***newInstance创建代理类
    return build().newInstance(target);
}

// 包装为ReflectiveFeign
public Feign build() {
      SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
          new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
              logLevel, decode404, closeAfterDecode, propagationPolicy);
      ParseHandlersByName handlersByName =
          new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
              errorDecoder, synchronousMethodHandlerFactory);
      return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}

newInstance()

反射创建动态代理类

@Override
public <T> T newInstance(Target<T> target) {
    // 将@FeignClient 接口中的方法全部封装成方法句柄(MethodHandler轻量级反射)
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    // 存放default方法的
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    // 遍历全部方法,根据类型添加到不同的 MethodHandlers中
    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 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;
}

image-20220216222448159

执行流程

真正调用的时候,会执行反射的invoke方法 ReflectiveFeign#FeignInvocationHandler#invoke

image-20220216224140579

  1. 如果不是equals、hashCode、toString方法,则会交由Map<Method, MethodHandler> dispatch.get().invoke()来反射执行对应的方法句柄

    image-20220217223510304

  2. 将接口的注解信息封装为 RequestTemplate(包含encode请求信息、请求方式、请求地址等)

    image-20220216224605739

  3. 失败重试策略

  4. client执行http请求(此时请求头会全部丢失),按照对应的配置去解码响应数据

    image-20220217225128998

  @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 {
          // ***真正的去请求,封装request和解析response
        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;
      }
    }
  }

executeAndDecode()

  1. 构建真正的request

    image-20220216225115143

  2. client(根据配置的feign的client)执行请求,执行时会将name转换为 ip + port

    image-20220216230116266

  3. 解析请求

  4. 状态码判断以及编码解码

  Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
      // 执行所有feign请求过滤器
    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());
      }
    }
  }

targetRequest()

执行过滤器逻辑

Request targetRequest(RequestTemplate template) {
  for (RequestInterceptor interceptor : requestInterceptors) {
    interceptor.apply(template);
  }
  return target.apply(template);
}

巨人的肩膀

https://zhuanlan.zhihu.com/p/346273428

https://www.cnblogs.com/crazymakercircle/p/11965726.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值