关注 “Java艺术” 我们一起成长!
经过前面几篇的源码分析,我们对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);
}
}
}
该配置类生效的前提条件很多:
1、项目中导入了feign-okhttp
包,即当前项目的classpath
下存在一个feign.okhttp.OkHttpClient
类,该类实现了Client
接口;
io.github.openfeign
feign-okhttp
11.0
2、在application.yaml
中配置feign.okhttp.enabled
为true
;
## 配置feign使用okhttp
feign:
okhttp:
enabled: true
3、未导入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})
相比付费专栏,免费的不香吗?欢迎点赞、分享转发、在看...
▼
往期原创精选
▼
Ribbon重试策略RetryHandler的配置与源码分析
OpenFeign与Ribbon源码分析总结(面试题)
Spring Cloud Ribbon源码分析(Spring Cloud Kubernetes)
Spring Cloud OpenFeign源码分析
[Java艺术] 微信号:javaskill
一个只推送原创文章的技术公众号,分享Java后端相关技术。