Openfeign源码浅析

文章目录

Openfeign源码浅析

Openfeign用途

  • Openfeing是一个声明式的一个http客户端,我们只需要在接口上加个@FeignClient注解就可以用于发起http请求。

Openfeign特性

  • 能够结合注册中心通过service服务发现负责均衡的方式发起调用,也能够指定url直接发起调用(如果url是个k8s的svc服务地址k8s会自动负载均衡)
  • 配置能够支持contextId维度隔离(可理解为接口维度),也就是说可以在服务接口维度支持请求超时时间、重试次数、编解码配置、日志级别等隔离,通过NamedContextFactory来实现
  • 能够方法与第三方的高可用件实现请求限流、熔断、降级等,比如与Hystrix、resilience4j、sentinel

Openfeign组件

  • Client:发送http请求的客户端
    • Default:默认实现,用于指定url时使用
    • FeignBlockingLoadBalancerClient:用于结合注册中心负载均衡时使用
    • RetryableFeignBlockingLoadBalancerClient:支持重试,用于结合注册中心负载均衡时使用
    • OkHttpClient:需要引入feign-okhttp依赖
  • Targeter:目标接口,用于支持熔断扩展实现
    • DefaultTargeter:默认实现
    • FeignCircuitBreakerTargeter:支持熔断,需要通过spring.cloud.openfeign.circuitbreaker.enabled开启,并且引入熔断组件,比如:spring-cloud-starter-circuitbreaker-resilience4j
  • Contract:用于解析接口上的方法,解析为方法对应MethodMetadata列表[]
    • SpringMvcContract
  • MethodMetadata:方法的元数据,包含returnType、bodyType、RequestTemplate
    • RequestTemplate:@RequestMapping解析的封装在这,最终会转换为Request(url、method、header、body)
  • MethodHandler:每个接口的方法对应的处理器,包含方法的MethodMetadata、目标对象、client
    • SynchronousMethodHandler:
    • AsynchronousMethodHandler:
  • FeignInvocationHandler:每个方法执行的代理方法,jdk动态代理实现的
    • Map<Method, MethodHandler> dispatch维护了方法和对应的方法处理器
  • Encoder:对象转字节数组,一般是通过Content-Type和HttpMessageConverter来进行编码
    • SpringEncoder
    • SpringFormEncoder
  • Decoder:字节数组转对象,一般是通过Content-Type和HttpMessageConverter来进行解码

代理生成流程

  • org.springframework.cloud.openfeign.FeignClientFactoryBean#getObject
  • org.springframework.cloud.openfeign.DefaultTargeter#target
  • feign.Feign.Builder#target(feign.Target)
  • feign.ReflectiveFeign#newInstance
  • feign.ReflectiveFeign.ParseHandlersByName#apply
    • feign.Contract#parseAndValidateMetadata
  • feign.InvocationHandlerFactory#create
    • FeignInvocationHandler:包含一个Map<Method, MethodHandler> methodToHandler和

源码执行流程

  • FeignAutoConfiguration-》openFeign的自动配置
  • EnableFeignClients-》@Import(FeignClientsRegistrar.class)
  • FeignClientsRegistrar.class -》是个ImportBeanDefinitionRegistrar实现,扫描FeignClient注解的接口
  • FeignClientFactoryBean-》BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
  • 接着看FeignClientFactoryBean.getObject
    • getTarget(); 生成目标对象
    • feign(feignClientFactory);构建feign
  • ReflectiveFeign-》
  • FeignInvocationHandler-》

FeignClient代理生成流程

  • feign.ReflectiveFeign#newInstance
    • 通过contract组件把接口上方法解析为方法元数据列表List
    • 再把List转换为Map<String, MethodHandler>nameToHandler映射,key为接口名+方法名+参数类型,value:一般为SynchronousMethodHandler
    • 在把nameToHandler映射转换为Map<Method, MethodHandler> methodToHandler方法 和方法处理器的映射,为后面执行的时候根据方法找到MethodHandler
    • 在根据methodToHandler创建jdk的动态代理调用处理器 FeignInvocationHandler
java 复制代码
public <T> T newInstance(Target<T> target) {
    //通过contract组件把接口上方法解析为方法元数据列表List<MethodMetadata>,并转换为Map<String, MethodHandler>nameToHandler
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
 
    //在把nameToHandler映射转换为Map<Method, MethodHandler> methodToHandler方法 和方法处理器的映射
    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)));
      }
    }
    //根据methodToHandler创建jdk的动态代理调用处理器 FeignInvocationHandler
    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;
  }

FeignClient接口代理执行流程

  • feign.ReflectiveFeign.FeignInvocationHandler#invoke
    • 通过methodToHandler根据方法找到MethodHandler
    • 执行MethodHandler的调用,feign.SynchronousMethodHandler#invoke
  • feign.SynchronousMethodHandler#invoke
java 复制代码
 public Object invoke(Object[] argv) throws Throwable {
    //编码:通过Encoder组件把方法上的参数编码,与springmvc一致也是通过HttpMessageConverter来进行编码,messageConverter.write
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        //执行调用并把响应解码为我们定义的方法返回值
        return executeAndDecode(template, options);
      } catch (RetryableException e) {
        //重试
        retryer.continueOrPropagate(e);
        continue;
      }
    }
  }
 
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    Request request = targetRequest(template);
    Response response;
    try {
        //通过client组件发起http请求
      response = client.execute(request, options);
      response = response.toBuilder()
          .request(request)
          .requestTemplate(template)
          .build();
    } catch (IOException e) {
    }
 
 
    if (decoder != null)
        //通过decoder把响应数据解码我们的对象,即:messageConverter.read
      return decoder.decode(response, metadata.returnType());
  }

Openfeign实现

从FeignClient注解开始

  • 我们只在接口上加个@FeignClient注解就能够发起http请求,那么根据以往的经验应该是把FactoryBean注入到spring容器,然后在调用FactoryBean.getObject方法的时候返回一个代理实例。
  • 顺着@FeignClient我们找到是FeignClientsRegistrar(ImportBeanDefinitionRegistrar实现)把含有@FeignClient的接口注册到spring容器,那么我们看看他代码实现
java 复制代码
//org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
@Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        //注册默认的配置
        registerDefaultConfiguration(metadata, registry);
        //注册FeignClient
        registerFeignClients(metadata, registry);
    }
 
    public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
 
        LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
        Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
        final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
        if (clients == null || clients.length == 0) {
            ClassPathScanningCandidateComponentProvider scanner = getScanner();
            scanner.setResourceLoader(this.resourceLoader);
            //扫描包含FeignClient的类
            scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
            Set<String> basePackages = getBasePackages(metadata);
            for (String basePackage : basePackages) {
                candidateComponents.addAll(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();
 
                //获取FeignClient注解上的属性值
                Map<String, Object> attributes = annotationMetadata
                        .getAnnotationAttributes(FeignClient.class.getCanonicalName());
 
                String name = getClientName(attributes);
                //注册注解configuration的配置
                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();
        Class clazz = ClassUtils.resolveClassName(className, null);
        ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
                ? (ConfigurableBeanFactory) registry : null;
        String contextId = getContextId(beanFactory, attributes);
        String name = getName(attributes);
        //关键点FeignClientFactoryBean,确实是个FactoryBean
        FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
        factoryBean.setBeanFactory(beanFactory);
        factoryBean.setName(name);
        factoryBean.setContextId(contextId);
        factoryBean.setType(clazz);
        factoryBean.setRefreshableClient(isClientRefreshEnabled());
        //把注解属性上的值赋值到FeignClientFactoryBean
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
            factoryBean.setUrl(getUrl(beanFactory, attributes));
            factoryBean.setPath(getPath(beanFactory, attributes));
            factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
            Object fallback = attributes.get("fallback");
            Object fallbackFactory = attributes.get("fallbackFactory");
            return factoryBean.getObject();
        });
 
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
 
         
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

画图总结

相关推荐
X***078815 小时前
从语言演进到工程实践全面解析C++在现代软件开发中的设计思想性能优势与长期生命力
java·开发语言
smileNicky15 小时前
SpringBoot系列之集成Pulsar教程
java·spring boot·后端
Sammyyyyy16 小时前
Rust 1.92.0 发布:Never Type 进一步稳定
java·算法·rust
alonewolf_9916 小时前
深入解析G1与ZGC垃圾收集器:原理、调优与选型指南
java·jvm·算法
小镇学者16 小时前
【c++】C++字符串删除末尾字符的三种实现方法
java·开发语言·c++
rfidunion16 小时前
springboot+VUE+部署(1。新建项目)
java·vue.js·spring boot
小翰子_16 小时前
Spring Boot整合Sharding-JDBC实现日志表按月按周分表实战
java·spring boot·后端
weixin_3993806916 小时前
OA 系统假死问题分析与优化
java·运维
豆沙沙包?16 小时前
2026年--Lc334-2130. 链表最大孪生和(链表转数组)--java版
java·数据结构·链表