栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Fegin的学习笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Fegin的学习笔记

文章目录
  • 前言
  • 一、学习内容?
  • 二、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是如何做的动态代理

二、Feign和Ribbon的关系 2.1.核心类图


如图:橙色边框内部的类是ribbon领域的,蓝色边框内部是feign领域的。ribbon的负载均衡主要包含两部分,一是选择服务即在目标服务集群中选择一个服务,二是向选择的服务发送请求。上图中红框部分封装了ribbon对选择服务的实现,绿框中完成了对请求标准的定义。最终使用AbstractLoadBalancerAwareClient类已抽象模板的模式将负载均衡的两部流程分完美集合。最终feign的FeignLoadBanlance类继承了AbstractLoadBalancerAwareClient并对IClient(请求标准)做了实现。下面看看各个类的主要方法

2.2.LoadBalancerContext

该类是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 IClient {
	//执行请求并返回响应。 预计不重试,直接抛出所有异常
    public T execute(S request, IClientConfig requestConfig) throws Exception; 
}
2.4.AbstractLoadBalancerAwareClient

一个抽象类,内部定义了一个抽象模板,将ribbon的负载均衡和请求流程完美集合

//提供客户端与负载均衡器集成的抽象类
public abstract class AbstractLoadBalancerAwareClient extends LoadBalancerContext implements IClient, IClientConfigAware {
    //核心的模板方法
	public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {
        LoadBalancerCommand command = 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);
            }
        }
        
    }
}
2.5.FeignLoadBalancer

feign调用ribbon的关键类就是该类。该类通过继承AbstractLoadBalancerAwareClient并实现IClient接口完成了对ribbon负载均衡的封装。

public class FeignLoadBalancer extends
		AbstractLoadBalancerAwareClient {
		//完成最终的请求
		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 {
		 ...省略代码
  }
}
2.6.问题一总结

至此feign和ribbon的调用关系已经梳理清楚了。总结如下
1 ribbon提供一套负载均衡api类LoadBalancerContext
2 ribbon定义了一套请求标准IClient接口
3 ribbon提供了一个抽象类AbstractLoadBalancerAwareClient,通过一个模板方法将上边两点完美的封装到一起。
4 feign的FeignLoadBalancer类继承了AbstractLoadBalancerAwareClient并实现了IClient标准,最终拥有的负载均衡的能力

三 、基于源码探索Spring是如何实现Feign的 3.1目标

通过对第一个问题的探索,可以了解到feign的核心类就是FeignLoadBalancer。所以这次探索的目的有两个
1 找到feign的入口类
2 从入口类入手找到FeignLoadBalancer类

3.2 要跟的代码的整体流程图

3.3 @EnableFeignClients

这个注解就是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 FactoryBean, InitializingBean, ApplicationContextAware {
		
    //入口方法
    @Override
	public Object getObject() throws Exception {
		return getTarget();
	}

	
	 T getTarget() {
		...省略代码
		Targeter targeter = get(context, Targeter.class);
		//重点在这里,看到targeter感觉这里距离动态代理的真相不远了,继续跟这里
		return (T) targeter.target(this, builder, context,
				new HardCodedTarget<>(this.type, this.name, url));
	}
    
}
 
3.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方法与问题一完美呼应

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号