openfeign是通过FeignClientFactoryBean生成动态代理对象的方式实现http客户端无感调用,可以做到像定义接口一样写http客户端调用代码。
配置Feign接口后,我们通常会在SpringBoot项目启动类上标记@EnableFeignClients,这个是生成动态代理对象的入口。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
/**
* basePackages 配置的别称
*/
String[] value() default {};
/**
* 扫描包路径下有@FeignClient注解的接口
*/
String[] basePackages() default {};
/**
* 扫描类对应包下有@FeignClient注解的接口
*/
Class<?>[] basePackageClasses() default {};
/**
* 默认配置
*/
Class<?>[] defaultConfiguration() default {};
/**
* 直接指定标记了@FeignClient的类
*/
Class<?>[] clients() default {};
}
@EnableFeignClients里面的配置主要是为了找到@FeignClient接口,正常情况下可指定basePackages路径,或者直接不加,会直接扫描启动类及其对应包下面的所有类,去找到所有标记了@FiegnClient的接口。
这里最关键的是@Import(FeignClientsRegistrar.class)引入了FeignClientsRegistrar类。
首先看下FeignClientsRegistrar实现的接口ImportBeanDefinitionRegistrar,Spring IOC在创建bean实例的时候
会调ImportBeanDefinitionRegistrar.registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)方法,这个方法里面是注册默认配置和注册FeignClient的逻辑。
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
...
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 注册默认配置
registerDefaultConfiguration(metadata, registry);
// 注册FeignClient
registerFeignClients(metadata, registry);
}
}
继续看registerFeignClients(metadata, registry)方法,前面主要是通过@EnableFeignClients注解配置找到所有的Feign接口生成BeanDefinition,最后遍历根据BeanDefinition一个个注册FeignClient
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 通过@EnableFeignClients的配置找到对应的Feign接口并生成对应的BeanDefinition
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
// 注册客户端配置
registerClientConfiguration(registry, name, attributes.get("configuration"));
// 注册FeignClient
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
点进去registerFeignClient(registry, annotationMetadata, attributes),这个方法才是真正的注册逻辑。
可以看到方法里面主要是创建了一个FeignClientFactoryBean工厂bean,最终通过getObject()生成bean实例。
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
factoryBean.setRefreshableClient(isClientRefreshEnabled());
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
factoryBean.setUrl(getUrl(beanFactory, attributes));
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
}
// 生成bean实例
return factoryBean.getObject();
});
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.setLazyInit(true);
validate(attributes);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
String[] qualifiers = getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) {
qualifiers = new String[] { contextId + "FeignClient" };
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
registerOptionsBeanDefinition(registry, contextId);
}
进入FeignClientFactoryBean的getObject方法,里面又继续调了getTarget方法。这里有两种情况,一种是指定了url,则会通过给的url封装请求,
如果没有则从注册中心获取服务地址,这里继续看loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
public Object getObject() {
return getTarget();
}
<T> T getTarget() {
FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
// 如果没有指定具体的url,则通过负载均衡从注册中心获取对应的服务地址信息
if (!StringUtils.hasText(url)) {
if (LOG.isInfoEnabled()) {
LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
}
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
}
...
}
继续loadBalance方法解析,这里主要是从FeignContext获取到Client实例和Targeter实例,debug的话这两个实例分别对应的类是
Client -> TraceRetryableFeignBlockingLoadBalancerClient
Targeter -> DefaultTargeter
这两个类都很关键
- TraceRetryableFeignBlockingLoadBalancerClient是后续调用feign接口时通过RetryableFeignBlockingLoadBalancerClient获取对应的负载均衡服务实例
- DefaultTargeter则是生成动态代理对象
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
applyBuildCustomizers(context, builder);
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-loadbalancer?");
}
继续看动态代理对象生成的逻辑,DefaultTargeter的target方法里面,主要调用了feign.target(target)
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
feign.target先调用了build方法,然后再调newInstance方法。先看builde方法的逻辑,核心是创建了SynchronousMethodHandler.Factory对象,并且返回了ReflectiveFeign对象。
这个工厂对象主要是用来创建SynchronousMethodHandler的,而SynchronousMethodHandler就是feign接口每个方法都会封装一个对应的MethodHandler,里面包含了封装请求,发送请求,解析响应的所有流程。
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
public Feign build() {
super.enrich();
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,
responseInterceptor, logger, logLevel, dismiss404, closeAfterDecode,
propagationPolicy, forceDecoding);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
继续看ReflectiveFeign.newInstance(Target target)方法,通过SpringMvcContract解析了feign接口相关的元信息,
SynchronousMethodHandler.Factory创建了这个类所有方法的MethodHandler,并最终通过InvocationHandlerFactory创建ReflectiveFeign.FeignInvocationHandler,最终返回动态代理对象。
public <T> T newInstance(Target<T> target) {
// 通过SpringMvcContract解析了feign接口相关的元信息,创建MethodHandler
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 创建ReflectiveFeign.FeignInvocationHandler,生成动态代理对象
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
至此所有的feign接口通过FeignInvocationHandler生成了动态代理对象,里面包含了feign接口所有方法的Map<Method, MethodHandler> dispatch。
后续调用feign接口只需要直接注入使用即可。动态代理对象直接对应方法时,最终会调用InvocationHandler的invoke方法。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
// 通过方法获取到对应的SynchronousMethodHandler执行
return dispatch.get(method).invoke(args);
}
在调用feign接口方法时,最终会执行SynchronousMethodHandler的invoke方法。
- 封装请求,执行请求,解码
- 如果抛RetryableException异常,如果不配置Retryer实例,则默认是不重试
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
// 执行请求并且对响应结果进行解码
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
// 累计重试次数,判断是否继续重试
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
executeAndDecode方法执行请求并且处理响应结果
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = targetRequest(template);
if (logLevel != Logger.Level.NONE) {
logger.logRequest(metadata.configKey(), logLevel, request);
}
Response response;
long start = System.nanoTime();
try {
// 发送请求
response = client.execute(request, options);
// ensure the request is set. TODO: remove in Feign 12
response = response.toBuilder()
.request(request)
.requestTemplate(template)
.build();
} catch (IOException e) {
if (logLevel != Logger.Level.NONE) {
logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));
}
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (decoder != null) {
return responseInterceptor
.aroundDecode(new InvocationContext(decoder, metadata.returnType(), response));
}
CompletableFuture<Object> resultFuture = new CompletableFuture<>();
// 处理响应结果
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
metadata.returnType(), elapsedTime);
try {
if (!resultFuture.isDone())
throw new IllegalStateException("Response handling not done");
return resultFuture.join();
} catch (CompletionException e) {
Throwable cause = e.getCause();
if (cause != null)
throw cause;
throw e;
}
}
RetryableFeignBlockingLoadBalancerClient做了三件事
- 获取负载均衡服务实例信息
- 构建完整请求
- 发送请求并得到响应结果
public Response execute(Request request, Request.Options options) throws IOException {
final URI originalUri = URI.create(request.url());
String serviceId = originalUri.getHost();
Assert.state(serviceId != null, "Request URI does not contain a valid hostname: " + originalUri);
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryFactory.createRetryPolicy(serviceId,
loadBalancerClient);
RetryTemplate retryTemplate = buildRetryTemplate(serviceId, request, retryPolicy);
return retryTemplate.execute(context -> {
Request feignRequest = null;
ServiceInstance retrievedServiceInstance = null;
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
.getSupportedLifecycleProcessors(
loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
RetryableRequestContext.class, ResponseData.class, ServiceInstance.class);
String hint = getHint(serviceId);
DefaultRequest<RetryableRequestContext> lbRequest = new DefaultRequest<>(
new RetryableRequestContext(null, buildRequestData(request), hint));
// On retries the policy will choose the server and set it in the context
// and extract the server and update the request being made
if (context instanceof LoadBalancedRetryContext) {
LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
ServiceInstance serviceInstance = lbContext.getServiceInstance();
if (serviceInstance == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. "
+ "Reattempting service instance selection");
}
ServiceInstance previousServiceInstance = lbContext.getPreviousServiceInstance();
lbRequest.getContext().setPreviousServiceInstance(previousServiceInstance);
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
// 负载均衡client获取到服务实例信息
retrievedServiceInstance = loadBalancerClient.choose(serviceId, lbRequest);
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Selected service instance: %s", retrievedServiceInstance));
}
lbContext.setServiceInstance(retrievedServiceInstance);
}
if (retrievedServiceInstance == null) {
if (LOG.isWarnEnabled()) {
LOG.warn("Service instance was not resolved, executing the original request");
}
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
retrievedServiceInstance);
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
.onComplete(new CompletionContext<ResponseData, ServiceInstance, RetryableRequestContext>(
CompletionContext.Status.DISCARD, lbRequest, lbResponse)));
feignRequest = request;
}
else {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Using service instance from LoadBalancedRetryContext: %s",
retrievedServiceInstance));
}
// 生成带IP和端口的完整服务地址,并构建feign请求
String reconstructedUrl = loadBalancerClient.reconstructURI(retrievedServiceInstance, originalUri)
.toString();
feignRequest = buildRequest(request, reconstructedUrl);
}
}
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
retrievedServiceInstance);
LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);
// 执行请求得到结果
Response response = LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(delegate, options,
feignRequest, lbRequest, lbResponse, supportedLifecycleProcessors, retrievedServiceInstance != null,
loadBalancerProperties.isUseRawStatusCodeInResponseData());
int responseStatus = response.status();
if (retryPolicy != null && retryPolicy.retryableStatusCode(responseStatus)) {
if (LOG.isDebugEnabled()) {
LOG.debug(String.format("Retrying on status code: %d", responseStatus));
}
byte[] byteArray = response.body() == null ? new byte[] {}
: StreamUtils.copyToByteArray(response.body().asInputStream());
response.close();
throw new LoadBalancerResponseStatusCodeException(serviceId, response, byteArray,
URI.create(request.url()));
}
return response;
}, new LoadBalancedRecoveryCallback<Response, Response>() {
@Override
protected Response createResponse(Response response, URI uri) {
return response;
}
});
}
具体的发送请求流程可以详细debug看下,所有细节列出来可能会显得太冗长。过程涉及了很多工厂模式和委托模式,自己debug以下会更加清楚一些细节。比如发送http的客户端,默认是HttpsURLConnection发送请求,LoadBalancerClientConfiguration默认会创建RoundRobinLoadBalancer轮询负载均衡策略,可以看下spring-cloud-openfeign-core下的META-INF\spring.factories自动装备类,进一步结合项目实际情况还可以自定义Encoder,Decoder,ErrorDecoder等。