feign学习

前言

Feigin是一个声明式HTTP客户端,简化了远程调用,让编写HTTP客户端变得简单。
使用自定义接口,然后在上面添加注解,感知上就像调用一个接口一样。

github地址

Feign的简单实现

maven依赖

  <!-- feign核心包 -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
            <version>10.4.0</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-jackson</artifactId>
            <version>10.2.0</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>10.2.0</version>
        </dependency>

请求接口


import com.feign.model.User;
import feign.Headers;
import feign.Param;
import feign.RequestLine;

public interface IService {

    @RequestLine("GET /user/info?id={id}")
    @Headers("Content-Type: application/json")
    User userInfo(@Param(value = "id") String id);
}

配置类


import feign.Feign;
import feign.Logger;
import feign.Request;
import feign.Retryer;
import feign.httpclient.ApacheHttpClient;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignService {

    @Bean
    public IService getFeignService() {
        return Feign.builder()
                .client(new ApacheHttpClient())
                .encoder(new JacksonEncoder())
                .decoder(new JacksonDecoder())
                .logger(new Logger.NoOpLogger())
                .logLevel(Logger.Level.FULL)
                .retryer(Retryer.NEVER_RETRY)
                .options(new Request.Options(1000, 3500))
                .target(IService.class, "http://localhost:8080");
    }
}

Controller


import com.feign.model.User;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/info")
    @ResponseBody
    public User info(@RequestParam("id") String id) {

        return User.of().setId(id)
                .setName("xixixi");
    }
}

调用Test


import com.feign.model.User;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.feign.client.IService;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest
@RunWith(SpringRunner.class)
public class Test {


    @Autowired
    private IService iService;

    @org.junit.Test
    public void test() {
        User s = iService.userInfo("001");
        System.out.println(s);
    }

}

从初始化构建实例和HTTP请求两个具体实现进行源码分析

构建实例–生成接口代理对象

在实现上,Feign是一个用于生成目标实例Feign#newInstance()的工厂,这个生成的实例便是接口的代理对象。

初始化构建实例

feign.Feign.Builder.java

  /**
     * 初始化构建实例,参数都可以自定义了builder的时候传进来
     */
    public static class Builder {

        // 请求模板的拦截器,默认为空
        private final List<RequestInterceptor> requestInterceptors = new ArrayList<RequestInterceptor>();
        // feign的日志等级,默认不打印日志
        private feign.Logger.Level logLevel = feign.Logger.Level.NONE;
        // 提取器.判断注解定义是否有效,并将有效信息组装成 List<MethodMetadata>
        private Contract contract = new Contract.Default();
        // http请求客户端.默认使用JDK的HttpURLConnection
        private Client client = new Client.Default(null, null);
        // 重试.默认开启,100ms重试一次,一共重试5次,最长持续1s
        private Retryer retryer = new Retryer.Default();
        // 日志记录器
        private feign.Logger logger = new feign.Logger.NoOpLogger();
        // 编码器.默认值支持String类型的编码.参数没有标注@Param注解时会交给编码器处理
        private Encoder encoder = new Encoder.Default();
        // 解码器.默认只能解码String和字节数组
        private Decoder decoder = new Decoder.Default();
        // 把@QueryMap标注的对象编码为Map查询参数明到值的映射
        private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();
        // 包装异常并往上抛
        private ErrorDecoder errorDecoder = new ErrorDecoder.Default();
        // 连接超时设置.默认10s链接超时,60s读取超时
        private Request.Options options = new Request.Options();
        // 控制反射方法调度,默认实现InvocationHandlerFactory
        private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default();
        // 默认不会解码404
        private boolean decode404;
        private boolean closeAfterDecode = true;
        // 异常传播策略,默认不包装不处理直接抛出
        private ExceptionPropagationPolicy propagationPolicy = NONE;

        ...

        /**
         * 获得所需要代理接口的代理类
         * @param apiType
         * @param url
         * @param <T>
         * @return
         */
        public <T> T target(Class<T> apiType, String url) {
            return target(new Target.HardCodedTarget<T>(apiType, url));
        }
       public <T> T target(Target<T> target) {
      return build().newInstance(target);
    }

        /**
         * 获得ReflectiveFeign的实例
         * @return
         */
        public Feign build() {
            SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
                    new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
                            logLevel, decode404, closeAfterDecode, propagationPolicy);
            ReflectiveFeign.ParseHandlersByName handlersByName =
                    new ReflectiveFeign.ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
                            errorDecoder, synchronousMethodHandlerFactory);
            return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
        }
    }

通过动态代理反射创建接口类实例

feign.ReflectiveFeign
ReflectiveFeign是Feign的唯一实现,实现了Feign#newInstance,创建一个target接口的代理对象。


    public class ReflectiveFeign extends Feign {

        // 提供方法,给接口的每个方法生成一个处理器
        private final ParseHandlersByName targetToHandlersByName;
        // 调度中心
        private final InvocationHandlerFactory factory;
        private final QueryMapEncoder queryMapEncoder;


        /**
         * creates an api binding to the {@code target}. As this invokes reflection, care should be taken
         * to cache the result.
         * 创建到目标的API绑定。由于这将调用反射,因此应该小心缓存结果。
         */
        @Override
        public <T> T newInstance(Target<T> target) {
            // 拿到该接口的所有方法的处理器。解析target的各种注解、方法,放到MethodMetadata里面
            // 这个里面的MethodHandler的实现为SynchronousMethodHandler
            Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
            // 拿到需要处理调用的方法处理器
             // 这个里面的MethodHandler的实现为DefaultMethodHandler
            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)) {
                    DefaultMethodHandler handler = new DefaultMethodHandler(method);
                    defaultMethodHandlers.add(handler);
                    methodToHandler.put(method, handler);
                } else {
                    methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
                }
            }

            // 为该目标接口类型创建一个InvocationHandler
            InvocationHandler handler = factory.create(target, methodToHandler);
            // 为接口创建一个代理对象.target.type()就是请求接口类
            // newProxyInstance的三个参数:
			// loader: 用哪个类加载器去加载代理对象
			// interfaces:动态代理类需要实现的接口
			// h:动态代理方法在执行时,会调用h里面的invoke方法去执行
            T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);

            for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
            // DefaultMethodHandler通过调用接口默认方法代码处理方法
                // 把目标对象绑定到方法句柄(MethodHandle)上。必须为DefaultMethodHandler的给定实例调用一次且仅调用一次
                defaultMethodHandler.bindTo(proxy);
            }
            return proxy;
        }
    }

factory.create()创建方法处理

SynchronousMethodHandler.class


final class SynchronousMethodHandler implements MethodHandler {

  private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L;

    // 方法元信息
    private final MethodMetadata metadata;
    ...
    // 请求拦截器,在target.apply(template),模板->请求的转换前完成拦截,而不是在发送请求前一刻
    // 内置仅有一个实现:BasicAuthRequestInterceptor用于鉴权
    private final feign.RequestTemplate.Factory buildTemplateFromArgs;

  private SynchronousMethodHandler(Target<?> target, Client client, Retryer retryer,
      List<RequestInterceptor> requestInterceptors, Logger logger,
      Logger.Level logLevel, MethodMetadata metadata,
      RequestTemplate.Factory buildTemplateFromArgs, Options options,
      Decoder decoder, ErrorDecoder errorDecoder, boolean decode404,
      boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy) {
    this.target = checkNotNull(target, "target");
    this.client = checkNotNull(client, "client for %s", target);
    this.retryer = checkNotNull(retryer, "retryer for %s", target);
    this.requestInterceptors =
        checkNotNull(requestInterceptors, "requestInterceptors for %s", target);
    this.logger = checkNotNull(logger, "logger for %s", target);
    this.logLevel = checkNotNull(logLevel, "logLevel for %s", target);
    this.metadata = checkNotNull(metadata, "metadata for %s", target);
    this.buildTemplateFromArgs = checkNotNull(buildTemplateFromArgs, "metadata for %s", target);
    this.options = checkNotNull(options, "options for %s", target);
    this.errorDecoder = checkNotNull(errorDecoder, "errorDecoder for %s", target);
    this.decoder = checkNotNull(decoder, "decoder for %s", target);
    this.decode404 = decode404;
    this.closeAfterDecode = closeAfterDecode;
    this.propagationPolicy = propagationPolicy;
  }
	static class Factory {
		...
    	public MethodHandler create(Target<?> target,
                                MethodMetadata md,
                                RequestTemplate.Factory buildTemplateFromArgs,
                                Options options,
                                Decoder decoder,
                                ErrorDecoder errorDecoder) {

					return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
          logLevel, md, buildTemplateFromArgs, options, decoder,
          errorDecoder, decode404, closeAfterDecode, propagationPolicy);
    }
  }
}

方法请求处理

invoke执行

invoke执行时利用ReflectiveFeign反射类实现InvocationHandler的invoke方法。是最外层的请求处理,会拿到dispatch中对一个对应的method执行。

static class FeignInvocationHandler implements InvocationHandler {

    private final Target target;
    private final Map<Method, MethodHandler> dispatch;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   ...

	// 通过放射拿到dispatch的map里面method对应的MethodHandler,执行
      return dispatch.get(method).invoke(args);
    }
}

dispatch.get(method)对应的MethodHandler实现类为SynchronousMethodHandler(同步方法调用处理器)。会构建方法请求模板,执行http请求,并完成解码,如果请求失败,会进行重试处理。


final class SynchronousMethodHandler implements MethodHandler {
   
 
    public Object invoke(Object[] argv) throws Throwable {
   		// 根据方法入参,构建请求模板
        RequestTemplate template = this.buildTemplateFromArgs.create(argv);
        // 获取参数里面第一个Options类型的参数
        Options options = this.findOptions(argv);
        // 克隆一个重试
        Retryer retryer = this.retryer.clone();

        while(true) {
            try {
            	// 执行发送HTTP请求,并且完成解码
                return this.executeAndDecode(template, options);
            } catch (RetryableException var9) {
            	// 重试相关处理
           		...
            }
        }
    }

    Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
    	// 构建方法模板,解析注解,拿到httpMethod、url、headers、body
    	// 里面会执行所有拦截器,完成模板定制
        Request request = this.targetRequest(template);
      	...
        Response response;
        try {
        	// 发送HTTP请求.Feign自己的client是ApacheHttpClient,也可以自己构造
            response = this.client.execute(request, options);
        } catch (IOException var16) {
            ...
            可以做一些错误处理
        }
       
        // 是否需要关闭流
        boolean shouldClose = true;
      
        try {
      		// 对response进行解析
       
        } finally {
        	// 关闭流并且释放与其相关的任何方法
            if (shouldClose) {
                Util.ensureClosed(response.body());
            }
        }
    }
	
	// 构建模板
    Request targetRequest(RequestTemplate template) {
    	// 执行请求拦截器,对模板做一些加工处理
        Iterator var2 = this.requestInterceptors.iterator();
        while(var2.hasNext()) {
            RequestInterceptor interceptor = (RequestInterceptor)var2.next();
            interceptor.apply(template);
        }
        // 执行实现类是HardCodedTarget硬编码目标类
        // 如果请求模版的URL不含有http,也就是说是个相对路径,这里就把Base Url加进去
		// 如果请求模版已经是绝对路径了,那就不管啦
        return this.target.apply(template);
    }

    Object decode(Response response) throws Throwable {
        try {
            return this.decoder.decode(response, this.metadata.returnType());
        } catch (FeignException var3) {
            throw var3;
        } catch (RuntimeException var4) {
            throw new DecodeException(response.status(), var4.getMessage(), response.request(), var4);
        }
    }

    Options findOptions(Object[] argv) {
        return argv != null && argv.length != 0 ? (Options)Stream.of(argv).filter((o) -> {
            return o instanceof Options;
        }).findFirst().orElse(this.options) : this.options;
    }
   
    }
}

发送http请求

ApacheHttpClient是Feign实现的Client类,会发送http请求并处理返回Response。

public final class ApacheHttpClient implements Client {
    

    public Response execute(Request request, Options options) throws IOException {
        HttpUriRequest httpUriRequest;
        try {
        	// 生成HttpUriRequest		
        	//toHttpUriRequest每次都会通过RequestBuilder创建一个新实例,是线程安全的
            httpUriRequest = this.toHttpUriRequest(request, options);
        } catch (URISyntaxException var5) {
            throw new IOException("URL '" + request.url() + "' couldn't be parsed into a URI", var5);
        }
		// 发送http请求
        HttpResponse httpResponse = this.client.execute(httpUriRequest);
        // 返回Response
        return this.toFeignResponse(httpResponse, request);
    }

HttpClient

ApacheHttpClient的默认client是apache的HttpClient的CloseableHttpClient。之后对http的请求也是执行的apache.HttpClient包的实现了。

这就是Feign整个的执行逻辑,处理Feign本身的这些,还用到了代理Proxy还有apache的HttpClient,这两个点也很值得深入研究一下。

参考
享学Feign

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值