背景
希望将http的调用由同步等待改为异步,仍使用feign的便捷。
使用feign理由
本质上其实feign就是将httpclient常用的操作进行简单封装,且屏蔽底层的httpclient,无感知具体的client实现,轻松完成具体client的替换
解决方案
feign在10.8版本后提供了Async接口(feign-github),如下:
demo代码实现
-
接口编写
public
interface
OriginFeignClient {
@RequestLine
(
"POST /async/server/api"
)
CompletableFuture<String> api(
@Param
(
"param"
) String param);
}
-
接口发布
@Configuration
public
class
OriginFeignClientConfig {
@Bean
public
SpringEncoder springEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
return
new
SpringEncoder(messageConverters);
}
@Bean
public
SpringDecoder springDecoder(ObjectFactory<HttpMessageConverters> messageConverters) {
return
new
SpringDecoder(messageConverters);
}
@Bean
public
OriginFeignClient originFeignClient(SpringEncoder springEncoder, SpringDecoder springDecoder) {
return
AsyncFeign.asyncBuilder()
.encoder(springEncoder)
.decoder(springDecoder)
.target(OriginFeignClient.
class
,
"http://localhost.charlesproxy.com:8090"
);
}
}
-
调用
@GetMapping
(
"testApi"
)
public
String testAsyncClient()
throws
ExecutionException, InterruptedException {
List<CompletableFuture<String>> results =
new
ArrayList<>();
for
(
int
i =
0
; i <
10
; i++) {
results.add(originFeignClient.api(i+
""
));
}
Thread.sleep(
3000
);
int
index =
0
;
for
(CompletableFuture<String> result : results) {
String str = result.get();
log.info(String.format(
"%d, result:%s, "
, index, str));
index++;
}
return
"success"
;
}
-
结果(很明显,是异步调用)
2021
-
05
-
11
14
:
31
:
29.989
- INFO [TraceId: , SpanId : ]
24745
--- [nio-
8091
-exec-
5
] c.m.a.c.controller.ClientController :
0
, result:receive: {
"param"
:
"0"
} at
2021
-
05
-
11
02
:
31
:
27.243
,
2021
-
05
-
11
14
:
31
:
29.993
- INFO [TraceId: , SpanId : ]
24745
--- [nio-
8091
-exec-
5
] c.m.a.c.controller.ClientController :
1
, result:receive: {
"param"
:
"1"
} at
2021
-
05
-
11
02
:
31
:
27.243
,
2021
-
05
-
11
14
:
31
:
29.995
- INFO [TraceId: , SpanId : ]
24745
--- [nio-
8091
-exec-
5
] c.m.a.c.controller.ClientController :
2
, result:receive: {
"param"
:
"2"
} at
2021
-
05
-
11
02
:
31
:
27.244
,
2021
-
05
-
11
14
:
31
:
29.995
- INFO [TraceId: , SpanId : ]
24745
--- [nio-
8091
-exec-
5
] c.m.a.c.controller.ClientController :
3
, result:receive: {
"param"
:
"3"
} at
2021
-
05
-
11
02
:
31
:
27.245
,
2021
-
05
-
11
14
:
31
:
29.995
- INFO [TraceId: , SpanId : ]
24745
--- [nio-
8091
-exec-
5
] c.m.a.c.controller.ClientController :
4
, result:receive: {
"param"
:
"4"
} at
2021
-
05
-
11
02
:
31
:
27.243
,
2021
-
05
-
11
14
:
31
:
29.996
- INFO [TraceId: , SpanId : ]
24745
--- [nio-
8091
-exec-
5
] c.m.a.c.controller.ClientController :
5
, result:receive: {
"param"
:
"5"
} at
2021
-
05
-
11
02
:
31
:
27.243
,
2021
-
05
-
11
14
:
31
:
29.996
- INFO [TraceId: , SpanId : ]
24745
--- [nio-
8091
-exec-
5
] c.m.a.c.controller.ClientController :
6
, result:receive: {
"param"
:
"6"
} at
2021
-
05
-
11
02
:
31
:
27.244
,
2021
-
05
-
11
14
:
31
:
29.997
- INFO [TraceId: , SpanId : ]
24745
--- [nio-
8091
-exec-
5
] c.m.a.c.controller.ClientController :
7
, result:receive: {
"param"
:
"7"
} at
2021
-
05
-
11
02
:
31
:
27.243
,
2021
-
05
-
11
14
:
31
:
29.997
- INFO [TraceId: , SpanId : ]
24745
--- [nio-
8091
-exec-
5
] c.m.a.c.controller.ClientController :
8
, result:receive: {
"param"
:
"8"
} at
2021
-
05
-
11
02
:
31
:
27.244
,
2021
-
05
-
11
14
:
31
:
29.998
- INFO [TraceId: , SpanId : ]
24745
--- [nio-
8091
-exec-
5
] c.m.a.c.controller.ClientController :
9
, result:receive: {
"param"
:
"9"
} at
2021
-
05
-
11
02
:
31
:
27.245
,
问题
以上使用仅限雨原生feign使用,那Spring Cloud Open Feign呢?看下代码吧
feign增加的async实现是10.8版本,我们找到Spring boot 2.2.13和相应cloud最新版Hoxton.SR11(当然不是最新,是该小版本下最新,因为目前项目中使用的就是该版本),排查思路就是以@EnableFeignClients为入口
- EnableFeignClients内容如下
@Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { }
引入了FeignClientsRegistrar
-
FeignClientsRegistrar功能如下:
-
扫描@FeignClient,并将其下入到BeanDefinition中
-
调用registerFeignClient,构造FeignClientFactoryBean(创建feign的工厂类,关键在此)
-
进入FeignClientFactoryBean内部看下,构建feign的方法:getTarget():
-
从context中去找feign.Feign.Builder的实现(即@Bean),看下当前项目中有哪几种实现:
-
org.springframework.cloud.openfeign.FeignCircuitBreaker.Builder Spring Cloud定义的通用断路器
-
feign.hystrix.HystrixFeign.Builder 断路器的原生实现
-
-
先不管哪种builder,最终执行:loadBalance,方法内容如下:
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) { Client client = getOptional(context, Client.class); if (client != null) { builder.client(client); Targeter targeter = get(context, Targeter.class); return targeter.target(this, builder, context, target); } throw new IllegalStateException( "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?"); }
好了!!看到我们想要的了,Client的创建,不对,等等!!Client,我们先看下他的实现类是不是包含我们的AsyncClient,F*U*C*K¥%##¥!!!
-
一切幻想破灭
-
-
原来这个一直都是个平行于现有Spring Cloud Feign实现的东东
好了,所以目前的Spring cloud openfeign中仍未支持该特性,先这样用吧