• feign架构原理解析


    什么是feign?

    来自官网的解释:Feign makes writing java http clients easier

    在使用feign之前,我们怎么发送请求?

    拿okhttp举例:

        public static void post(String url, HashMap<String, String > paramsMap){
            OkHttpClient mOkHttpClient = new OkHttpClient();
            FormBody.Builder formBodyBuilder = new FormBody.Builder();
            Set<String> keySet = paramsMap.keySet();
            for(String key:keySet) {
                String value = paramsMap.get(key);
                formBodyBuilder.add(key,value);
            }
            FormBody formBody = formBodyBuilder.build();
            Request request = new Request
                    .Builder()
                    .post(formBody)
                    .url(url)
                    .build();
            try (Response response = mOkHttpClient.newCall(request).execute()) {
                System.out.println(response.body().string());
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            HashMap<String,String> paramsMap = new HashMap<String, String>() ;
            paramsMap.put("name","小明");
            paramsMap.put("html","<html>...");
            post("https://10.0.4.147:8015/crcc",paramsMap);
        }

    有了feign之后我们怎么发送请求

    @FeignClient(value = "FooBarService", configuration = FooBarServiceFeignConfiguration.class)
    public interface FooBarService {
        @RequestMapping(value = "/foo", method = RequestMethod.GET)
        String foo(@RequestParam(value = "param1") String param1);
    
        @RequestMapping(value = "/bar", method = RequestMethod.POST)
        String bar(@RequestParam(value = "param1") String param1, @RequestParam(value = "param2") String param2);
    }
    @Autowired
    FooBarService fooBarService;
    public String foo() {
        return fooBarService.foo("rt");
    }

    几行代码就能搞定,很大程度的节省了工作量,而且客户端和服务端关于接口的定义只需要写一次

    具体的利弊我们这里就不做分析,在微服务盛行的现在,服务之间的调用单纯使用http client的场景已经基本不存在 

    spring cloud openfeign的加载过程

    上面的代码为什么接口没有实现类也可以使用,是不是跟mybatis一样使用了代理?

    没错,接口最后都会生成代理实现

    (右键新标签打开可查看大图)

      

     spring cloud openfeign关于代理的生成过程

    (右键新标签打开可看大图)

    feign的REST Client API思想

    JAX-RS标准

    最新的REST接口标准为JAX-RS2.0,但是标准是供参考不能拿来直接吃的,具体还是要通过实现了标准的中间件来进行使用

    JAX-RS2.0 之 REST Client API

    摘自《Java RESTful Web Service实战(第2版)》

     为什么JAX-RS2.0这么去抽象,我们这里暂不深入去思考,先拿来主义

    jersey

    jersey是JAX-RS标准的参考实现,是Java领域中最纯正的REST服务开发框架,例如eureka也是使用jersey来做REST接口和客户端发送请求,详见《服务发现之eureka》 

    jersey 之 REST Client API

    官方文档示例

    ClientConfig clientConfig = new ClientConfig();
    clientConfig.register(MyClientResponseFilter.class);
    clientConfig.register(new AnotherClientFilter());
     
    Client client = ClientBuilder.newClient(clientConfig);
    client.register(ThirdClientFilter.class);
     
    WebTarget webTarget = client.target("http://example.com/rest");
    webTarget.register(FilterForExampleCom.class);
    WebTarget resourceWebTarget = webTarget.path("resource");
    WebTarget helloworldWebTarget = resourceWebTarget.path("helloworld");
    WebTarget helloworldWebTargetWithQueryParam =
            helloworldWebTarget.queryParam("greeting", "Hi World!");
     
    Invocation.Builder invocationBuilder =
            helloworldWebTargetWithQueryParam.request(MediaType.TEXT_PLAIN_TYPE);
    invocationBuilder.header("some-header", "true");
     
    Response response = invocationBuilder.get();
    System.out.println(response.getStatus());
    System.out.println(response.readEntity(String.class));

    feign与JAX-RS2.0

    feign主要是作为客户端发送请求,所以也是参考对照了JAX-RS2.0标准

    feign并不是REST Client,只是参考了REST Client的实现,具体的目标还是为了更简单的实现http client请求

    feign中怎么进行对应呢?

    为什么这么去抽象我们这里也暂不深入研究(更深层的JAX-RS为什么这么抽象还未探明) 

    feign代理的执行流程和关键对象

    代理生成时用到了什么组件、代理执行时用到了什么组件?

    上文我们知道,所有请求最后都交给了MethodHandler来执行,所以我们重点关注MethodHandler即可

    (右键新标签打开可查看大图)

    MethodHandler的关键对象和执行请求的流程

      

    1.RequestTemplate.Factory

    创建RequestTemplate的工厂,包含MethodMetadata和Encoder对象

    其中MethodMetadata是应用初始化时Contract解析@RequestMapping @RequestParam等注解而来的中间数据

    2.Encoder 

    报文压缩gzip等

    3.RequestInterceptor

    为请求附加一些信息,类似spring mvc的interceptor拦截器

    4.Target

    主要是把@FeignClient里的url拼接到RequestTemplate

    5.Options 

    用于请求的参数配置

    6.Decoder 

    解析返回报文,如果返回404,判断decode404==true则解析,否则交给ErrorDecoder解析

    7.ErrorDecoder

    请求错误处理

    8.Logger.Level

    日志等级,包含四种 none basic headers full

    9.Logger

    对应的配置为LoggerFactory,记录日志用

    10.Retryer

    重试,DefaultRetryer默认会重试5次

    11.Client

    真正执行http请求的客户端,可以配置,默认由FeignRibbonClientAutoConfiguration进行配置结合ribbon使用

      

    spring cloud openfeign的配置

    配置的优先级顺序

    (右键新标签打开可查看大图)

     

    properties和spring bean可以配置的内容

    主要还是配置我们上面feign的关键对象,properties和spring bean可配置的项如下

     

    同ribbon一样,spring-cloud-openfeign的配置也是懒加载,每个feignclient都可以有自己个性化的配置,且配置是懒加载的,但是为每个接口生成代理的时候已经去注册和使用了相关的配置,其实懒加载没有用了。

    所以只实现了最终目的:每个feignclient 都可以有自己个性化的配置

    这里的FeignContext跟ribbon的SpringClientFactory同理 

    public class FeignContext extends NamedContextFactory<FeignClientSpecification> {
       public FeignContext() {
          super(FeignClientsConfiguration.class, "feign", "feign.client.name");
       }
    }

    feign与ribbon对接的关键点

     feign与ribbon对接主要还是在Client对象上做文章,将Client替换为继承Ribbon模板的实现类,这样就可以对执行请求前后做一些负载逻辑,详见《负载均衡之ribbon》

  • 相关阅读:
    使用RSA实现客户端和后台的非对称加密
    PHP异步并行网络扩展swoole安装
    rvm is not a function的解决方法
    python中的sort(),sorted()的用法
    RESTful框架中的Mixin中的五个类视图
    RESTful框架的API请求流程
    理解RESTful架构
    Django中的admin管理工具
    一个程序员的自我介绍--英文版
    Django中的认证系统
  • 原文地址:https://www.cnblogs.com/roytian/p/12196790.html
Copyright © 2020-2023  润新知