(一)Springcloud-OpenFeign原理之集成改造原生Openfeign与简单使用


文章将会介绍Springcloud-Openfeign对原生Openfeign的改造原因及方式,最后提供简单的使用案例及扩展点分析,需要提前了原生Openfeign和Spring容器的相关原理

1.和原生Openfeign的关系

原生Openfeign指的是io.github.openfeign,前身是Netflix的Feign项目,不是Springcloud体系中的springcloud-openfeign。原生的Openfeign凭借其合理的抽象设计,大大增强了框架的可扩展性,springcloud-openfeign正是基于框架的可扩展性,替换其核心的几个组件,让Feign支持解析SpringMVC注解,且动态代理对象由FactoryBean接口完成构造,进而完美的集成到Spring体系中。

关于原生OpenFeign如何优化HTTP调用流程并抽象对应组件跳至该文章阅读:原生OpenFeign相较于传统HTTP工具的优化和原理

只有了解了原生Openfeign的实现原理及核心组件的作用后看本篇文章才有意义。

2.Springcloud-Openfeign的改造

2.1 改造目标

如果要知道Springcloud-Openfeign对原生的Openfeign做了什么改造,就需要知道Springcloud-Openfeign想要达到的目标是什么。

Springcloud引入改造Openfeign目的有三:

  1. Springcloud体系缺乏一款灵活轻便且扩展性高的RPC框架;
  2. 经过改造后容易集成到Spring容器中;
  3. 引入的RPC框架可以兼容SpringMVC注解以降低RPC的学习成本和使用便利性。

而引入Openfeign刚好满足Springcloud的三个目的:

  1. Openfeign由于抽象出各个功能组件,因此可扩展性和灵活性极高;
  2. Openfeign采用了类似于Mybatis的JDK动态代理,使用接口就可以生成代理对象,轻便且容易集成到Spring容器中管理;
  3. Openfeign使用Contract协议组件去解析接口方法的注解,如果要支持SpringMVC的注解,只需要实现解析SpringMVC注解的组件,并用其替换Openfeign的Contract组件即可实现编写SpringMVC风格的接口去调用远程接口。

对于Ribbon和Hystrix这些组件的搭配使用属于额外的扩展了,和Springcloud-Openfeign的基础改造无关,放到以后再分析。

2.2 改造内容

Springcloud最重要的改造内容有两点:

  1. 便于集成到Spring框架中;
  2. 支持像写SpringMVC接口一样编写调用远程的接口。

2.2.1 集成到Spring

原生Openfeign使用JDK动态代理来代理接口类的方式就决定了Feign接入Spring容器会非常简单,只需要把生成的动态代理对象交给Spring容器管理即可。大致流程图如下:

Feign集成到Spring流程

途中青色的是Spring刷新流程中解析@Configuration配置类的流程,红色的为Springcloud-Openfeign的可插拔引入实现流程,绿色的则是在注入bean时从FactoryBean获取实例的描述。

这一套实现方式对Spring的@Import实现原理和FactroyBean作用有一定了解的话应该比较容易理解Feign的Spring集成改造。在这里简单描述一下上面的流程:

  1. Spring解析@Configuration配置类的流程;
  2. 检测到@EnableFeignClients注解引入了FeignClientsRegistrar;
  3. FeignClientsRegistrar扫描指定目录下被@FeignClient注解的接口类;
  4. 将被扫描到的类使用FeignClientFactoryBean工厂类封装;
  5. 注入或需要使用Feign接口方法时,在工厂类里从Spring容器中取出需要替换的组件,并使用Builder构造器构造JDK动态代理类。

在FeignClientFactoryBean的getObject()方法中,代码的组成方式和构造原生Openfeign代理类的方式流程基本一致,只是构造组件改成了从Spring容器中获取。原生Openfeign的构造使用方式:

public interface TestFeign {
    /**
     * 测试方法
     */
    @RequestLine("GET /test.json")
    String test();
}

public void test() {
    TestFeign testFeign = Feign.builder()
            // .contract(new XXXContract()) 可替换
            // .encoder(new XXXEncoder()) 可替换
            // .decoder(new XXXDecoder()) 可替换
            .client(new OkHttpClient())
            .requestInterceptors(new ArrayList<>())
            .target(TestFeign.class, "localhost:8080");
    // 调用HTTP API像调用方法一样,调用路径为:GET localhost:8080/test.json
    System.out.println(testFeign.test());
}

FeignClientFactoryBean的getObject()方法流程和上述代码流程类似。

2.2.2 替换构造组件

再回顾一下原生Openfeign的几个核心组件:

  • Contract(协议):表示Feign支持的协议,一般而言指的是注解,通过替换不同的Contract以支持解析不同的注解。如果需要支持Spring的MVC注解,则需要替换该组件;
  • Client(客户端):Feign是简化HTTP调用的框架,本身不提供调用HTTP功能,依赖于客户端的具体实现类。如果要使用不同的HTTP工具,替换Client即可,Springcloud-Openfeign支持OkHttp3的Client实现;
  • Encoder(编码器):将传入的参数进行编码获得body、headers或文件流,负责把方法传入的对象转成对应的编码形式。最常见的是把@ReqeustBody转成json串放到body中;
  • Decoder(解码器):在获得响应的内容后,需要用解码器把响应内容转成接口方法返回类型的对象。如把常用的json返回串转成对应字段对象。

由上面一节已得知Spring可以使用FactoryBean来构造Feign的接口动态代理对象,如果要替换组件直接把需要替换的组件放入Spring容器中管理,并在构造时取出对应的组件把默认的组件一一替换,这样就可以达到替换原生Feign的核心组件以支持不同的功能了。示意图如下:

替换核心组件示意图
图中黑色框为原生Feign提供的接口,红色部分为官方默认实现类,绿色部分则是Spring的实现类,把绿色部分的实现类自动注入到Spring容器中,在FactoryBean构造时取出来进行替换,这样就完成了功能替换或扩展。

通过上面两步的优化改造,原生Openfeign就被集成到了Springcloud体系中,成为了Springcloud-Openfeign。

2.3 初步集成使用

接下来看下Springcloud-Openfeign集成原生Openfeign后的简单使用案例:

/** 主函数启动类 */
@EnableFeignClients(basePackages = "xx.xx")
@SpringBootApplication
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class);
    }
}
/** 被Feign注解的接口类 */
@FeignClient(name = "test", url = "localhost:8081/springcloud-test/")
public interface HttpTestFeign {
    /** 需要调用的接口路径,使用的是SpringMVC注解 */
    @GetMapping("/test/t")
    String test();
}
/** 使用的类 */
@Service
public class TestService{
    /** 注入Feign接口类,注入时会使用工厂类构造代理对象 */
    @Autowired
    private HttpTestFeign httpTestFeign;
    /** 调用方法 */
    public String feign() {
        return httpTestFeign.test();
    }
}

yml文件的属性这里就不贴上去了,因为通过这几个配置,我们已经可以正常使用Springcloud-Openfeign来调用远程HTTP API了。

2.4 支持的扩展点

Feign是非常灵活的RPC框架,支持的扩展点也非常多,但作为RPC框架,如果要融入到微服务体系中必须要考虑到的功能有三:

  1. 集成熔断限流;
  2. 流量负载均衡;
  3. 服务发现与注册。

先看Feign主要组件的调用流程:
主要组件调用流程

从主要组件调用流程可以大概分析一下需要支持上面的三个功能大概可以替换哪些组件:

  1. 熔断限流:熔断限流由于触发了,整个流程就可以直接终止,因此肯定适合在起点位置进行处理,这样才能保证熔断限流后不会走多余的流程,提高性能。基于这样的原因熔断限流一般都是替换InvocationHandler,能够从起点终止全流程,避免多余的性能损耗;
  2. 流量负载均衡:需要在发起HTTP请求时进行流量管控,选择性能较好的接收方进行发送。这个功能可以放在Target和Client组件实现,但如果要控制HTTP客户端的相关参数,就只能放到Client组件实现。事实上和Feign搭配使用的Ribbon就是放在Client实现,以便控制HTTP工具的参数;
  3. 服务发现与注册:需要根据生产者和消费者的标识来动态的匹配具体调用地址,以避免开发者频繁维护ip+port。有两种情况:如果只是为了获得调用地址,可以在Spring容器中根据占位符替换,或在Target中根据标识从服务发现注册框架中取;但如果是为了获取调用地址列表做负载均衡,则不推荐集成到Feign中,而应该集成到负载均衡框架中,因为只有负载均衡框架关心服务列表,Feign只需要调用负载均衡框架。事实上Feign的服务发现注册就是使用Ribbon和Eureka集成,由Eureka提供服务列表给Ribbon,Ribbon再根据负载均衡情况进行选择,与而Feign则只需要调用Ribbon即可。

扩展点暂时便分析到这里,为后续分析和Ribbon、Eureka或Sentinel的集成做个铺垫。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值