这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战
本系列代码地址:github.com/JoJoTec/spr…
在使用云原生的很多微服务中,比较小规模的可能直接依靠云服务中的负载均衡器进行内部域名与服务映射,通过健康检查接口判断实例健康状态,然后直接使用 OpenFeign 生成对应域名的 Feign Client。Spring Cloud 生态中,对 OpenFeign 进行了封装,其中的 Feign Client 的各个组件,也是做了一定的定制化,可以实现在 OpenFeign Client 中集成服务发现与负载均衡。在此基础上,我们还结合了 Resilience4J 组件,实现了微服务实例级别的线程隔离,微服务方法级别的断路器以及重试。
我们先来分析下 Spring Cloud OpenFeign
Spring Cloud OpenFeign 解析
从 NamedContextFactory 入手
Spring Cloud OpenFeign 的 github 地址:github.com/spring-clou…
首先,根据我们之前分析 spring-cloud-loadbalancer 的流程,我们先从继承 NamedContextFactory
的类入手,这里是 FeignContext
,通过其构造函数,得到其中的默认配置类:
1 | csharp复制代码public FeignContext() { |
从构造方法可以看出,默认的配置类是:FeignClientsConfiguration
。我们接下来详细分析这个配置类中的元素,并与我们之前分析的 OpenFeign 的组件结合起来。
负责解析类元数据的 Contract,与 spring-web 的 HTTP 注解相结合
为了开发人员更好上手使用和理解,最好能实现使用 spring-web 的 HTTP 注解(例如 @RequestMapping
,@GetMapping
等等)去定义 FeignClient 接口。在 FeignClientsConfiguration
中就是这么做的:
FeignClientsConfiguration.java
1 | typescript复制代码@Autowired(required = false) |
其核心提供的 Feign 的 Contract 就是 SpringMvcContract
,SpringMvcContract
主要包含两部分核心逻辑:
- 定义 Feign Client 专用的 Formatter 与 Converter 注册
- 使用 AnnotatedParameterProcessor 来解析 SpringMVC 注解以及我们自定义的注解
定义 Feign Client 专用的 Formatter 与 Converter 注册
首先,Spring 提供了类型转换机制,其中单向的类型转换为实现 Converter 接口;在 web 应用中,我们经常需要将前端传入的字符串类型的数据转换成指定格式或者指定数据类型来满足我们调用需求,同样的,后端开发也需要将返回数据调整成指定格式或者指定类型返回到前端页面(在 Spring Boot 中已经帮我们做了从 json 解析和返回对象转化为 json,但是某些特殊情况下,比如兼容老项目接口,我们还可能使用到),这个是通过实现 Formatter 接口实现。举一个简单的例子:
定义一个类型:
1 | less复制代码@Data |
我们定义可以通过字符串解析出这个类的对象的 Converter,例如 “1,zhx” 就代表 id = 1 并且 name = zhx:
1 | typescript复制代码public class StringToStudentConverter implements Converter<String, Student> { |
然后将这个 Converter 注册:
1 | typescript复制代码@Configuration(proxyBeanMethods = false) |
编写一个测试接口:
1 | less复制代码@RestController |
调用 /test/string-to-student?student=1,zhx
,可以看到返回:
1 | json复制代码{ |
同样的,我们也可以通过 Formatter 实现:
1 | typescript复制代码public class StudentFormatter implements Formatter<Student> { |
然后将这个 Formatter 注册:
1 | typescript复制代码@Configuration(proxyBeanMethods = false) |
Feign 也提供了这个注册机制,为了和 spring-webmvc 的注册机制区分开,使用了 FeignFormatterRegistrar 继承了 FormatterRegistrar 接口。然后通过定义 FormattingConversionService
这个 Bean 实现 Formatter 和 Converter 的注册。例如:
假设我们有另一个微服务需要通过 FeignClient 调用上面这个接口,那么就需要定义一个 FeignFormatterRegistrar 将 Formatter 注册进去:
1 | typescript复制代码@Bean |
之后我们定义 FeignClient:
1 | less复制代码@FeignClient(name = "test-server", contextId = "test-server") |
在调用 get 方法时,会调用 StudentFormatter 的 print 将 Student 对象输出为格式化的字符串,例如 {"id": 1,"name": "zhx"}
会变成 1,zhx
。
AnnotatedParameterProcessor 来解析 SpringMVC 注解以及我们自定义的注解
AnnotatedParameterProcessor
是用来将注解解析成 AnnotatedParameterContext
的 Bean,AnnotatedParameterContext
包含了 Feign 的请求定义,包括例如前面提到的 Feign 的 MethodMetadata
即方法元数据。默认的 AnnotatedParameterProcessor
包括所有 SpringMVC 对于 HTTP 方法定义的注解对应的解析,例如 @RequestParam
注解对应的 RequestParamParameterProcessor
:
RequestParamParameterProcessor.java
1 | scss复制代码public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) { |
我们也可以实现 AnnotatedParameterProcessor
来自定义我们的注解,配合 SpringMVC 的注解一起使用去定义 FeignClient
HTTP 编码解码器,与 spring-boot 中的编码解码器相结合
Spring Cloud 中的任何组件,都是基于 Spring Boot 而实现的。由于 Spring Boot 中已经有了 HTTP 编码解码器,就可以不用单独给 OpenFeign 单独再实现 HTTP 编码解码器了,而是考虑将 OpenFeign 的编码解码器接口用 Spring Boot 的 HTTP 编码解码器实现。
在 FeignClientsConfiguration 中,提供了默认的实现:
1 | typescript复制代码//由于初始化顺序以及 NamedContextFactory 的 Configuration 初始化的原因,这里需要注入 ObjectFactory 而不是直接注入 HttpMessageConverters 防止找不到 Bean |
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer
本文转载自: 掘金