Spring Cloud Feign原理详解

目录
  • Feign的大体机制
  • @EnableFeignClients 和 @FeignClient 注解
    • registerDefaultConfiguration方法
    • registerFeignClients方法
  • feign客户端的动态代理

Feign 主要是帮助我们方便进行rest api服务间的调用,其大体实现思路就我们通过标记注解在一个接口类上(注解上将包含要调用的接口信息),之后在调用时根据注解信息组装好请求信息,接下来基于ribbon这些负载均衡器来生成真实的服务地址,最后将请求发送出去;之后将接收到的结果反序列化为相关的Java对象供我们直接使用。 下面我们走进Spring Cloud对feign封装的源码中去了解其主要实现机制。

Feign的大体机制

通过在启动类上标记 @EnableFeignClients 注解来开启feign的功能,服务启动后会扫描 @FeignClient 注解标记的接口,然后根据扫描的注解信息为每个接口类生成feign客户端请求,同时解析接口方法中的Spring MVC的相关注解,通过专门的注解解析器识别这些注解信息,以便后面可以正确的组装请求参数,使用 Ribbon 和 Eureka 获取到请求服务的真实地址等信息,最后使用 http 相关组件进行执行调用。其大致流程图如下:

@EnableFeignClients 和 @FeignClient 注解

在EnableFeignClients 注解类中有一个 @Import(FeignClientsRegistrar.class)的配置

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 引入FeignClientsRegistrar 来扫描@FeignClient注解下的类
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
    ...
}

我们追踪代码进入到FeignClientsRegistrar类中,会发现FeignClientsRegistrar 类实现了ImportBeanDefinitionRegistrar(在spring context 项目中)接口,因此spring boot启动时会调用它的registerBeanDefinitions()方法,该方法中会扫描 EnableFeignClients 和 FeignClient 注解信息并设置相关信息。

/**
 * spring boot 启动时会自动调用 ImportBeanDefinitionRegistrar 入口方法
 */
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata
        , BeanDefinitionRegistry registry) {
    // 读取 @EnableFeignClients 注解中信息
    registerDefaultConfiguration(metadata, registry);
    // 扫描所有@FeignClient注解的类
    registerFeignClients(metadata, registry);
}

registerDefaultConfiguration方法

在registerDefaultConfiguration()方法中会读取@EnableFeignClients注解信息,然后将这些信息注册到一个 BeanDefinitionRegistry 里面去;之后feign的一些默认配置将通过这里注册的信息中取获取。

registerFeignClients方法

  • registerFeignClients()方法会扫描相关包路径(如果EnableFeignClients的basePackages没有配置,默认会直接使用启动类所在的包路径)下所有的@FeiginClient注解的类
  • 然后根据@FeiginClient注解信息向BeanDefinitionRegistry里面注册bean,注意这里设置的bean名称生成规则是使用服务名+FeignClientSpecification.class.getSimpleName(),因此如果对一个服务写多个接口类会发生bean名称重复导致注册失败。所以需要增加一个 allow-bean-definition-overriding: true 的配置。
  • 最后会调用 registerFeignClient() 方法注册feign客户端,这里的bean名称的为当前接口类的类路径。

其流程图如下:

feign客户端的动态代理

上面registerFeignClient()方法中在构建bean的时候,实际构建的是FeignClientFactoryBean。

BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);

FeignClientFactoryBean 类对父类的getObject()方法进行了重写,后面动态代理时使用的就是它来获取feign client的。在这里会根据上面注解配置,同时会读取application.yml配置信息,根据配置来设置feign的相关信息,比如编解码器、注解解析器、请求超时时间等;之后如果没有设置url那么就会和负载均衡器(ribbon)整合。最后会通过反射将接口中相关方法进行解析保存供后面进行jdk代理使用。

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

以上就是Spring Cloud Feign原理详解的详细内容,更多关于Spring Cloud Feign原理的资料请关注我们其它相关文章!

(0)

相关推荐

  • 浅谈SpringCloud feign的http请求组件优化方案

    1 描述 如果我们直接使用SpringCloud Feign进行服务间调用的时候,http组件使用的是JDK的HttpURLConnection,每次请求都会新建一个连接,没有使用线程池复用.具体的可以从源码进行分析 2 源码分析 我们在分析源码很难找到入口,不知道从何开始入手,我们在分析SpringCloud feign的时候可用在配置文件下面我讲一下个人的思路. 1 首先我点击@EnableFeignClients 看一下这个注解在哪个资源路径下 如下图所示: 2 找到服务启动加载的配置文件

  • 基于springcloud异步线程池、高并发请求feign的解决方案

    ScenTaskTestApplication.java package com.test; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; /** * @author scen *

  • SpringCloud Feign的使用简介

    简介 feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service.Spring Cloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端. 在springcloud中不仅可以使用Ribbo进行负载均衡,也可以使用Feign.Feign是在Ribbon的基础上进行了一次改进,采用接口的方式实现负载均衡. 使用 导入依赖 <dependency> <groupId>org.sprin

  • SpringCloud OpenFeign Post请求400错误解决方案

    在微服务开发中SpringCloud全家桶集成了OpenFeign用于服务调用,SpringCloud的OpenFeign使用SpringMVCContract来解析OpenFeign的接口定义. 但是SpringMVCContract的Post接口解析实现有个巨坑,就是如果使用的是@RequestParam传参的Post请求,参数是直接挂在URL上的. 问题发现与分析 最近线上服务器突然经常性出现CPU高负载的预警,经过排查发现日志出来了大量的OpenFeign跨服务调用出现400的错误(HT

  • 详解SpringCloud-OpenFeign组件的使用

    思考: 使用RestTemplate+ribbon已经可以完成服务间的调用,为什么还要使用feign? String restTemplateForObject = restTemplate.getForObject("http://服务名/url?参数" + name, String.class); 存在问题: 1.每次调用服务都需要写这些代码,存在大量的代码冗余 2.服务地址如果修改,维护成本增高 3.使用时不够灵活 说明 https://cloud.spring.io/sprin

  • 完美解决SpringCloud-OpenFeign使用okhttp替换不生效问题

    事发地 原默认的Feign是使用URLConnector进行通信的,当换为okhttp时,直接引入包及配置以下内容根本不生效,还是走原生的. feign: okhttp: enable: true 事件还原 创建项目并引入pom相关的依赖如下: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xml

  • Spring Cloud Feign内部实现代码细节

    1. 概述 Feign用于服务间调用,它的内部实现是一个包含Ribbon(负载均衡)的**JDK-HttpURLConnection(Http)**调用.虽然调用形式是类似于RPC,但是实际调用是Http,这也是为什么Feign被称为伪RPC调用的原因. 内部调用过程如下: 2. 代码细节 1) BaseLoadBalancer.java配置初始化 重点功能: 1. 初始化负载均衡策略 2. 初始化取服务注册列表调度策略 void initWithConfig(IClientConfig cli

  • Spring Cloud Alibaba 使用 Feign+Sentinel 完成熔断的示例

    Feign的使用 Feign也是网飞开发的,SpringCloud 使用 Feign 非常简单,我下边演示一下: 首先 服务消费者这边肯定需要一个对应的依赖: compile("org.springframework.cloud:spring-cloud-starter-openfeign") 需要启用Feign的话,也得在启动类上面加个注解 @EnableFeignClients 然后,创建一个 Feign 的接口,像这样子 package com.skypyb.sc.feign;

  • 解决Spring Cloud Feign 请求时附带请求头的问题

    问题描述 Feign 在请求时是不会将 request 的请求头带着请求的,导致假如 Feign 调用的接口需要请求头的信息,比如当前用户的 token 之类的就获取不到 解决方案 FeignConfiguration 通过实现 Feign 的 RequestInterceptor 将从上下文中获取到的请求头信息循环设置到 Feign 请求头中. /** * feign 配置文件 * 将请求头中的参数,全部作为 feign 请求头参数传递 * @author: linjinp * @create

  • SpringCloud Open feign 使用okhttp 优化详解

    我就废话不多说了,大家还是直接看代码吧~ <!--web 模块 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <!--排除tomcat依赖 --> <exclusion> <artifactId&

  • SpringCloud Feign转发请求头(防止session失效)的解决方案

    微服务开发中经常有这样的需求,公司自定义了通用的请求头,需要在微服务的调用链中转发,比如在请求头中加入了token,或者某个自定义的信息uniqueId,总之就是自定义的一个键值对的东东,A服务调用B服务,B服务调用C服务,这样通用的东西如何让他在一个调用链中不断地传递下去呢?以A服务为例: 方案1 最傻的办法,在程序中获取,调用B的时候再转发,怎么获取在Controller中国通过注解获取,或者通过request对象获取,这个不难,在请求B服务的时候,通过注解将值放进去即可:简代码如下: 获取

随机推荐