侧边栏壁纸
博主头像
流殃博主等级

用微笑面对生活

  • 累计撰写 176 篇文章
  • 累计创建 43 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Feign原理

流殃
2022-03-20 / 0 评论 / 0 点赞 / 262 阅读 / 6,824 字 / 正在检测是否收录...

产生原因

主要就是为了更加快捷和方便的发送http请求,屏蔽了底层的协议,只需要非常简单的操作就可以发起请求了

OpenFeign与Feign的关系

feignspring cloud组件中的一个轻量级restfulhttp服务客户端,简化接口调用,将http调用转为rpc调用,让调用远程接口像调用同进程应用内的接口调用一样简单。

dubborpc远程调用一样,通过动态代理实现接口的调用。feign通过封装包装请求体、发送http请求、获取接口响应结果、序列化响应结果等接口调用动作来简化接口的调用。

openfeign则是spring cloudfeign的基础上支持了spring mvc的注解,如@RequesMapping@GetMapping@PostMapping等。openfeign还实现与Ribbon的整合。

服务提供者只需要提供API接口,而不需要像dubbo那样需要强制使用implements实现接口,使用fegin不要求服务提供者在Controller使用implements关键字实现接口。

架构图

image.png
image.png

使用

注解

@EnableFeignClients

由于是导入了FeignClientsRegistrar这个类,从名字就知道是 feign客户端导入的类

FeignClientsRegistrar

image.png
这个类实现了四个接口

  • ResourceLoaderAware 资源导入
  • ImportBeanDefinitionRegistrar 导入bean
  • BeanClassLoaderAware bean类加载器感知
  • EnvironmentAware 环境感知
  @Override
  public void registerBeanDefinitions(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
      //默认配置注入
    registerDefaultConfiguration(metadata, registry);
    //feignclient注入
    registerFeignClients(metadata, registry);
  }

registerDefaultConfiguration

  private void registerDefaultConfiguration(AnnotationMetadata metadata,
      BeanDefinitionRegistry registry) {
    Map<String, Object> defaultAttrs = metadata
        .getAnnotationAttributes(EnableFeignClients.class.getName(), true);

    if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
      String name;
      if (metadata.hasEnclosingClass()) {
        name = "default." + metadata.getEnclosingClassName();
      }
      else {
        name = "default." + metadata.getClassName();
      }
      registerClientConfiguration(registry, name,
          defaultAttrs.get("defaultConfiguration"));
    }
  }
  1. 获取元数据
  2. 对于元数据为空的两种情况处理
    • 有默认配置并且有的类被被禁用了
    • 有默认并且没有禁用
  3. 将client配置注册进去
  private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
      Object configuration) {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder
        .genericBeanDefinition(FeignClientSpecification.class);
    builder.addConstructorArgValue(name);
    builder.addConstructorArgValue(configuration);
    registry.registerBeanDefinition(
        name + "." + FeignClientSpecification.class.getSimpleName(),
        builder.getBeanDefinition());
  }

FeignClientSpecification 就是clien具体一个类,会有对应的属性,配置

registerClientConfiguration

	private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
			Object configuration) {
//genericBeanDefinition 这个方法才是真正生成一个bean的方法
		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientSpecification.class);
		builder.addConstructorArgValue(name);
		builder.addConstructorArgValue(configuration);
//registerBeanDefinition方法进去就是到了spring注册bean的过程了,所以到此为止
		registry.registerBeanDefinition(
				name + "." + FeignClientSpecification.class.getSimpleName(),
				builder.getBeanDefinition());
	}

FeignInvocationHandler

feign的动态代理是在ReflectiveFeign里面的一个静态内部类中的

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

    @Override
    public boolean equals(Object obj) {
      if (obj instanceof FeignInvocationHandler) {
        FeignInvocationHandler other = (FeignInvocationHandler) obj;
        return target.equals(other.target);
      }
      return false;
    }

    @Override
    public int hashCode() {
      return target.hashCode();
    }

    @Override
    public String toString() {
      return target.toString();
    }
  }

invoke,就是调用的MethodHandler 的invoke的方法

MethodHandler

  interface MethodHandler {

    Object invoke(Object[] argv) throws Throwable;
  }

invoke 两个实现类,DefaultMethodHandler 和SynchronousMethodHandler

DefaultMethodHandler

  @Override
  public Object invoke(Object[] argv) throws Throwable {
    if(handle == null) {
      throw new IllegalStateException("Default method handler invoked before proxy has been bound.");
    }
    return handle.invokeWithArguments(argv);
  }

如果处理器不为空,就走这个方法,invokeWithArguments 是java反射里面的一个方法,所以到此为止

SynchronousMethodHandler

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

executeAndDecode

  Object executeAndDecode(RequestTemplate template) throws Throwable {
    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);
      // 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());
      }
    }
  }

SynchronousMethodHandler的invoke(…)方法,调用了自己的executeAndDecode(…) 请求执行和结果解码方法。该方法的工作步骤:

  1. 通过RequestTemplate 请求模板实例,生成远程URL请求实例 request
  2. 用自己的 feign 客户端client成员,excecute(…) 执行请求,并且获取 response 响应
  3. 对response 响应进行结果解码

Client

这个接口中有一个execute方法,这个方法有两个实现,一个是Default,一个是LoadBalancerFeignClient

这个和我参考中的不一样,应该是版本原因,我不是使用的源码在看到,是直接用依赖看的,我使用的依赖是

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
  • Default就是直接发送了http请求
  • LoadBalancerFeignClient 内部使用了 Ribben 客户端负载均衡技术完成URL请求处理。在原理上,简单的使用了delegate包装代理模式:Ribben负载均衡组件计算出合适的服务端server之后,由内部包装 delegate 代理客户端完成到服务端server的HTTP请求;所封装的 delegate 客户端代理实例的类型,可以是 Client.Default 默认客户端,也可以是 ApacheHttpClient 客户端类或OkHttpClient 高性能客户端类,还可以其他的定制的feign.Client 客户端实现类型。

参考

0

评论区