- 前言
- 一、学习内容?
- 二、Feign和Ribbon的关系
- 2.1.核心类图
- 2.2.LoadBalancerContext
- 2.3.IClient
- 2.4.AbstractLoadBalancerAwareClient
- 2.5.FeignLoadBalancer
- 2.6.问题一总结
- 三 、基于源码探索Spring是如何实现Feign的
- 3.1目标
- 3.2 要跟的代码的整体流程图
- 3.3 @EnableFeignClients
- 3.4 FeignClientsRegistrar
- 3.5 FeignClientFactoryBean
- 3.6 HystrixTargeter
- 3.7 Feign
- 3.8 ReflectiveFeign
- 3.9 SynchronousMethodHandler
- 3.10 LoadBalancerFeignClient
- 3.11 问题二总结
前言
总所周知spring中负载均衡主要是基于ribbon实现的,而我们的项目中对负载均衡的处理大多用到的都是feign注解。对其内部的实现很好奇于是进行了学习并在此进行记录
一、学习内容?主要有两个疑问点。
1 feign是如何拥有负载均衡能力的,底层是如何调用ribbon的
2 feign的实现原理,在项目中对feign的应用就是@EnableFeignClients和@FeignClient注解,spring是如何做的动态代理
如图:橙色边框内部的类是ribbon领域的,蓝色边框内部是feign领域的。ribbon的负载均衡主要包含两部分,一是选择服务即在目标服务集群中选择一个服务,二是向选择的服务发送请求。上图中红框部分封装了ribbon对选择服务的实现,绿框中完成了对请求标准的定义。最终使用AbstractLoadBalancerAwareClient类已抽象模板的模式将负载均衡的两部流程分完美集合。最终feign的FeignLoadBanlance类继承了AbstractLoadBalancerAwareClient并对IClient(请求标准)做了实现。下面看看各个类的主要方法
该类是ribbon对外提供负载均衡的api类。有两个比较重要的api。1是选择服务,2是基于选择的服务拼接出最终的url
//一个类包含旨在用作负载平衡客户端的 API,它是此类的子类
public class LoadBalancerContext implements IClientConfigAware {
//负载均衡选择服务的api
public Server getServerFromLoadBalancer(@Nullable URI original, @Nullable Object loadBalancerKey) throws ClientException {
...省略代码
return new Server(host, port);
}
//基于选择的服务拼接出最终url的api
public URI reconstructURIWithServer(Server server, URI original) {
...省略代码
}
}
2.3.IClient
该类定义了一套请求标准,feign主要工作其实就是完成了对该类的实现
//可以执行单个请求的客户端 public interface IClient2.4.AbstractLoadBalancerAwareClient{ //执行请求并返回响应。 预计不重试,直接抛出所有异常 public T execute(S request, IClientConfig requestConfig) throws Exception; }
一个抽象类,内部定义了一个抽象模板,将ribbon的负载均衡和请求流程完美集合
//提供客户端与负载均衡器集成的抽象类 public abstract class AbstractLoadBalancerAwareClient2.5.FeignLoadBalancerextends LoadBalancerContext implements IClient, IClientConfigAware { //核心的模板方法 public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException { LoadBalancerCommandcommand = buildLoadBalancerCommand(request, requestConfig); try { //跟到submit方法中 //可以看到selectServer()方法,继续跟可以看到其实调用的就是 //LoadBalancerContext.getServerFromLoadBalancer方法 return command.submit( new ServerOperation () { @Override //这里的server已经是选择好的了 public Observable call(Server server) { //这里调用的就是LoadBalancerContext.reconstructURIWithServer //拼接出最终的url(把服务名换成ip和端口) URI finalUri = reconstructURIWithServer(server, request.getUri()); //这里就是对IClient标准的实现了 S requestForServer = (S) request.replaceUri(finalUri); try { //AbstractLoadBalancerAwareClient.this就是FeignLoadBalancer //FeignLoadBalancer完成了对IClient的实现 详见2.5 return Observable.just(AbstractLoadBalancerAwareClient.this. execute(requestForServer, requestConfig)); } catch (Exception e) { return Observable.error(e); } } }) .toBlocking() .single(); } catch (Exception e) { Throwable t = e.getCause(); if (t instanceof ClientException) { throw (ClientException) t; } else { throw new ClientException(e); } } } }
feign调用ribbon的关键类就是该类。该类通过继承AbstractLoadBalancerAwareClient并实现IClient接口完成了对ribbon负载均衡的封装。
public class FeignLoadBalancer extends AbstractLoadBalancerAwareClient2.6.问题一总结{ //完成最终的请求 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(this.connectTimeout), override.readTimeout(this.readTimeout)); } else { options = new Request.Options(this.connectTimeout, this.readTimeout); } //跟到execute里可以看到底层就是jdk的http请求 Response response = request.client().execute(request.toRequest(), options); return new RibbonResponse(request.getUri(), response); } //对IClient中ClientRequest的实现 protected static class RibbonRequest extends ClientRequest implements Cloneable { ...省略代码 } //对IClient中IResponse的实现 protected static class RibbonResponse implements IResponse { ...省略代码 } }
至此feign和ribbon的调用关系已经梳理清楚了。总结如下
1 ribbon提供一套负载均衡api类LoadBalancerContext
2 ribbon定义了一套请求标准IClient接口
3 ribbon提供了一个抽象类AbstractLoadBalancerAwareClient,通过一个模板方法将上边两点完美的封装到一起。
4 feign的FeignLoadBalancer类继承了AbstractLoadBalancerAwareClient并实现了IClient标准,最终拥有的负载均衡的能力
通过对第一个问题的探索,可以了解到feign的核心类就是FeignLoadBalancer。所以这次探索的目的有两个
1 找到feign的入口类
2 从入口类入手找到FeignLoadBalancer类
这个注解就是feign的入口。其源头来至springboot自动装配,这里不展开了。把这里当作起点
@import(FeignClientsRegistrar.class)//跟这个注册器
public @interface EnableFeignClients {
...省略代码
}
3.4 FeignClientsRegistrar
该类主要有如下几个功能
1 确认是否开启了feign的自动装配
2 找到系统中所有被@FeignClient修饰的接口 [这个注解中封装了BeanDefinition所需要的数据]
3 通过FeignClientFactoryBean类为每一个@FeignClient修饰的接口 创建代理类,并将代理类交由spring容器管理
class FeignClientsRegistrar
implements importBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
//入口方法
@Override
public void registerBeanDefinitions(Annotationmetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
//跟这个方法
registerFeignClients(metadata, registry);
}
//注册客户端
public void registerFeignClients(Annotationmetadata metadata,
BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set basePackages;
//看项目中有么有EnableFeignClients注解,是否开启了自动装配feign
Map attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class>[] clients = attrs == null ? null
: (Class>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getbasePackages(metadata);
}
else {
final Set clientClasses = new HashSet<>();
basePackages = new HashSet<>();
for (Class> clazz : clients) {
basePackages.add(ClassUtils.getPackageName(clazz));
clientClasses.add(clazz.getCanonicalName());
}
AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
@Override
protected boolean match(Classmetadata metadata) {
String cleaned = metadata.getClassName().replaceAll("\$", ".");
return clientClasses.contains(cleaned);
}
};
scanner.addIncludeFilter(
new AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
}
//这里在各个路径下找被@FeignClient修饰的类
for (String basePackage : basePackages) {
Set candidateComponents = scanner
.findCandidateComponents(basePackage);
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 attributes = annotationmetadata
.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//重点在这里 继续跟这里
registerFeignClient(registry, annotationmetadata, attributes);
}
}
}
}
//真正的注册客户端
private void registerFeignClient(BeanDefinitionRegistry registry,
Annotationmetadata annotationmetadata, Map attributes) {
String className = annotationmetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
//重点在这里
//FeignClientFactoryBean本质是一个FactoryBean
//需要了解spring的一些知识,这里不展开了直接说结论
//所有被@FeignClient修饰的接口对应的代理类其实就来自于FeignClientFactoryBean的getObject方法
//后面继续跟FeignClientFactoryBean的getObject方法
.genericBeanDefinition(FeignClientFactoryBean.class);
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
String contextId = getContextId(attributes);
definition.addPropertyValue("contextId", contextId);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = contextId + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
// null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) {
alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
}
3.5 FeignClientFactoryBean
该类的作用就是为所有被@FeignClient修饰的接口生成代理对象
class FeignClientFactoryBean implements FactoryBean3.6 HystrixTargeter
class HystrixTargeter implements Targeter {
@Override
public T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget target) {
...省略代码
//跟这里
return feign.target(target);
}
}
3.7 Feign
//Feign 的目的是简化针对假装宁静的 http api 的开发。
//在实现上,Feign 是一个生成targeted http api 的factory
public abstract class Feign {
public T target(Target target) {
//继续跟这里
return build().newInstance(target);
}
}
3.8 ReflectiveFeign
这个类里可以看到被@FeignClient修饰的接口对应的代理类的正在面目,到这代理类已经被找到了。
这些代理类会在项目启动时生成
public class ReflectiveFeign extends Feign {
@Override
public T newInstance(Target target) {
Map nameToHandler = targetToHandlersByName.apply(target);
Map methodToHandler = new linkedHashMap();
List defaultMethodHandlers = new linkedList();
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)));
}
}
//熟悉的jdk动态代理方法
//动态代理的核心代理逻辑其实就在InvocationHandler的实现类FeignInvocationHandler的invoke方法中
//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;
}
//重点FeignInvocationHandler 静态内部类
static class FeignInvocationHandler implements InvocationHandler {
private final Target target;
private final Map dispatch;
FeignInvocationHandler(Target target, Map dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
... 省略代码
// 重点在这里 跟这里
//dispatch.get(method)就是SynchronousMethodHandler类
//当@FeignClient修饰的接口方法被调用时,最终就会触发这个方法
return dispatch.get(method).invoke(args);
}
... 省略代码
}
}
3.9 SynchronousMethodHandler
该类是feign的另一个入口,当有feign客户端方法被调用时就会触发该类的invoke方法
final class SynchronousMethodHandler implements MethodHandler {
@Override
public Object invoke(Object[] argv) throws Throwable {
...省略代码
while (true) {
try {
//核心方法 跟这里
return executeAndDecode(template, options);
} catch (RetryableException e) {
... 省略代码
}
}
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 {
//重点 这里的client就是LoadBalancerFeignClient类
response = client.execute(request, options);
...省略代码
}
}
3.10 LoadBalancerFeignClient
该类和我们要找到目标类【FeignLoadBalancer】长得已经很像了^ ^
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);
//核心方法在这里
//问题1中ribbon对外提供的负载均衡api的名字就是executeWithLoadBalancer 参考2.4
//猜测lbClient(clientName)返回的就是我们要找的FeignLoadBalancer
return lbClient(clientName)
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
}
catch (ClientException e) {
IOException io = findIOException(e);
if (io != null) {
throw io;
}
throw new RuntimeException(e);
}
}
//重点 这里找到了我们要找的目标类
//到这里 spring对feign动态代理的实现主线流程已经跟完了
private FeignLoadBalancer lbClient(String clientName) {
return this.lbClientFactory.create(clientName);
}
}
3.11 问题二总结
1 当feign自动装配被开启时,spring会便利指定路径去找@FeignClient注释的接口
2 spring通过jdk动态代理来代理每一个被@FeignClient注释的接口,代理处理器采用FeignInvocationHandler
3 代理对象的方法被调用时就会触发SynchronousMethodHandler的invoke方法
4 最终可以跟到feignLoadBalancer.executeWithLoadBalancer方法与问题一完美呼应



