Implementation of Feign on Spring WebClient. Brings you the best of two worlds together : concise syntax of Feign to write client side API on fast, asynchronous and non-blocking HTTP client of Spring WebClient.
之前的 Feign 源码随便看看 是非响应式的,是不能用于 webflux 的应用,然而 Feign Reactive 是可以用于 webflux 应用的。底层使用了 webclient 非阻塞的 web 客户端。
还是从一个单元测试开始
需要引入 feign reactive 的坐标 com.playtika.reactivefeign:feign-reactor-core:3.0.3 和 com.playtika.reactivefeign:feign-reactor-webclient:3.0.3
@SpringBootTest
class ReactiveFeignTest {
public static String baseUrl;
private static final String DEFAULT_BODY = "hello, world!";
@BeforeAll
public static void init() {
// 初始化 mock web server,用于 mock http请求
MockWebServer server = new MockWebServer();
MockResponse mockResponse = new MockResponse().setBody(DEFAULT_BODY);
server.enqueue(mockResponse);
baseUrl = server.url("/default").toString();
}
// 测试客户端 feign 的请求模板定义
interface TestClient {
@RequestLine("GET /test/{owner}")
Mono<String> test(@Param("owner") String owner);
}
@Test
void testSimpleReactiveFeign() {
// 使用 Feign 创建出 TestClient 的实例
TestClient testClient = WebReactiveFeign.<TestClient>builder().target(TestClient.class, baseUrl);
// 发起 http 调用
String result = testClient.test("o").block();
Assertions.assertEquals(DEFAULT_BODY, result);
}
}
还是之前的例子只不过改成了 WebReactiveFeign,模板接口的方法返回值改成 Mono 或者 Flux。
如何做到的非阻塞
从 WebReactiveFeign#builder() 看起。
public class WebReactiveFeign {
public static <T> Builder<T> builder() {
return builder(WebClient.builder());
}
public static <T> Builder<T> builder(WebClient.Builder webClientBuilder) {
return new Builder<>(webClientBuilder);
}
public static <T> Builder<T> builder(WebClient.Builder webClientBuilder,
WebClientFeignCustomizer webClientCustomizer) {
return new Builder<>(webClientBuilder, webClientCustomizer);
}
public static class Builder<T> extends CoreWebBuilder<T> {
......
}
}
abstract public class CoreWebBuilder<T> extends ReactiveFeign.Builder<T>{
protected CustomizableWebClientBuilder webClientBuilder;
protected CoreWebBuilder(WebClient.Builder webClientBuilder) {
this.webClientBuilder = new CustomizableWebClientBuilder(webClientBuilder);
this.webClientBuilder.clientConnector(buildClientConnector());
updateClientFactory();
}
// 主要是这个地方构建 ReactiveHttpClientFactory
// 用于创建 ReactiveHttpClient 类似于 feign 中的 client 组件
protected void updateClientFactory(){
clientFactory(methodMetadata -> webClient(
methodMetadata, webClientBuilder.build(), errorMapper()));
}
......
}
看到这里有个印象,feign reactive 不再通过实现 feign 中的 client 去实现响应式,因为 client 的返回值不是 Publisher<T> 类型的,所以用 ReactiveHttpClient 作为响应式的 client 抽象。
看看 ReactiveHttpClient 的源码和 ReactiveHttpClientFactory 的源码。
public interface ReactiveHttpClientFactory {
default void target(Target target){}
ReactiveHttpClient create(MethodMetadata methodMetadata);
}
public interface ReactiveHttpClient<P extends Publisher<?>> {
Mono<ReactiveHttpResponse<P>> executeRequest(ReactiveHttpRequest request);
}
CoreWebBuilder#updateClientFactory 的大概意思是在调用 ReactiveHttpClientFactory#create() 会返回 ReactiveHttpClient 实例,这里具体返回的是 WebReactiveHttpClient。
public class WebReactiveHttpClient<P extends Publisher<?>> implements ReactiveHttpClient<P> {
......
private final WebClient webClient;
@Override
public Mono<ReactiveHttpResponse<P>> executeRequest(ReactiveHttpRequest request) {
return webClient.method(HttpMethod.valueOf(request.method()))
.uri(request.uri())
.headers(httpHeaders -> setUpHeaders(request, httpHeaders))
.body(provideBody(request))
.exchange()
.onErrorMap(ex -> {
Throwable errorMapped = errorMapper.apply(request, ex);
if(errorMapped != null){
return errorMapped;
} else {
return new ReactiveFeignException(ex, request);
}
})
.map(response -> toReactiveHttpResponse(request, response));
}
......
}
真正是通过非阻塞的 webclient 实现的。
feign client 实例的构建过程
继续回到 CoreWebBuilder,这个类继承了 ReactiveFeign.Builder ,最终调用了 ReactiveFeignBuilder#target() 创建出了 feign client 实例。
public interface ReactiveFeignBuilder<T> {
......
/**
* Defines target and builds client.
*
* @param apiType API interface
* @param url base URL
* @return built client
*/
default T target(final Class<T> apiType, final String url) {
return target(new Target.HardCodedTarget<>(apiType, url));
}
default T target(final Class<T> apiType, final String name, final String url) {
if(name.equals(url)){
throw new IllegalArgumentException(String.format("Name is equal to url: name=[%s], url=[%s]", name, url));
}
if(!url.contains(name)){
throw new IllegalArgumentException(String.format("Name should be part of url: name=[%s], url=[%s]", name, url));
}
return target(new Target.HardCodedTarget<>(apiType, name, url));
}
/**
* Defines target and builds client.
*
* @param target target instance
* @return built client
*/
default T target(final Target<T> target) {
return build().newInstance(target);
}
default ReactiveFeign build() {
return new ReactiveFeign(contract(),
buildReactiveMethodHandlerFactory(buildReactiveClientFactory()),
invocationHandlerFactory());
}
/**
* 解析方法中的注解,和 feign 中的一样
*/
Contract contract();
/**
* 创建 构建ReactiveInvocationHandler的工厂
* ReactiveInvocationHandler主要做的就是
* dispatch.get(method).invoke(args);
*/
default InvocationHandlerFactory invocationHandlerFactory(){
return new ReactiveInvocationHandler.Factory();
}
/**
* MethodHandlerFactory 是 feign reactive 新添加的组件
* 用于创建 MethodHandler
* PublisherClientFactory 用于创建 PublisherClient
* PublisherClient 中持有 ReactiveHttpClient 对象
* MethodHandler 中持有 PublisherClient 对象
* 所以 ReactiveInvocationHandler -> dispatch.get(method).invoke(args)
* -> MethodHandler -> PublisherClient -> ReactiveHttpClient -> 发起请求
*/
MethodHandlerFactory buildReactiveMethodHandlerFactory(PublisherClientFactory reactiveClientFactory);
/**
* 构建PublisherClientFactory实例
*/
PublisherClientFactory buildReactiveClientFactory();
}
最终调用的是 ReactiveFeign#newInstance() 创建实例。从这里开始就有点绕了,注意 ReactiveFeign 的构造函数,调用了下面的四个方法,其实都已经被 ReactiveFeign.Builder 重写过了。
public abstract static class Builder<T> implements ReactiveFeignBuilder<T>{
......
protected InvocationHandlerFactory invocationHandlerFactory =
new ReactiveInvocationHandler.Factory();
protected Builder(){
contract(new Contract.Default());
addLoggerListener(new DefaultReactiveLogger(Clock.systemUTC()));
}
@Override
public Contract contract(){
return contract;
}
@Override
public InvocationHandlerFactory invocationHandlerFactory(){
return invocationHandlerFactory;
}
@Override
public PublisherHttpClient create(MethodMetadata methodMetadata) {
checkNotNull(clientFactory,
"clientFactory wasn't provided in ReactiveFeign builder");
ReactiveHttpClient reactiveClient = clientFactory.create(methodMetadata);
List<ReactiveHttpExchangeFilterFunction> exchangeFilterFunctionsAll = new ArrayList(exchangeFilterFunctions);
if(decode404){
exchangeFilterFunctionsAll.add(ofResponseProcessor(ResponseMappers.ignore404()));
}
if(statusHandler != null) {
exchangeFilterFunctionsAll.add(ofResponseProcessor(handleStatus(statusHandler)));
}
for(ReactiveLoggerListener<Object> loggerListener : loggerListeners){
exchangeFilterFunctionsAll.add(log(methodMetadata, target, loggerListener));
}
Optional<ReactiveHttpExchangeFilterFunction> exchangeFilterFunction = exchangeFilterFunctionsAll.stream()
.reduce(ReactiveHttpExchangeFilterFunction::then);
if(exchangeFilterFunction.isPresent()){
reactiveClient = exchangeFilterFunction.get().filter(reactiveClient);
}
// 根据返回值类型区分不同的PublisherHttpClient
// 返回值是mono或者flux
reactivefeign.publisher.PublisherHttpClient publisherClient = toPublisher(reactiveClient, methodMetadata);
// 重试策略
if (retryPolicy != null) {
publisherClient = retry(publisherClient, methodMetadata, retryPolicy.toRetryFunction());
}
return publisherClient;
}
};
}
@Override
public MethodHandlerFactory buildReactiveMethodHandlerFactory(PublisherClientFactory reactiveClientFactory) {
MethodHandlerFactory methodHandlerFactory = new ReactiveMethodHandlerFactory(reactiveClientFactory);
return fallbackFactory != null
? new FallbackMethodHandlerFactory(methodHandlerFactory, (Function<Throwable, Object>) fallbackFactory)
: methodHandlerFactory;
}
protected PublisherHttpClient toPublisher(ReactiveHttpClient reactiveHttpClient, MethodMetadata methodMetadata){
if(isResponsePublisher(methodMetadata.returnType())){
return new ResponsePublisherHttpClient(reactiveHttpClient);
}
// 根据返回值判断返回 MonoPublisherHttpClient 或者 FluxPublisherHttpClient
Class returnPublisherType = returnPublisherType(methodMetadata);
if(returnPublisherType == Mono.class){
return new MonoPublisherHttpClient(reactiveHttpClient);
} else if(returnPublisherType == Flux.class){
return new FluxPublisherHttpClient(reactiveHttpClient);
} else {
throw new IllegalArgumentException("Unknown returnPublisherType: " + returnPublisherType);
}
}
......
}
到这里可以调用 ReactiveFeign#newInstance()
public class ReactiveFeign {
......
private final Contract contract;
private final MethodHandlerFactory methodHandlerFactory;
private final InvocationHandlerFactory invocationHandlerFactory;
public <T> T newInstance(Target<T> target) {
final Map<String, MethodHandler> nameToHandler = targetToHandlersByName(target);
// 接口中的方法和MethodHandler的对应关系
final Map<Method, InvocationHandlerFactory.MethodHandler> methodToHandler = new LinkedHashMap<>();
final List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<>();
for (final Method method : target.type().getMethods()) {
if (isDefault(method)) {
final DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method,
nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// jdk 的动态代理创建实例
final InvocationHandler handler = invocationHandlerFactory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (final DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
......
}
MethodHandler的创建过程
ReactiveFeign 中的 MethodHandlerFactory 来创建 MethodHandler。
public class ReactiveMethodHandlerFactory implements MethodHandlerFactory {
......
@Override
public MethodHandler create(MethodMetadata metadata) {
MethodHandler methodHandler = new PublisherClientMethodHandler(
target, metadata, publisherClientFactory.create(metadata));
if(isResponsePublisher(metadata.returnType())){
return new MonoMethodHandler(methodHandler);
}
Type returnPublisherType = returnPublisherType(metadata);
if(returnPublisherType == Mono.class){
return new MonoMethodHandler(methodHandler);
} else if(returnPublisherType == Flux.class) {
return new FluxMethodHandler(methodHandler);
} else {
throw new IllegalArgumentException("Unknown returnPublisherType: " + returnPublisherType);
}
}
......
}
当模板接口中的方法返回值不是 mono 或者 flux 的时候会报错。这里到PublisherClientFactory 创建 PublisherClient 的时候也会区分返回值的类型来确定 PublisherClient 的具体实现。
整体请求的流程
ReactiveInvocationHandler
-> dispatch.get(method).invoke(args)
-> MethodHandler(返回值区分mono/flux)
-> PublisherClient (返回值区分mono/flux)
-> ReactiveHttpClient
-> webClient
-> 发起请求