OpenFeign 源码解读:动态代理+负载均衡实现

OpenFeign使用@EnableFeignClients开启服务,该注解标有@Import(FeignClientsRegistrar.class),该ImportBeanDefinitionRegistrar会利用扫描路径的方式扫描java文件中带有的@FeignClient(...)的接口,关于这种扫描注解的方式,我仿照写了简化实现:mini-explore

注意,@FeignClient只能注解在接口上。且该接口的所有方法都必须有@GetMapping/@PostMapping注解,此处由feign.Contract接口的实现类SpringMvcContract控制。

OpenFeign通过对注解了@FeignClient的类生成代理该类的BeanDefinition,并默认设置该BeanDefinition.setPrimary(true),所以即使存在重复类型的bean,依然会走OpenFeign的代理。

代理该类的BeanDefinition通过如下方式获取:

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

	private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,Map<String, Object> attributes) {
		...
		// 虽然为FactroyBean接口实现类
		// 但该FactroyBean并未注入容器
		// 只是为了获取代理,完全可以不用实现FactoryBean接口
		FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
		// 支持${}占位符,其中会使用Environment extends PropertyResolver#resolvePlaceholders
		// url也支持占位符 
		String name = getName(attributes);
		definition.addPropertyValue("name", name);
		...
		// 提供Supplier<?>
		// 在spring依赖注入时,会走这个匿名类的Supplier获取代理类对象
		BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
			...
			// *********获取代理对象*********
			return factoryBean.getObject();
		});

		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
		definition.setLazyInit(true);
		// 获取beanDefinition
		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
		// has a default, won't be null
		boolean primary = (Boolean) attributes.get("primary");
		beanDefinition.setPrimary(primary);
		
		// 注册beanDefinition
		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}

由于实现了懒加载,所以并不会在应用启动时就开始OpenFeign生成代理的流程。当用到@FeignClient的接口中的方法时,spring会自动依赖注入动态代理接口的类,此时会使用BeanDefinitionBuilder.genericBeanDefinition(Class,Supplier)方法提供的Supplier<?>来返回对象,该逻辑在AbstractAutowireCapableBeanFactory中,如下:

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {

	protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		...
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
		...
	}

}

最终会到FeignClientFactoryBean#getObject中拿代理对象,动态代理和负载均衡实现的入口。

public class FeignClientFactoryBean
		implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {
	
	@Override
	public Object getObject() {
		return getTarget();
	}

	<T> T getTarget() {
		FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
				: applicationContext.getBean(FeignContext.class);
		// BUILDER
		Feign.Builder builder = feign(context);

		if (!StringUtils.hasText(url)) {
			// 没有提供url,则根据name进行负载均衡
			return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
		}
		
		// 提供了url,则直接连接url
		Client client = getOptional(context, Client.class);
		
		builder.client(client);
		applyBuildCustomizers(context, builder);

		Targeter targeter = get(context, Targeter.class);
		return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
	}

}

Targeter#target最终会调用ReflectiveFeign#newInstance

public class ReflectiveFeign extends Feign {
	
	@Override
  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>();

    for (Method method : target.type().getMethods()) {
      // 是否是类不是接口
      if (method.getDeclaringClass() == Object.class) {
        continue;
       
      } else 
      // 是否是接口的default的方法
	  if (Util.isDefault(method)) {
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      }
      // 加入MethodHandler的实现类SynchronousMethodHandler
      // SynchronousMethodHandler委托Client最终执行远程调用
      else {
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    }
    // JDK动态代理
    InvocationHandler handler = factory.create(target, methodToHandler);
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    ...
    return proxy;
  }

}

MethodHandler的实现类SynchronousMethodHandler委托给Client#execute接口实现类最终执行远程调用,Client接口实现类中可以实现负载均衡,注意OpenFeign的负载均衡需要引入spring-cloud-starter-loadbalancer依赖。
在这里插入图片描述
上面的LoadBalanceClient用于代理一个Client做负载均衡和重试,最终调用的被代理的Client实现了Http远程调用的功能,而Http远程调用工具有多种,OpenFeign提供了4种Http调用的Client:

  • Client.Default
  • ApacheHttpClient
  • ApacheHttp5Client
  • OkHttpClient

所以上面的FeignBlockingLoadBalancerClientRetryableFeignBlockingLoadBalancerClient可以代理以上的4种,共有4*2=8种配置。

if Spring Cloud LoadBalancer is in the classpath, FeignBlockingLoadBalancerClient is used. If none of them is in the classpath, the default feign client is used

同时,Feign也提供了重试操作,具体的调用多层代理过程如下图(实现类方便举例,可配置更改):
在这里插入图片描述
关于openFeign中的Retryer重试,可在@FeignClient中设置configuration参数,但是在企业中,OpenFeign的接口通常定义为二方包供其他服务调用,不进行复杂的设置,至于OpenFeign调用的重试机制可整合spring-retry实现。

OpenFeign需要依赖loadbalancerorg.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory实现负载均衡(OpenFeign老版本通过Ribbon实现负载均衡)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

T.Y.Bao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值