文章目录
前言
该篇博文,主要通过源码分析方式,介绍Reactive Feign接口,底层封装过程,并借此解答上一篇博文一文读懂Reactive Feign:底层请求调用原理及实现细节提及的部分源码类的串联过程。
通过该篇博文,大致可以了解如下内容:
- Feign接口,在Spring环境中,被代理成哪个Bean,进行实际使用。
- Feign接口代理类,内部封装逻辑。
- Feign请求,数据流转方式。
一、Feign接口,代理类创建过程
相信大家知道,在配置类上,通过EnableFeignClients注解,启用Reactive Feign。该注解,通过Import注解,引入ReactiveFeignClientsRegistrar。通过该类,扫描ReactiveFeignClient注解标记的接口,并通过动态代理,装配成Bean。
关于这部分内容,与FeignClient代理类创建过程分析类似,此处不再赘述。不同点在于,Reactive Feign通过ReactiveFeignClientFactoryBean,开启代理类功能。后续内容,也是基于该类,进行分析。
二、Feign代理类,请求处理逻辑封装
ReactiveFeignClientFactoryBean,实现FactoryBean接口,通过getObject为获取Bean实例。此处,将通过geiObject方法,开启这部分内容的源码分析。
java
// ReactiveFeignClientFactoryBean.java 内容
@Override
public Object getObject() {
return getTarget();
}
/**
* @param <T> the target type of the Feign client
* @return a {@link ReactiveFeign} client created with the specified data and the context information
*/
private <T> T getTarget() {
ReactiveFeignNamedContext namedContext = new ReactiveFeignNamedContext(applicationContext, name);
ReactiveFeignBuilder builder = namedContext.get(ReactiveFeignBuilder.class)
.contract(namedContext.get(Contract.class));
builder = applyConfigurators(builder, namedContext);
if (decode404) {
builder = builder.decode404();
}
builder = fallback(builder, namedContext);
// 通过判断url是否有值,确定是否需要开启负载均衡。this.name,值为服务实例名称。
// builder,对应于ReactiveFeignBuilder
return StringUtils.hasText(this.url)
? (T) builder.target(type, buildUrl())
: (T) builder.target(type, this.name, buildUrl());
}
// ReactiveFeignBuilder.java 内容
default T target(final Target<T> target) {
return build().newInstance(target);
}
// ReactiveFeign构造方法,三个参数类型,分别为ReactiveContract、ReactiveMethodHandlerFactory、ReactiveInvocationHandler.Factory
// ReactiveContract,内部引入SpringMvcContract,并在SpringMvcContract,完成信息收集工作,在进一步判断返回值类型,是否满足Reactor要求。
default ReactiveFeign build() {
return new ReactiveFeign(contract(),
buildReactiveMethodHandlerFactory(buildReactiveClientFactory()),
invocationHandlerFactory());
}
接下来,我们将开始分析ReactiveFeign.newInstance方法,该方法,是一个比较重要的方法,在该方法内部,结合SpringMvcContract、ReactiveMethodHandlerFactory、ReactiveInvocationHandler.Factory,创建MethodHandler实例,后续,请求是通过Handler完成的处理。在这里,我们首先介绍MethodHandler的创建过程
java
Map<String, MethodHandler> targetToHandlersByName(final Target target) {
// 此处使用SpringMvcContract,收集接口方法信息。MethodMetadata,包含接口类、方法、方法签名等信息。
Map<String, MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type())
.stream()
.collect(Collectors.toMap(
MethodMetadata::configKey,
md -> md
));
Map<String, Method> configKeyToMethod = Stream.of(target.type().getMethods())
.collect(Collectors.toMap(
method -> Feign.configKey(target.type(), method),
method -> method
));
final Map<String, MethodHandler> result = new LinkedHashMap<>();
methodHandlerFactory.target(target);
for (final Map.Entry<String, Method> entry : configKeyToMethod.entrySet()) {
String configKey = entry.getKey();
MethodMetadata md = metadata.get(configKey);
MethodHandler methodHandler = md != null
// 通过ReactiveMethodHandlerFactory.create方法,创建MethodHandler实例。
? methodHandlerFactory.create(md)
: methodHandlerFactory.createDefault(entry.getValue()); //isDefault(entry.getValue())
result.put(configKey, methodHandler);
}
return result;
}
// ReactiveMethodHandlerFactory.java 内容
@Override
public MethodHandler create(MethodMetadata metadata) {
// 首先创建PublisherClientMethodHandler实例,其内部包含PublisherHttpClient实例,该用例,完成请求执行动作。
MethodHandler methodHandler = new PublisherClientMethodHandler(
target, metadata, publisherClientFactory.create(metadata));
if(isResponsePublisher(metadata.returnType())){
return new MonoMethodHandler(methodHandler);
}
// 通过返回值,分别构造MonoMethodHandler、FluxMethodHandler,其内部,都是通过PublisherClientMethodHandler,完成请求执行动作。这两个MethodHandler,仅仅将结果通过Mono.from、Flux.from,封装PublisherClientMethodHandler执行处理动作。
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);
}
}
// PublisherClientMethodHandler内容
@Override
public Publisher<?> invoke(final Object[] argv) {
// 其中publisherClient,为LoadBalancerPublisherClient类型实例。
return publisherClient.executeRequest(buildRequest(argv));
}
// LoadBalancerPublisherClient.java内容。
@Override
public Publisher<Object> executeRequest(ReactiveHttpRequest request) {
return Mono.from(reactiveLoadBalancer.choose())
.flatMapMany(serviceInstanceResponse -> {
URI lbUrl = reconstructURI(serviceInstanceResponse.getServer(), request.uri());
ReactiveHttpRequest lbRequest = new ReactiveHttpRequest(request, lbUrl);
// publisherClient,为MonoPublisherHttpClient
return publisherClient.executeRequest(lbRequest);
});
}
到此,就基本上结束Reactive Feign,请求内部处理逻辑的封装。再次,做一个简单的总结
Reactive Feign接口,通过动态代理,创建代理类,并注册Spring Bean。当接口调用时,内部处理逻辑,最终通过LoadBalancerPublisherClient,进行请求处理,然后,根据返回值类型,将其封装成Mono、Flux。
三、Feign请求,数据流转
在这里,主要介绍MonoPublisherHttpClient,用于揭开Reactive Feign,内部,如何执行请求。
java
// MonoPublisherHttpClient.java 内容
@Override
public Mono<Object> executeRequest(ReactiveHttpRequest request) {
// reactiveHttpClient为WebReactiveHttpClient实例
Mono<ReactiveHttpResponse> response = Mono.defer(() -> reactiveHttpClient.executeRequest(request));
return response.flatMap(resp -> Mono.from(resp.body()));
}
// WebReactiveHttpClient.java 内容
@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));
}
总结
- 通过EnableFeignClients注解,开启Reactive Feign功能。
- Reactive Feign,通过动态代理,将Reactive Feign装配成Bean。
- 基于微服务调用时,先通过ReactiveLoadBalancer.choose,选择服务实例。
- 获取到服务实例后,通过WebClient,进行请求调用,并根据返回值不同,将结果通过Mono.from、Flux.from进行封装。