前言
上一节,我们学习了 Feign 的相关配置与使用。这节继续学习 Feign 的相关源码。
启动加载
我们知道,在使用 Feign 时,需要在启动类上使用 @EnableFeignClients
注解。
Open Feign 的奥秘就藏在 @EnableFeignClients
中:
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
/** basePackages 别名 */
String[] value() default {};
/** 扫描哪些包路径 */
String[] basePackages() default {};
/** 扫描某类所在的标签 */
Class<?>[] basePackageClasses() default {};
/** Feign 的全局配置类 */
Class<?>[] defaultConfiguration() default {};
/** 扫描哪些类 */
Class<?>[] clients() default {};
}
@EnableFeignClients
注解包含了一些属性,例如包路径,全局配置类等等,但其中最重要的还是 @Import(FeignClientsRegistrar.class)
注解。
之前通过 《深入学习 Spring Boot》系列我们知道,Spring Boot 启动时,会执行被 @Import(XXX.class)
导入的ImportBeanDefinitionRegistrar
配置类。
所以下面,我们直接去看 FeignClientsRegistrar
类。
FeignClientsRegistrar
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private ResourceLoader resourceLoader;
private Environment environment;
// ...
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 注册全局配置
registerDefaultConfiguration(metadata, registry);
// 注册FeignClient
registerFeignClients(metadata, registry);
}
// ...
}
核心方法就是这个 registerBeanDefinitions
了,内部分别调用了 registerDefaultConfiguration
、registerFeignClients
,用于加载配置与加载 Feign Client。
registerDefaultConfiguration
这个里面基本上就是去扫描默认的配置。
private void registerDefaultConfiguration(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
// 获取 EnableFeignClients 的属性值
Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
// 处理 name 与 Feign配置 的映射关系
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
// 由于这里是默认配置,所以加上了 default 前端。
// 实际 name = default.XXX.XXXX.XXX
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
// 这个方法在注册 Feign Client 的配置类时,还会用到。
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
// 这个最后在注册 BD 时,又把 name 加了一层。
// feign-name.配置类地址.FeignClientSpecification#getSimpleName
registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),builder.getBeanDefinition());
}
registerFeignClients
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
// ... 扫描配置的 OpenFeign 包或者类,到 candidateComponents 中。
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) c
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);
// 注册 FeignClient 上的配置类。和上面的一样。
registerClientConfiguration(registry, name,attributes.get("configuration"));
// 注册 FeignClient 本身
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
// 注册 Fegin BD 到 Spring 上下文中,留待后续进行 Bean 初始化。
private void registerFeignClient(BeanDefinitionRegistry registry,AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
// 获取 FeginClient 的 Class 对象。
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
// 获取 Feign 的一些基础配置项
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactor? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
// Spring Boot 提供的 FactoryBean 机制,可以对 Bean 做增强。
// 《深入学习 Spring Boot》 系列中有提到过,后面也会重点分析该类。
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
// 构建 FeignClient 的 BeanDefinitionBuilder
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(clazz, () -> {
// 一些 FeignClient 的基础配置。
factoryBean.setUrl(getUrl(beanFactory, attributes));
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
// 这个我们很熟悉,Feign 的 Fallback。
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));
}
// 获取 Feign 的增强代理的方法.
// 后续我们使用注入 FeignClient 实例时,Spring 会通过这个方法为我们注入 FeignClient 的代理类。
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" };
}
// 注册 BD
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,qualifiers);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
那么至此,Spring Boot 就帮助我们,将 Feign 配置类以及 Feign Client BD 扫描到 Spring Boot 中了,留待后一步初始化。
代理增强的实现
FeignClientFactoryBean
public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,ApplicationContextAware, BeanFactoryAware {
// ...
@Override
public Object getObject() {
return getTarget();
}
<T> T getTarget() {
// 获取 FeignContext 获取 FeignClient 的上下文,每个 Client 一个 Context,由此实现配置隔离。
FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class):applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
// 通常我们走这一分支
if (!StringUtils.hasText(url)) {
if (!name.startsWith("http")) {
url = "http://" + name;
} else {
url = name;
}
url += cleanPath();
return (T) loadBalance(builder, context,new HardCodedTarget<>(type, name, url));
}
// ...
}
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
// 从容器中获取 Client。Client 就是 HTTP 请求的执行器了。
// 一般情况下,这里选择到的 Client 实现是 LoadBalancerFeignClient ,其中包含了负载均衡的能力。
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
// 当我们选用不同的断路器实现时,这里获取获取不同的实现。
// 目前最常用的还是 Hystrix。
Targeter targeter = get(context, Targeter.class);
// 第三方可以在此做一个增强实现,例如 Hystrix 在此设置了 fallback。
return targeter.target(this, builder, context, target);
}
// ...
}
}
Feign
public abstract class Feign {
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
public Feign build() {
Client client = Capability.enrich(this.client, capabilities);
Retryer retryer = Capability.enrich(this.retryer, capabilities);
List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
.map(ri -> Capability.enrich(ri, capabilities))
.collect(Collectors.toList());
Logger logger = Capability.enrich(this.logger, capabilities);
Contract contract = Capability.enrich(this.contract, capabilities);
Options options = Capability.enrich(this.options, capabilities);
Encoder encoder = Capability.enrich(this.encoder, capabilities);
Decoder decoder = Capability.enrich(this.decoder, capabilities);
InvocationHandlerFactory invocationHandlerFactory =
Capability.enrich(this.invocationHandlerFactory, capabilities);
QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
// 此类是将来生成 Feign 代理类的代理功能工厂类。
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
// 此类将来用于扫面 Fegin 中包含的方法与生成对应方法的增强方法。
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
public class ReflectiveFeign extends Feign {
public <T> T newInstance(Target<T> target) {
// 获取 Feign 接口及注解信息的方法,随后封装为 Map<接口方法签名字符串,接口方法处理类> 形式返回。
// MethodHandler实例为子类 SynchronousMethodHandler 。
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
// 把 Map<String, MethodHandler> 转化为 Map<Method, MethodHandler> , 方便构建代理类。
for (Method method : target.type().getMethods()) {
// ...
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
// 根据 Map<Method, MethodHandler> 创建代理类。
// 我们看下面 FeignInvocationHandler, 实际上就是根据当前方法,取出对应的增强逻辑进行执行。
InvocationHandler handler = factory.create(target, methodToHandler);
// 生成代理类
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
// ...
return proxy;
}
static class FeignInvocationHandler implements InvocationHandler {
private final Target target;
private final Map<Method, MethodHandler> dispatch;
// ...
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// ...
return dispatch.get(method).invoke(args);
}
}
}
至此,FeignClient 的代理类就生成结束了,回头我们在类中注入 FeignClient,获取到的就是 Feign 的代理了。
那么接下来,我们的目标就放在 MethodHandler 上,去查看它是如何做增强的。
SynchronousMethodHandler
第一步,我们来看代理方法中拥有的属性。基本上,就是我们在《『 OpenFeign』使用与配置篇》中提到的那些配置项。
final class SynchronousMethodHandler implements MethodHandler {
// 接口方法的基本信息
private final MethodMetadata metadata;
// 可以认为是 FeignClient 的 Class 对象持有者
private final Target<?> target;
// HTTP 请求执行器
private final Client client;
// 重试器
private final Retryer retryer;
// 请求拦截器
private final List<RequestInterceptor> requestInterceptors;
// 日志类
private final Logger logger;
// 日志级别
private final Logger.Level logLevel;
private final RequestTemplate.Factory buildTemplateFromArgs;
// 请求可选项,超时时间之类的。
private final Options options;
// 异常处理策略
private final ExceptionPropagationPolicy propagationPolicy;
}
第二步,我们来查看执行方法。
final class SynchronousMethodHandler implements MethodHandler {
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
// 从这里我们可以知道,只有 RetryableException 可以被重试。
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;
}
}
// ...
continue;
}
}
}
// 执行请求,并且将响应解码
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
// 在这里获取 Request,并且内部执行了请求拦截器
Request request = targetRequest(template);
Response response;
long start = System.nanoTime();
try {
// 通过 Client 执行请求,还记上面看到 Client 实例是子类 LoadBalancerFeignClient。
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) {
// ...
throw errorExecuting(request, e);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (decoder != null)
return decoder.decode(response, metadata.returnType());
// ...
try {
// ....
return resultFuture.join();
} catch (CompletionException e) {
// 。。。
}
}
}
至此,Feign 发送请求前后的处理逻辑,我们基本已经探究结束了。
接下来就是,Client 是如何发送 HTTP 请求的,以及负载均衡是如何处理的。
执行请求与负载均衡
负载均衡这里涉及到 Ribbon , 不是我们这篇文章的重心,所以下面会简要地摘取核心代码来理解。
LoadBalancerFeignClient
// FeignClient HTTP 执行器
public class LoadBalancerFeignClient implements Client {
// ...
@Override
public Response execute(Request request, Request.Options options) throws IOException {
try {
URI asUri = URI.create(request.url());
String clientName = asUri.getHost();
URI uriWithoutHost = cleanUrl(request.url(), clientName);
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
this.delegate, request, uriWithoutHost);
IClientConfig requestConfig = getClientConfig(options, clientName);
// 这里创建 一个 FeignLoadBalancer 负载均衡器,然后执行 executeWithLoadBalancer 方法。
return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
// ...
}
}
}
FeignLoadBalancer
public class FeignLoadBalancer extends
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
}
public abstract class AbstractLoadBalancerAwareClient<S extends ClientRequest, T extends IResponse>
extends LoadBalancerContext implements IClient<S, T>, IClientConfigAware {
// 当调用者想要将请求分派到负载均衡器选择的服务器时,应该使用此方法,而不是在请求的 URI 中指定服务器。
// 它通过调用 reconstructURIWithServer(Server, URI) 计算最终的URI,
// 然后调用 executeWithLoadBalancer(ClientRequest, IClientConfig) 。
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
try {
return command.submit(
new ServerOperation<T>() {
@Override
public Observable<T> call(Server server) {
URI finalUri = reconstructURIWithServer(server, request.getUri());
S requestForServer = (S) request.replaceUri(finalUri);
try {
return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
}
catch (Exception e) {
return Observable.error(e);
}
}
})
.toBlocking()
.single();
} catch (Exception e) {
// 。。。
}
}
}
LoadBalancerCommand
public class LoadBalancerCommand<T> {
// ...
public Observable<T> submit(final ServerOperation<T> operation) {
// ...
// Use the load balancer
// 这里的关键之处在于 selectServer() , 即选择一个客户端。
Observable<T> o = (server == null ? selectServer() : Observable.just(server))
.concatMap(new Func1<Server, Observable<T>>() {
@Override
// Called for each server being selected
public Observable<T> call(Server server) {
// ....
// Called for each attempt and retry
Observable<T> o = Observable
.just(server)
.concatMap(new Func1<Server, Observable<T>>() {
@Override
public Observable<T> call(final Server server) {
return operation.call(server).doOnEach(new Observer<T>() {
// ....
});
}
});
if (maxRetrysSame > 0)
o = o.retry(retryPolicy(maxRetrysSame, true));
return o;
}
});
// ...
}
private Observable<Server> selectServer() {
return Observable.create(new OnSubscribe<Server>() {
@Override
public void call(Subscriber<? super Server> next) {
try {
// 调用 loadBalancerContext.getServerFromLoadBalancer 获取 Server 信息。
Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);
next.onNext(server);
next.onCompleted();
} catch (Exception e) {
next.onError(e);
}
}
});
}
}
LoadBalancerContext
public class LoadBalancerContext implements IClientConfigAware {
// 只取两行核心代码
public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
//
ILoadBalancer lb = getLoadBalancer();
Server svc = lb.chooseServer(loadBalancerKey);
}
}
BaseLoadBalancer
public Server chooseServer(Object key) {
if (counter == null) {
counter = createCounter();
}
counter.increment();
if (rule == null) {
return null;
} else {
try {
// 最终,通过 IRule#choose 获取选择了一个 Server。
return rule.choose(key);
} catch (Exception e) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e);
return null;
}
}
}
这里逻辑嵌套地比较深,不要绕晕了哦。LoadBalancerCommand
里 submit 方法体中,获取到 Server 之后,执行了 operation.call(server)
,即下面这段逻辑:
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
try {
return command.submit(
new ServerOperation<T>() {
// 这段逻辑
@Override
public Observable<T> call(Server server) {
// 这里就是用 Server 服务端的真实 ip port 去替换服务名。
URI finalUri = reconstructURIWithServer(server, request.getUri());
// 填充为真实的 url 地址
S requestForServer = (S) request.replaceUri(finalUri);
try {
// 执行请求
return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
}
catch (Exception e) {
return Observable.error(e);
}
}
})
.toBlocking()
.single();
} catch (Exception e) {
// 。。。
}
}
@Override
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride) throws IOException {
Request.Options options;
// 该配置上的超时时间还得给配置上
if (configOverride != null) {
RibbonProperties override = RibbonProperties.from(configOverride);
options = new Request.Options(override.connectTimeout(connectTimeout),
TimeUnit.MILLISECONDS, override.readTimeout(readTimeout),
TimeUnit.MILLISECONDS, override.isFollowRedirects(followRedirects));
}else {
options = new Request.Options(connectTimeout, TimeUnit.MILLISECONDS,
readTimeout, TimeUnit.MILLISECONDS, followRedirects);
}
// 执行请求
Response response = request.client().execute(request.toRequest(), options);
return new RibbonResponse(request.getUri(), response);
}
至于 IRule 接口,就是由各使用方自行提供了,例如 Nacos 就是自行实现了
public class NacosRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
@Override
public Server choose(Object key) {
try {
String clusterName = this.nacosDiscoveryProperties.getClusterName();
String group = this.nacosDiscoveryProperties.getGroup();
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
String name = loadBalancer.getName();
NamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());
// 获取 服务端 实例
List<Instance> instances = namingService.selectInstances(name, group, true);
// ...
List<Instance> instancesToChoose = instances;
if (StringUtils.isNotBlank(clusterName)) {
List<Instance> sameClusterInstances = instances.stream()
.filter(instance -> Objects.equals(clusterName,instance.getClusterName()))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(sameClusterInstances)) {
instancesToChoose = sameClusterInstances;
} // ...
}
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);
return new NacosServer(instance);
}
catch (Exception e) {
// ...
}
}
}
执行请求
如果我们没有配置其他的HTTP底层,就是使用的默认的 HttpURLConnection 去发送请求。
// Client.Default
@Override
public Response execute(Request request, Options options) throws IOException {
HttpURLConnection connection = convertAndSend(request, options);
return convertResponse(connection, request);
}
但是 HttpURLConnection 的性能不是很好,所以我们平时也会将其替换为其他的 HTTP Client。
例如,通过配置 okhttp 更换为 okhttp 发起请求:
pom
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>xx.xx</version>
</dependency>
yml 与 配置
feign:
okhttp:
enabled: true
OkHttpClient
public final class OkHttpClient implements Client {
// ...
@Override
public feign.Response execute(feign.Request input, feign.Request.Options options)
throws IOException {
okhttp3.OkHttpClient requestScoped;
if (delegate.connectTimeoutMillis() != options.connectTimeoutMillis()
|| delegate.readTimeoutMillis() != options.readTimeoutMillis()
|| delegate.followRedirects() != options.isFollowRedirects()) {
requestScoped = delegate.newBuilder()
.connectTimeout(options.connectTimeoutMillis(), TimeUnit.MILLISECONDS)
.readTimeout(options.readTimeoutMillis(), TimeUnit.MILLISECONDS)
.followRedirects(options.isFollowRedirects())
.build();
} else {
requestScoped = delegate;
}
Request request = toOkHttpRequest(input);
Response response = requestScoped.newCall(request).execute();
return toFeignResponse(response, input).toBuilder().request(input).build();
}
}
小结
OpenFeign 的原理分析篇到这里基本结束了。
在这篇里,主要分析了 Spring Boot 项目里,OpenFeign 是如何启动的、以及 Feign 中是如何使用代理做增强。
最后简单地分析了下,执行请求以及负载均衡的流程。