原创 吴就业 132 0 2020-07-07
本文为博主原创文章,未经博主允许不得转载。
本文链接:https://wujiuye.com/article/80da4ca08a5a499482573c25cf71d8d1
作者:吴就业
链接:https://wujiuye.com/article/80da4ca08a5a499482573c25cf71d8d1
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。
本篇文章写于2020年07月07日,从公众号|掘金|CSDN手工同步过来(博客搬家),本篇为原创文章。
经过前面几篇的源码分析,我们对OpenFeign
与Ribbon
也相对熟悉了。
看框架源码的目的就是解决我们的一些疑惑,能够知其然并知其所以然,以及用好框架。
很多时候,我们需要在项目中调用一些第三方接口,例如对接支付宝支付、微信支付,调用支付接口。如果项目中引入了OpenFeign
,那么我们是否可以使用OpenFeign
去调用第三方接口呢?答案肯定是可以的。
虽然调用第三方接口不需要服务发现,所以也不需要使用Ribbon
实现负载均衡,但我们依然可以单独使用OpenFeign
。使用OpenFeign
不仅能够简化调用接口的步骤,也能顺便使用OpenFeign
提供的重试机制,不需要再编写一个HttpUtils
工具类,何乐而不为呢。
本篇内容:
* 配置OpenFeign
使用OkHttp
* OpenFeign
的重试配置
* OpenFeign
的拦截器配置
OpenFeign
通过Client
发送http
请求,而默认的Client
则是使用HttpURLConnection
实现发送http
请求的。
如果你觉得HttpURLConnection
性能不行,你也可以通过自定义Client
将发送http
请求的动作切换到其它你认为更优秀的框架来完成。OpenFeign
也为我们提供了两种选择,一种是使用okhttp
框架,另一种是使用apache
的httpclient
框架。
当OpenFeign
与Ribbon
双剑合璧时,实际向服务提供者发起请求还是由OpenFeign
的Client
完成,所以我们切换Client
是全局有效的。
OpenFeign
为我们使用okhttp
框架提供了Client
接口的实现(feign.okhttp.OkHttpClient
),
并且提供自动配置类FeignAutoConfiguration.OkHttpFeignConfiguration
。
public class FeignAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
protected static class OkHttpFeignConfiguration {
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(okhttp3.OkHttpClient client) {
return new OkHttpClient(client);
}
}
}
该配置类生效的前提条件很多:
feign-okhttp
包,即当前项目的classpath
下存在一个feign.okhttp.OkHttpClient
类,该类实现了Client
接口;<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>11.0</version>
</dependency>
application.yaml
中配置feign.okhttp.enabled
为true
;## 配置feign使用okhttp
feign:
okhttp:
enabled: true
Ribbon
包,即不使用Ribbon
,项目中不存在com.netflix.loadbalancer.ILoadBalancer
这个类;而当项目中使用Ribbon
时,OpenFeign
创建的不再是默认的Default
,也不是OkHttpClient
,而是LoadBalancerFeignClient
。FeignRibbonClientAutoConfiguration
配置类被设置在FeignAutoConfiguration
配置类之前完成自动配置,FeignRibbonClientAutoConfiguration
往容器中注入了LoadBalancerFeignClient
。
@AutoConfigureBefore(FeignAutoConfiguration.class)
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
OkHttpFeignLoadBalancedConfiguration.class,
DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {
}
FeignRibbonClientAutoConfiguration
使用@Import
导入三个自动配置LoadBalancerFeignClient
的配置类,但最终只会有一个被导入,当我们配置feign.okhttp.enabled
为true
,且项目中添加了feign-okhttp
包的依赖时,OkHttpFeignLoadBalancedConfiguration
生效。
@ConditionalOnProperty("feign.okhttp.enabled")
// 导入OkHttpFeignConfiguration自动配置类
@Import(OkHttpFeignConfiguration.class)
class OkHttpFeignLoadBalancedConfiguration {
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory,
// 该okHttpClient是由OkHttpFeignConfiguration自动配置的
okhttp3.OkHttpClient okHttpClient) {
OkHttpClient delegate = new OkHttpClient(okHttpClient);
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
}
}
虽然OpenFeign
与Ribbon
整合使用时,OpenFeign
使用的Client
是LoadBalancerFeignClient
,但这个LoadBalancerFeignClient
只是实现负载均衡的桥梁,实际还是通过委托模式将发送请求的工作委托给其它Client
完成,而这里使用的就是feign.okhttp.OkHttpClient
。
通过前面的源码分析的学习,我们知道,当配置@FeignClient
注解的url
属性时,不会使用LoadBalancerFeignClient
,但我们配置的feign.okhttp.OkHttpClient
依然生效。原因是OpenFeign
不会再创建新的Client
,但会从LoadBalancerFeignClient
对象中取得委托对象feign.okhttp.OkHttpClient
。
我们也可以在FeignClientFactoryBean
的getTarget
方法添加断点调试,以验证使用@FeignClient
注解注释的第三方接口在不走服务发现的情况下,会不会使用feign.okhttp.OkHttpClient
。测试省略…
当我们使用OpenFeign
调用第三方接口时,由于第三方接口不走服务发现,所以我们需要直接在@FeignClient
注解上给出接口的url
。由于在@FeignClient
注解上给出了接口的url
,所以OpenFeign
绝对不会走负载均衡逻辑,而是从LoadBalancerFeignClient
对象中拿到委托对象feign.okhttp.OkHttpClient
创建接口的代理对象,所以最终调用接口发起请求时使用的也是同一个feign.okhttp.OkHttpClient
。
OpenFeign
为每个Client
提供一个环境隔离的AnnotationConfigApplicationContext
,以实现为不同Client
注册不同的配置Bean
,如重试器Retryer
、请求拦截器RequestInterceptor
等。
每个Client
不是说每个使用@FeignClient
注解注释的接口,而是多个name
相同的被@FeignClient
注解注释的接口集合,这组接口都指向同一个服务提供者。
调用内部服务我们可能不会使用OpenFeign
的重试机制,而是使用Ribbon
的重试机制。只有在使用OpenFeign
调用第三方接口时才有必要使用OpenFeign
的重试机制。
复杂的实现可通过获取FeignContext
去为每个Client
注入配置类。有趣的是,FeignContext
是一个NamedContextFactory
,为每个Client
单独提供一个AnnotationConfigApplicationContext
,而Ribbon
的SpringClientFactory
也是一个NamedContextFactory
,也是为每个Client
单独提供一个AnnotationConfigApplicationContext
。
当我们使用@FeignClient
注解注释一个接口时,如果指定了Url
,且Url
是以http
开头的,则不会走Ribbon
负载均衡,根据这一定律,我们就能很明确的知道,什么情况下使用Ribbon
的重试机制,而什么情况下可以使用OpenFeign
的重试机制。
由于每个Client
是环境隔离的,除了可通过获取FeignContext
去为每个Client
注入配置类之外,@FeignClient
注解的configuration
属性也可用来导入配置类。
创建配置类。
public class DefaultFeignRetryConfig {
@Bean
public Retryer retryer() {
return new MyRetry();
}
private static class MyRetry implements Retryer {
/**
* 最大重试次数
*/
private final static int retryerMax = 1;
/**
* 当前重试次数
*/
private int currentRetryCnt = 0;
@Override
public void continueOrPropagate(RetryableException e) {
if (currentRetryCnt > retryerMax) {
throw e;
}
// 连接异常时重试
if (e.getCause() instanceof ConnectException) {
currentRetryCnt++;
return;
}
throw e;
}
@Override
public Retryer clone() {
return new MyRetry();
}
}
}
给@FeignClient
注解的configuration
属性添加该配置类。
@FeignClient(name = "alipay",
path = "/v1",
url = "${fegin-client.alipay-url}",
configuration = {DefaultFeignRetryConfig.class})
OpenFeign
提供请求拦截器以便我们可以实现一些额外操作,例如拦截请求,在请求头添加Basic
授权信息。
与配置OpenFeign
的重试器一样,配置拦截器也可在Client
的配置类中注入多个请求拦截器(RequestInterceptor
),多个请求拦截器名称不能相同。
例如,调用某支付公司的支付接口需要Basic
授权,那么我们需要注册一个BasicAuthRequestInterceptor
为所有请求添加授权头。
public class DefaultFeignRetryConfig {
// 添加授权拦截器
@Bean("basicAuthRequestInterceptor")
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("test", "test123456");
}
}
最后确保已经给@FeignClient
注解的configuration
属性添加该配置类。
@FeignClient(name = "alipay",
path = "/v1",
url = "${fegin-client.alipay-url}",
configuration = {DefaultFeignRetryConfig.class})
声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。
本篇我们继续通过了解Spring Cloud Kubernetes实现动态加载配置接口来理解Spring Cloud动态配置实现的整个流程。
订阅
订阅新文章发布通知吧,不错过精彩内容!
输入邮箱,提交后我们会给您发送一封邮件,您需点击邮件中的链接完成订阅设置。