• 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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    实践

    客户端代码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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    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);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

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

    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();
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Contract

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

    public interface Contract {
      List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType);
    }
    
    • 1
    • 2
    • 3
    • 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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    源码分析

    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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    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;
    
    }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    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> {
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    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");
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    client.execute
    @Override
    public Response execute(Request request, Options options) throws IOException {
      HttpURLConnection connection = convertAndSend(request, options);
      return convertResponse(connection, request);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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;
        }
    
      }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    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;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    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);
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    代理对象: 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    时序图

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

    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

  • 相关阅读:
    C++ 制作动态链接库.dll及.dll使用
    【0基础前端】CSS-C3总结详细笔记包含代码块从入门到高阶通俗易懂
    Go 语言内置类型全解析:从布尔到字符串的全维度探究
    Java集合框架【二容器(Collection)[ArrayList]】
    蓝桥杯KMP总结
    技术干货|你需要知道的 10 种常见机器学习算法
    【彩色图像处理GUI】各种颜色映射、重新调整大小和更改分辨率、伽玛校正,对比度,反转颜色(Matlab代码实现)
    Nginx平滑升级的详细操作方法
    做强礼品经济韧性与活力,金秋10月第30届深圳礼品展来袭!
    MySQL之误删数据如何处理
  • 原文地址:https://blog.csdn.net/it_freshman/article/details/125398131