IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Feign源码分析(without spring) -> 正文阅读

[Java知识库]Feign源码分析(without spring)

https://spring-cloud-wiki.readthedocs.io/zh_CN/latest/pages/feign.html

简介

Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。Feign提供了HTTP请求的模板,通过编写简单的接口和注解,就可以定义好HTTP请求的参数、格式、地址等信息。Feign会完全代理HTTP请求,开发时只需要像调用方法一样调用它就可以完成服务请求及相关处理。
开源地址:https://github.com/OpenFeign/feign。Feign整合了Ribbon负载和Hystrix熔断,可以不再需要显式地使用这两个组件。总体来说,Feign具有如下特性:

  • 可插拔的注解支持,包括Feign注解和JAX-RS注解;
  • 支持可插拔的HTTP编码器和解码器;
  • 支持Hystrix和它的Fallback;
  • 支持Ribbon的负载均衡;
  • 支持HTTP请求和响应的压缩。

Spring Cloud Feign致力于处理客户端与服务器之间的调用需求,简单来说,使用Spring Cloud Feign组件,他本身整合了Ribbon和Hystrix。可设计一套稳定可靠的弹性客户端调用方案,避免整个系统出现雪崩效应。

本文不会涉及Spring Cloud , 仅做Feign源码分析用。

前提

已有站点

已有站点http://localhost:8080/echo/welcome/jim
在这里插入图片描述

maven依赖

   <dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-core</artifactId>
      <version>11.8</version>
    </dependency>
    <dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-slf4j</artifactId>
      <version>11.8</version>
    </dependency>

实践

客户端代码EchoClient

public interface EchoClient {
    /**
     * /echo/welcome/{name}
     */
    @Headers({"Accept:*/*", "Accept-Language:    zh-cn"})
    @RequestLine("GET /welcome/{name}")
    String welcome(URI baseUri, @Param("name") String name);
}

RequestInterceptor

public class Global1RequestInterceptor implements RequestInterceptor {
    private org.slf4j.Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void apply(RequestTemplate template) {
        logger.info("应用于:{}",template.request().url());
    }
}

Capability

public class ClientWatchCapability implements Capability {

    @Override
    public Client enrich(Client client) {
        return new WatchableClient(null, null);
    }

    public static class WatchableClient extends Client.Default {
        private final Logger logger = LoggerFactory.getLogger(this.getClass());

        public WatchableClient(SSLSocketFactory sslContextFactory, HostnameVerifier hostnameVerifier) {
            super(sslContextFactory, hostnameVerifier);
        }

        @Override
        public Response execute(Request request, Request.Options options) throws IOException {
            Stopwatch stopwatch = Stopwatch.createStarted();
            Response response = super.execute(request, options);
            stopwatch.stop(); // optional
            logger.info("time: " + stopwatch); // formatted string like "12.3 ms"
            return response;
        }
    }
}

test-case

@Test
    public  void test01() {
        Feign feign = Feign.builder()
                //SSLSocketFactory:SSLSocket工厂
                //HostnameVerifier:实现主机名验证功能;在握手期间,如果URL的主机名和服务器的标识主机名不匹配,则验证机制可以回调此接口实现程序来确定是否应该允许此连接
                .client(new Client.Default(null, null))
                .encoder(new Encoder.Default())
                .decoder(new Decoder.Default())
                .contract(new Contract.Default()) // 契约: 定义@FeignClient接口中支持的 注解和值的类型
                .options(new Request.Options(100,200)) //定义如超时时间等
                .retryer(Retryer.NEVER_RETRY) //重试策略
                .logLevel(Logger.Level.FULL)
                .errorDecoder(new ErrorDecoder.Default())
                .queryMapEncoder(new BeanQueryMapEncoder())
                .addCapability(new ClientWatchCapability())
//                .decode404()
                //添加拦截器
                .requestInterceptors(Lists.newArrayList(new Global1RequestInterceptor(), new Global2RequestInterceptor()))
                .build();
        EchoClient client = feign.newInstance(Target.EmptyTarget.create(EchoClient.class));
        URI baseUri = URI.create("http://localhost:8080/echo");
        String result = client.welcome(baseUri,"jim");
        System.out.println(result);
    }

执行结果
在这里插入图片描述

Feign属性

public abstract class Feign {
    private Logger.Level logLevel = Logger.Level.NONE;
    private Contract contract = new Contract.Default();
    private Client client = new Client.Default(null, null);
    private Retryer retryer = new Retryer.Default();
    private Logger logger = new NoOpLogger();
    private Encoder encoder = new Encoder.Default();
    private Decoder decoder = new Decoder.Default();
    private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();
    private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
    private Options options = new Options();
    
}

Contract

契约: 定义@FeignClient接口中支持的 注解和值的类型.

public interface Contract {
  List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType);
}
  • Contract.Default: 支持@RequestLine("GET /welcome/{name}")
  • SpringMvcContract:支持@RequestMapping,@GetMapping等注解。

RequestInterceptor

作用类似于SpringMvc重的RequestInterceptor.intercept(),

public interface RequestInterceptor {
  /**
   * Called for every request. 
   * Add data using methods on the supplied {@link RequestTemplate}.
   */
  void apply(RequestTemplate template);
}

Capability

public interface Capability {

	/**
	* 将capabilities拓展能力依次 增强componentToEnrich
	*/
	static <E> E enrich(E componentToEnrich, List<Capability> capabilities) {
	    return capabilities.stream()
	        // invoke each individual capability and feed the result to the next one.
	        // This is equivalent to:
	        // Capability cap1 = ...;
	        // Capability cap2 = ...;
	        // Capability cap2 = ...;
	        // Contract contract = ...;
	        // Contract contract1 = cap1.enrich(contract);
	        // Contract contract2 = cap2.enrich(contract1);
	        // Contract contract3 = cap3.enrich(contract2);
	        // or in a more compact version
	        // Contract enrichedContract = cap3.enrich(cap2.enrich(cap1.enrich(contract)));
	        .reduce(
	            componentToEnrich,
	            (component, capability) -> invoke(component, capability),
	            (component, enrichedComponent) -> enrichedComponent);
	  }
	  
	 static <E> E invoke(E target, Capability capability) {
	    return Arrays.stream(capability.getClass().getMethods())
	        .filter(method -> method.getName().equals("enrich"))
	        .filter(method -> method.getReturnType().isInstance(target))
	        .findFirst()
	        .map(method -> {
	            return (E) method.invoke(capability, target);
	        })
	        .orElse(target);
	  }

  default Client enrich(Client client) {
    return client;
  }
}

源码分析

Feign.builder()..newInstance(Target.EmptyTarget.create(EchoClient.class));

build

public abstract class Feign {
	private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default();
        
    public Feign build() {
          //一系列Capability.enrich()增强....

          //1.
          SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
              new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                  logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);

          //2.    
          ParseHandlersByName handlersByName =
              new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
                  errorDecoder, synchronousMethodHandlerFactory);

          //3.   构建ReflectiveFeign返回
          return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
        }

    }

}

1.SynchronousMethodHandler

final class SynchronousMethodHandler implements MethodHandler {
      static class Factory {
         public MethodHandler create(Target<?> target,
                                MethodMetadata md,
                                RequestTemplate.Factory buildTemplateFromArgs,
                                Options options,
                                Decoder decoder,
                                ErrorDecoder errorDecoder) {
          //a. factory创建 SynchronousMethodHandler
          return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
              logLevel, md, buildTemplateFromArgs, options, decoder,
              errorDecoder, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
        }
      }

  @Override
  public Object invoke(Object[] argv) throws Throwable {
    //1.1 使用传递来的方法参数argv来创建请求模板RequestTemplate。 --- 见BuildTemplateByResolvingArgs
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    //1.2 查找参数中是否存在Options: 用来定义如连接超时等配置
    Options options = findOptions(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
     //重试---略
     //1.3 执行
     return executeAndDecode(template, options);
    }
  }
	Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
        //1.4
        Request request = targetRequest(template);

        //略....

        //1.5 调用client.execute() --- 内部调用 HttpURLConnection执行http请求
        Response response = client.execute(request, options);
        return decoder.decode(response, metadata.returnType());
    }
	
  //1.4  
  Request targetRequest(RequestTemplate template) {
  	//for-each 执行RequestInterceptor.apply(RequestTemplate)
    for (RequestInterceptor interceptor : requestInterceptors) {
      interceptor.apply(template);
    }
	//将RequestTemplate转换为实际的Request对象
    return target.apply(template);
  }
}
BuildTemplateByResolvingArgs
private static class BuildTemplateByResolvingArgs implements RequestTemplate.Factory {
    protected final MethodMetadata metadata;
    protected final Target<?> target;

   RequestTemplate mutable = RequestTemplate.from(metadata.template());
      mutable.feignTarget(target);
      //Method中是否存在参数累心为URI,如果有,将该参数url设置为Target的url
      if (metadata.urlIndex() != null) {
        int urlIndex = metadata.urlIndex();
        mutable.target(String.valueOf(argv[urlIndex]));
      }
	RequestTemplate template = resolve(argv, mutable, varBuilder);
	//略.....
	      return template;

}

}
Target

中间讲解一段关于Target

public interface Target<T> {
  Class<T> type();
  String name();
  String url();
  
  public Request apply(RequestTemplate input);

  public static class HardCodedTarget<T> implements Target<T> {
  }

  public static class EmptyTarget<T> implements Target<T> {
  }
}

new Target.HardCodedTarget(EchoClient.class,"http://localhost:8080/echo");
String result = client.welcome(URI.create(""),"jim");

//等价于
EchoClient client = feign.newInstance(Target.EmptyTarget.create(EchoClient.class));
String result = client.welcome(URI.create("http://localhost:8080/echo"),"jim");

client.execute
@Override
public Response execute(Request request, Options options) throws IOException {
  HttpURLConnection connection = convertAndSend(request, options);
  return convertResponse(connection, request);
}

2.ParseHandlersByName

public class ReflectiveFeign extends Feign {

  static final class ParseHandlersByName {

    public Map<String, MethodHandler> apply(Target target) {
        //检查契约是否合规
      List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
      Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
      for (MethodMetadata md : metadata) {
        BuildTemplateByResolvingArgs buildTemplate;
        if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
          buildTemplate =
              new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
        } else if (md.bodyIndex() != null || md.alwaysEncodeBody()) {
          buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
        } else {
          buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);
        }
        
        //md.configkey == target.type + method.name +args 组成 方法的指纹id
        //为各个方法创建 MethodHandler
        result.put(md.configKey(),factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
      }
      return result;
    }

  }

}

Feign.newInstance(Target)

public class ReflectiveFeign extends Feign {
  private final ParseHandlersByName targetToHandlersByName;
  private final InvocationHandlerFactory factory; // invocationHandlerFactory.Default
  private final QueryMapEncoder queryMapEncoder;

  ReflectiveFeign(ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory,
      QueryMapEncoder queryMapEncoder) {
    this.targetToHandlersByName = targetToHandlersByName;
    this.factory = factory;
    this.queryMapEncoder = queryMapEncoder;
  }
 @Override
  public <T> T newInstance(Target<T> target) {
    //1 为target.targetType所有的方法 创建对应的 MethodHandler
    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 if (Util.isDefault(method)) { //isDefault略...
        DefaultMethodHandler handler = new DefaultMethodHandler(method);
        defaultMethodHandlers.add(handler);
        methodToHandler.put(method, handler);
      } else {
         //2 记录target.targetType中method 和它对应的targetType
        methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
      }
    

    //3  InvocationHandlerFactory.Default创建代理对象
    InvocationHandler handler = factory.create(target, methodToHandler);

    //3 创建实例
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    //5 default略...
    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }
}

InvocationHandlerFactory.Default.create()

public interface InvocationHandlerFactory {

  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);

  interface MethodHandler {
    Object invoke(Object[] argv) throws Throwable;
  }

  static final class Default implements InvocationHandlerFactory {
    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
      return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
    }
  }
}

代理对象: ReflectiveFeign.FeignInvocationHandler

  static class FeignInvocationHandler implements InvocationHandler {
	private final Target target;
    private final Map<Method, MethodHandler> dispatch;

    FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
      this.target = checkNotNull(target, "target");
      this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //找到method对应的 MethodHandler,执行invoke方法;
      return dispatch.get(method).invoke(args);
    }
}

时序图

在这里插入图片描述
执行原理:

  1. 创建EchoClient的代理类Proxy,它的invocationHandler类型是ReflectiveFeign.FeignInvocationHandler
  2. 根据Map<Method, MethodHandler> dispatch 找到method对应的MethodHandler,最终逻辑交由它来执行,此时的methoHandler类型为SynchronousMethodHandler
    3.SynchronousMethodHandler 的操作分为如下步骤
    • 根据ContractTarget.type()解析List<MethodMetadata> metadata,创建空的RequestTemplate
    • 解析调用方法的args[],填充RequestTemplate
    • RequestTemplate转换为Request
    • 使用Client.execute(request,option),它内部使用HttpURLConnection来执行http请求。

生成的代理类
在这里插入图片描述

详细生成方法: https://blog.csdn.net/it_freshman/article/details/78873842

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-26 16:47:16  更:2022-06-26 16:48:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 4:51:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码