相关注解
@EnableFeignClients
EnableFeignClients导入了一个ImportBeanDefinitionRegistrar,FeignClientsRegistrar,它会读取EnableFeignClients#basePackages或clients,扫描对应包下@FeignClient类,得到Bean定义。
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients
这里的Bean定义的目的不是为了注册,而是获取类上@FeignClient注解的属性信息。
@FeignClient
@FeignClient注解的属性:
根据@FeignClients的属性创建Feign代理工厂对象(FeignClientFactoryBean),注册一个bean定义,它的实例工厂(instanceSupplier)执行FeignClientFactoryBean.getObject()创建了Feign代理对象
FeignClientFactoryBean
FeignClientFactoryBean.getObject()最终通过Feign这个抽象类的newInstance(Target target)方法创建代理对象
public abstract <T> T newInstance(Target<T> target);
而Feign通过建造者Feign.Builder创建
public Feign build() {
。。。
}
默认是ReflectiveFeign,最终得到的是jdk代理对象,默认的InvocationHandler实现是FeignInvocationHandler,它为方法到methodHandler的映射,将每个方法调用转发到对应的MethodHandler
class FeignInvocationHandler implements InvocationHandler{
private final Map<Method, MethodHandler> dispatch;
@Override
public Object invoke(Object proxy, Method method, Object[] args){
...
return dispatch.get(method).invoke(args);
}
}
MethodHandler
MethodHandler由SynchronousMethodHandler.Factory创建
public MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
...
}
创建它需要MethodMetadata ,也就是方法元数据,由契约(Contract)创建
public interface Contract {
List<MethodMetadata> parseAndValidateMetadata(Class<?> targetType);
}
契约定义了Feign接口方法的解析方式,或者说编写标准,比如SpringMvcContract定义了按照SpringMvc的编写标准来声明Http请求头、请求体参数
SpringMvcContract
总体分为3个步骤:
public interface AnnotatedParameterProcessor {
//支持的方法参数的注解类型
Class<? extends Annotation> getAnnotationType();
//处理注解,根据注解属性设置MethodMetadata的属性
boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method);
}
对于没有AnnotatedParameterProcessor 能处理的注解标注的方法参数,根据类型保存它们的参数下标
public final class MethodMetadata{
//URI类型
private Integer urlIndex;
//其他类型
private Integer bodyIndex;
}
RequestTemplate
保存Contract解析出来的HTTP请求信息模板,包括请求头、请求方法、uri、查询串、请求体等
public class RequestParamParameterProcessor implements AnnotatedParameterProcessor {
@Override
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
...
//将@RequestParam的值写入RequestTemplate
String name = requestParam.value();
//params.add(String.format("{%s}", paramName));
Collection<String> query = context.setTemplateParameter(name, data.template().queries().get(name));
data.template().query(name, query);
}
}
public final class RequestTemplate{
private final Map<String, QueryTemplate> queries;
private final Map<String, HeaderTemplate> headers;
private UriTemplate uriTemplate;
private HttpMethod method;
private Request.Body body;
}
QueryTemplate、HeaderTemplate和UriTemplate 组合了Template,Template用来表示包含几对大括号的字符串,根据大括号分割,大括号外部的是纯文本,内部的是变量名称,执行时会根据参数动态替换。
public final class QueryTemplate {
//查询串的值,在url中一般是逗号分割
private List<Template> values;
//查询串的名称
private final Template name;
}
public class Template {
//TemplateChunk的实现类有Literal和Expression,表示纯文本和带大括号的表达式,解析方法feign.template.Template#parseFragment
private final List<TemplateChunk> templateChunks;
//解析得到templateChunks
private void parseFragment(String fragment) {
...
Expression expression = Expressions.create(chunk);
if (expression == null) {
this.templateChunks.add(Literal.create(this.encodeLiteral(chunk)));
} else {
this.templateChunks.add(expression);
}
}
protected String resolveExpression(
Expression expression,
Map<String, ?> variables) {
String resolved = null;
//根据变量动态替换
Object value = variables.get(expression.getName());
...
}
}
RequestTemplate.Factory
负责填充RequestTemplate的参数,实现类有BuildTemplateByResolvingArgs、BuildFormEncodedTemplateFromArgs、BuildEncodedTemplateFromArgs
interface Factory {
RequestTemplate create(Object[] argv);
}
实现类:
//比如RequestParamAnnoationProcessor
//data.indexToName().put(i, names);
// 解析时添加indexToName
context.setParameterName(name);
class BuildTemplateByResolvingArgs implements RequestTemplate.Factory {
@Override
public RequestTemplate create(Object[] argv) {
//执行时根据实参argv获取值,添加到varBuilder,传给RequestTemplate进行参数替换
Map<String, Object> varBuilder = new LinkedHashMap<String, Object>();
for (Entry<Integer, Collection<String>> entry : metadata.indexToName().entrySet()) {
int i = entry.getKey();
Object value = argv[entry.getKey()];
for (String name : entry.getValue()) {
varBuilder.put(name, value);
}
}
//Template.resolveExpression
RequestTemplate template = resolve(argv, mutable, varBuilder);
}
}
Encoder
负责编码请求体,将用户定义的Model编码后放入请求体
public interface Encoder {
void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}
默认实现是SpringEncoder,在FeignClientsConfiguration中定义了
@Bean
@ConditionalOnMissingBean
@ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
public Encoder feignEncoder(ObjectProvider<AbstractFormWriter> formWriterProvider,
ObjectProvider<HttpMessageConverterCustomizer> customizers) {
//return new SpringEncoder(new SpringFormEncoder(), messageConverters, encoderProperties, customizers);
return springEncoder(formWriterProvider, encoderProperties, customizers);
}
request.body(outputMessage.getOutputStream().toByteArray(), charset);
SynchronousMethodHandler
Feign接口实现类是JDK动态代理对象,InvocationHandler的默认实现是FeignInvocationHandler,服务调用时转发给方法对应的MethodHandler处理,默认实现是SynchronousMethodHandler,处理流程:
public interface RequestInterceptor {
void apply(RequestTemplate template);
}
public interface Client {
Response execute(Request request, Options options) throws IOException;
}
class AsyncResponseHandler {
void handleResponse(CompletableFuture<Object> resultFuture,
String configKey,
Response response,
Type returnType,
long elapsedTime) {
...
}
}
Decoder
响应码>=200且<300,使用Decoder处理,负责将Response转成用户Pojo
public interface Decoder {
Object decode(Response response, Type type) throws IOException, DecodeException, FeignException;
}
默认实现在FeignClientsConfiguration中定义了
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder(ObjectProvider<HttpMessageConverterCustomizer> customizers) {
return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(messageConverters, customizers)));
}
从AsyncResponseHandler.handleResponse和Decoder 实现类可以看出,Feign接口方法返回值支持的类型:
ErrorDecoder
响应码<200或>=300时调用,抛出得到的异常
public interface ErrorDecoder {
public Exception decode(String methodKey, Response response);
}
默认实现是抛出FeignException,并且检查如果有Retry-after响应头,在Retry-after的时间点进行重试
RetryableFeignBlockingLoadBalancerClient
支持重试的负载均衡客户端,通过spring retry实现,默认相同实例不重试,不同实例重试一次,http访问抛异常时重试,返回非2xx响应码不重试,可以通过LoadBalancerClientsProperties配置。生效条件是引入spring-retry, 配置类是DefaultFeignLoadBalancerConfiguration(默认Client)或HttpClientFeignLoadBalancerConfiguration(ApacheHttpClient)
InterceptorRetryPolicy 是spring retry的重试策略,主要负责创建LoadBalancedRetryContext,判断重试和处理异常时则是通过LoadBalancedRetryPolicy 来完成
public class InterceptorRetryPolicy implements RetryPolicy {
private final LoadBalancedRetryPolicy policy;
@Override
public RetryContext open(RetryContext parent) {
return new LoadBalancedRetryContext(parent, request);
}
@Override
public boolean canRetry(RetryContext context) {
//执行LoadBalancedRetryPolicy.canRetryNextServer
}
@Override
public void registerThrowable(RetryContext context, Throwable throwable) {
//执行LoadBalancedRetryPolicy.registerThrowable
}
}
LoadBalancedRetryPolicy 用来判断是否应该在同一实例或不同实例间重试,出现异常时registerThrowable记录同一实例和不同实例的重试次数,得到响应时根据响应码判断是否应该重试
public interface LoadBalancedRetryPolicy {
boolean canRetrySameServer(LoadBalancedRetryContext context);
boolean canRetryNextServer(LoadBalancedRetryContext context);
void registerThrowable(LoadBalancedRetryContext context, Throwable throwable);
boolean retryableStatusCode(int statusCode);
}
RetryAwareServiceInstanceListSupplier
是ServiceInstanceListSupplier的实现类,并且是一个包装器,实现了过滤服务实例中上一次访问过的实例,如果过滤后没有实例了,则还是使用过滤前的
public class RetryAwareServiceInstanceListSupplier{
@Override
public Flux<List<ServiceInstance>> get(Request request) {
...
RetryableRequestContext context = (RetryableRequestContext) request.getContext();
filteredByPreviousInstance(instances, previousServiceInstance);
...
}
private List<ServiceInstance> filteredByPreviousInstance(List<ServiceInstance> instances,
ServiceInstance previousServiceInstance) {
...
}
}
前一次请求的实例保存在LoadBalancedRetryContext中,然后写入到RetryableRequestContext,它保存在DefaultRequest中
public class LoadBalancedRetryContext{
private ServiceInstance previousServiceInstance;
public void setServiceInstance(ServiceInstance serviceInstance) {
//注意,这里是this.serviceInstance,保存前一次请求的实例
setPreviousServiceInstance(this.serviceInstance);
this.serviceInstance = serviceInstance;
}
}
总结
Client执行请求时,首先会执行LoadBalancerClient.choose(),而它委派给ReactiveLoadBalancer.choose(),ReactiveLoadBalancer实现了根据多个服务实例获取其中一个的逻辑,服务实例通过执行ServiceInstanceListSupplier.get()获取,ServiceInstanceListSupplier最终执行服务发现客户端DiscoveryClient获取服务实例。
得到服务实例后,执行LoadBalancerClient#reconstructURI将请求中的服务名称替换成真正的服务实例的ip和端口号,最后执行真正的Client发出请求