前言
前几篇文章已经分析过了OpenFeign的Client
组件和重试组件。
今天开始分析OpenFeign的第3个组件RequestInterceptor
。
RequestInterceptor
是在发起远程请求前对请求对象进行拦截的,这个组件其实使用场景挺常见的。比如传递公用的参数,对feign请求url进行限流计数,编码等场景,下面我们来详细看看他的原理。
RequestInterceptor组成
我们需要先看下RequestInterceptor的组成。
package feign;
public interface RequestInterceptor {
void apply(RequestTemplate var1);
}
RequestInterceptor 是一个接口,apply方法接受请求模版RequestTemplate对象,
我们可以看到 feign.SynchronousMethodHandler#executeAndDecode
执行方法中
Object executeAndDecode(RequestTemplate template) throws Throwable {
//这里会对请求做一次拦截处理
Request request = this.targetRequest(template);
long start = System.nanoTime();
Response response;
try {
response = this.client.execute(request, this.options);
//...
return var9;
}
//对请求拦截器进行应用
Request targetRequest(RequestTemplate template) {
Iterator var2 = this.requestInterceptors.iterator();
while(var2.hasNext()) {
//循环每个拦截器进行应用到请求对象
RequestInterceptor interceptor = (RequestInterceptor)var2.next();
interceptor.apply(template);
}
return this.target.apply(template);
}
RequestTemplate组成
RequestInterceptor能够帮我们做什么呢?我们看下RequestTemplate类的组成
public final class RequestTemplate implements Serializable {
private static final Pattern QUERY_STRING_PATTERN = Pattern.compile("(?<!\\{)\\?");
//请求参数
private final Map<String, QueryTemplate> queries = new LinkedHashMap();
//请求头
private final Map<String, HeaderTemplate> headers;
private String target;
private String fragment;
private boolean resolved;
//请求url
private UriTemplate uriTemplate;
private HttpMethod method;
//请求字符集编码
private transient Charset charset;
//请求体
private Body body;
private boolean decodeSlash;
private CollectionFormat collectionFormat;
里面有几个非常熟悉的字段,
queries是存储请求的参数
headers存储请求头
uriTemplate存储请求url
RequestInterceptor使用案例
就是上面这三个字段,工作中可以用来解决一些问题。
一、压缩请求传输的数据
这个OpenFeign内置已经做了一些扩展,我们对一些请求响应时间性能要求比较高的场景,通常需要对请求传输的数据进行压缩
。OpenFeign内置了gzip压缩算法的实现
FeignContentGzipEncodingInterceptor
压缩请求体FeignAcceptGzipEncodingInterceptor
压缩响应体
这两个配置项项目中有没有用过呢?通过这两个项目就可以开启压缩功能了
feign.compression.request.enabled
、 feign.compression.response.enabled
都是通过FeignAcceptGzipEncodingAutoConfiguration
自动装配类实现的
- 开启响应压缩
@Configuration
@EnableConfigurationProperties({
FeignClientEncodingProperties.class})
@ConditionalOnClass({
Feign.class})
@ConditionalOnBean({
Client.class})
@ConditionalOnProperty(
value = {
"feign.compression.response.enabled"},
matchIfMissing = false
)
@ConditionalOnMissingBean(
type = {
"okhttp3.OkHttpClient"}
)
@AutoConfigureAfter({
FeignAutoConfiguration.class})
public class FeignAcceptGzipEncodingAutoConfiguration {
public FeignAcceptGzipEncodingAutoConfiguration() {
}
//响应压缩拦截器
@Bean
public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
return new FeignAcceptGzipEncodingInterceptor(properties);
}
}
- 开启请求压缩
@Configuration
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
@ConditionalOnClass(Feign.class)
@ConditionalOnBean(Client.class)
// The OK HTTP client uses "transparent" compression.
// If the content-encoding header is present it disable transparent compression
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
@ConditionalOnProperty(value = "feign.compression.request.enabled", matchIfMissing = false)
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignContentGzipEncodingAutoConfiguration {
@Bean
public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor(
FeignClientEncodingProperties properties) {
return new FeignContentGzipEncodingInterceptor(properties);
}
}
需要注意的是如果使用okhttp通信客户端,okhttp是支持transparent compression
压缩的,因此上面的配置都是在非okhttp客户端的情况下生效。
二、透传tocken,TraceId,请求计数,监控打点等
如果我们需要对一些请求传一些固定的参数,比如token,或者接入统一的监控平台,一般会使用请求拦截器实现。
比如下方案例,通过RequestInterceptor传递traceId,请求计数
public class TraceIdInterceptor implements RequestInterceptor {
AtomicLong uriAtomicLong = new AtomicLong();
@Override
public void apply(RequestTemplate requestTemplate) {
//传递traceid给下游系统
requestTemplate.header("traceId", UUID.fastUUID().toString(true));
String url = requestTemplate.url();
long accessCount = uriAtomicLong.incrementAndGet();
//上报访问次数...
}
}
RequestInterceptor生效
以上面TraceIdInterceptor
为例,如何使RequestInterceptor生效呢?
一、全局生效
通过配置类的方式,增加@Configuration注解,让Spring容器扫描到,这样就会对所有FeignClient生效。
@Configuration
public class FeignClientConfiguration {
@Bean
public TraceIdInterceptor interceptor() {
return new TraceIdInterceptor();
}
}
二、针对指定FeignClient生效
如果想只对某些FeignClient生效,需要创建一个配置类,同时在FeignClient中指定configuration配置类属性。
public class FeignClientConfiguration {
@Bean
public TraceIdInterceptor interceptor() {
return new TraceIdInterceptor();
}
}
@FeignClient(value = "fox-server", configuration = TestFeignClientConfiguration.class)
public interface FeignService {
@PostMapping("/get")
String getName(@RequestBody @Validated DemoRequest request);
}
注意这种情况FeignClientConfiguration是没有增加@Configuration,不会被Spring容器扫描到的。
总结
RequestInterceptor是OpenFeign提供一个对Feign请求拦截的组件,能够在发起请求前对header,body进行修改,一般用来实现一些非业务功能的需求
,比如公用参数,监控,打点,压缩
等,支持对全局FeignClient生效,也支持对部分FeignClient生效。
关于RequestInterceptor的分析结束了,你有没有更好的使用场景案例呢,欢迎评论区分享给大家。