首页 > 数据库 > 数据库开发技术 > feign构建与调用全流程分析
之前介绍了灰度方案以及实现,分析feign调用的时候,有点不太尽兴,这次再丰富一下。
首先,我们在feign调用时,使用了FeignClient注解。
#接口调用@FeignClient("demo-service")public interface DemoServiceFeginClient {}# 开启feign@EnableFeignClients(basePackages = {"com.yxkong.api"})public class ApiStarter {}
定位下发现注解位于
FeignClient和EnableFeignClients 位于Maven:org.springframework.cloudspring-cloud-openfeign-core2.2.8.RELEASE
我们看下EnableFeignClients的源码
//关键点,利用spring的动态注册bean的机制,在 Configuration配置加载分析 中已经详细解说public EnableFeignClients {class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAwarepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)//如果有配置defaultConfiguration 则注册到iocregisterDefaultConfiguration(metadata, registry);//根据配置扫描的feignClient,生成bean并注册到registryregisterFeignClients(metadata, registry);}public void registerFeignClients(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());//从EnableFeignClients查找配置的clientsfinal Class<?>[] clients = attrs == null ? null: (Class<?>[]) attrs.get("clients");if (clients == null || clients.length == 0) {ClassPathScanningCandidateComponentProvider scanner = getScanner();scanner.setResourceLoader(this.resourceLoader);//设置过滤条件FeignClientscanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));//在getBasePackages将所有的路径聚合Set<String> basePackages = getBasePackages(metadata);for (String basePackage : basePackages) {//将扫描到的所有FeignClient放入candidateComponentscandidateComponents.addAll(scanner.findCandidateComponents(basePackage));}}for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {//构建FeignClientFactoryBean注入ioc中registerFeignClient(registry, annotationMetadata, attributes);}}}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并注入到ioc中FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();factoryBean.setBeanFactory(beanFactory);// 注入对象的名称是配置的或者spring自动生成factoryBean.setName(name);factoryBean.setContextId(contextId);//对象类型就是FeignClient标注的对象本身(如此通过@Autowird能快速获取到对应的对象)factoryBean.setType(clazz);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));}//从FeignClientFactoryBean中获取构建的Feign对象return factoryBean.getObject();});BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,qualifiers);//最终将Feign对象注入到IOC中BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}}
我们看到FeignClientFactoryBean.getObject()中调用了getTarget()方法
public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,ApplicationContextAware, BeanFactoryAware {public Object getObject() {return getTarget();}<T> T getTarget() {FeignContext context = beanFactory != null? beanFactory.getBean(FeignContext.class): applicationContext.getBean(FeignContext.class);// 这块是关键,构建了一个Feign对象,在这里设置feign具备哪些功能Feign.Builder builder = feign(context);//没有配置url的时候走下面的流程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));}if (StringUtils.hasText(url) && !url.startsWith("http")) {url = "http://" + url;}String url = this.url + cleanPath();Client client = getOptional(context, Client.class);if (client != null) {if (client instanceof LoadBalancerFeignClient) {// not load balancing because we have a url,// but ribbon is on the classpath, so unwrapclient = ((LoadBalancerFeignClient) client).getDelegate();}if (client instanceof FeignBlockingLoadBalancerClient) {// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrapclient = ((FeignBlockingLoadBalancerClient) client).getDelegate();}if (client instanceof RetryableFeignBlockingLoadBalancerClient) {// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrapclient = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();}builder.client(client);}Targeter targeter = get(context, Targeter.class);return (T) targeter.target(this, builder, context,new HardCodedTarget<>(type, name, url));}protected Feign.Builder feign(FeignContext context) {FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);Logger logger = loggerFactory.create(type);// @formatter:offFeign.Builder builder = get(context, Feign.Builder.class)// required values.logger(logger).encoder(get(context, Encoder.class)).decoder(get(context, Decoder.class)).contract(get(context, Contract.class));// @formatter:on// 关键点configureFeign(context, builder);applyBuildCustomizers(context, builder);return builder;}protected void configureFeign(FeignContext context, Feign.Builder builder) {//配置FeignconfigureUsingConfiguration(context, builder);//从属性中获取配feign的属性configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()),builder);configureUsingProperties(properties.getConfig().get(contextId), builder);}protected void configureUsingConfiguration(FeignContext context,Feign.Builder builder) {主要包含:1,日志级别2,Retryer3,ErrorDecoder4,Request.Options5,RequestInterceptor6,QueryMapEncoder7,ExceptionPropagationPolicy 异常策略}protected <T> T loadBalance(Feign.Builder builder, FeignContext context,HardCodedTarget<T> target) {Client client = getOptional(context, Client.class);if (client != null) {builder.client(client);//Targeter是HystrixTargeterTargeter targeter = get(context, Targeter.class);//具体看HystrixTargeter.target 方法return targeter.target(this, builder, context, target);}//看这个自定义异常就能发现Client是spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer这两个里面的throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?");}}class HystrixTargeter implements Targeter {public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTarget<T> target) {return feign.target(target);}}public abstract class Feign {//构建了Feign的默认参数public static class Builder {private InvocationHandlerFactory invocationHandlerFactory =new InvocationHandlerFactory.Default();}public <T> T target(Target<T> target) {return build().newInstance(target);}public Feign build() {SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);ParseHandlersByName handlersByName =new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,errorDecoder, synchronousMethodHandlerFactory);//invocationHandlerFactory 在Builder中构建了默认InvocationHandlerFactory.Default()return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);}}public class ReflectiveFeign extends Feign {public <T> T newInstance(Target<T> target) {// targetToHandlersByName内构建了SynchronousMethodHandler和BuildTemplateByResolvingArgsMap<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);// factory 就是在Feign.build传入的,是Feign.Builder初始化的//在此create的时候ReflectiveFeign.FeignInvocationHandler(target, dispatch);InvocationHandler handler = factory.create(target, methodToHandler);//最后代理的handler是FeignInvocationHandlerT proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class<?>[] {target.type()}, handler);return proxy;}static class FeignInvocationHandler implements InvocationHandler {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {}}}//FeignClient 没有配置url,只有一个服务名public interface DemoServiceFeignClient {ResultBean hello();}
综上所述,在从FeignClientFactoryBean.getObject的时候,根据配置参数构建了一个Feign。
整个feign对象的生成流程如下:

这里又有两个点:
1,Client 用哪个?怎么注入的?
2,Targeter用哪个?
回过头来,继续看
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,HardCodedTarget<T> target) {//从spring的context中获取指定类型Client的实例Client client = getOptional(context, Client.class);if (client != null) {builder.client(client);//从spring的context中获取指定类型Targeter的实例//Targeter是HystrixTargeterTargeter targeter = get(context, Targeter.class);return targeter.target(this, builder, context, target);}//看这个自定义异常就能发现Client是spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer这两个里面的throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?");}

其中
Defult是feign的默认实现,内部使用HttpURLConnection 执行调用;
LoadBalancerFeignClient是在org.springframework.cloud.openfeign.ribbon包下;
剩下的两个在org.springframework.cloud.openfeign.loadbalancer 包下。
这种注入性的配置在springboot里一般都在configuration中。
看下spring.factorieswe文件
=\org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\org.springframework.cloud.openfeign.FeignAutoConfiguration,\org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration
AutoConfiguration 是有顺序的。
在FeignRibbonClientAutoConfiguration中,
//第一个就是HttpClientFeignLoadBalancedConfiguration@Import({ HttpClientFeignLoadBalancedConfiguration.class,OkHttpFeignLoadBalancedConfiguration.class,HttpClient5FeignLoadBalancedConfiguration.class,DefaultFeignLoadBalancedConfiguration.class })public class FeignRibbonClientAutoConfiguration {}
//没引入这个类,不执行//这里又导入了HttpClientclass HttpClientFeignLoadBalancedConfiguration {}//这个也没引入,不执行class OkHttpFeignLoadBalancedConfiguration {}//这个没引入,不执行class HttpClient5FeignLoadBalancedConfiguration {}
再看最后一个
(proxyBeanMethods = false)class DefaultFeignLoadBalancedConfiguration {public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,SpringClientFactory clientFactory) {return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,clientFactory);}}
到这,默认的Client 的注入有了是LoadBalancerFeignClient。
我们再看
@Configuration(proxyBeanMethods = false)@ConditionalOnClass(Feign.class)@EnableConfigurationProperties({ FeignClientProperties.class,FeignHttpClientProperties.class, FeignEncoderProperties.class })@Import(DefaultGzipDecoderConfiguration.class)public class FeignAutoConfiguration {@Configuration(proxyBeanMethods = false)@Conditional(DefaultFeignTargeterConditions.class)protected static class DefaultFeignTargeterConfiguration {@Bean@ConditionalOnMissingBeanpublic Targeter feignTargeter() {return new DefaultTargeter();}}@Configuration(proxyBeanMethods = false)@Conditional(FeignCircuitBreakerDisabledConditions.class)@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")//优先使用这个@ConditionalOnProperty(value = "feign.hystrix.enabled", havingValue = "true",matchIfMissing = true)protected static class HystrixFeignTargeterConfiguration {@Bean@ConditionalOnMissingBeanpublic Targeter feignTargeter() {return new HystrixTargeter();}}@Configuration(proxyBeanMethods = false)@ConditionalOnClass(CircuitBreaker.class)@ConditionalOnProperty(value = "feign.circuitbreaker.enabled", havingValue = "true")protected static class CircuitBreakerPresentFeignTargeterConfiguration {@Bean@ConditionalOnMissingBean@ConditionalOnBean(CircuitBreakerFactory.class)public Targeter circuitBreakerFeignTargeter(CircuitBreakerFactory circuitBreakerFactory) {return new FeignCircuitBreakerTargeter(circuitBreakerFactory);}}}
到这,默认的Targeter 是 HystrixTargeter。
我们再看
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,HardCodedTarget<T> target) {Client client = getOptional(context, Client.class);if (client != null) {builder.client(client);Targeter targeter = get(context, Targeter.class);//最后返回的是targeter.targetreturn targeter.target(this, builder, context, target);}//看这个自定义异常就能发现Client是spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer这两个里面的throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?");}//具体实现class HystrixTargeter implements Targeter {public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,FeignContext context, Target.HardCodedTarget<T> target) {//最终走的是feign.target()return feign.target(target);}}public abstract class Feign {public <T> T target(Target<T> target) {//build()构建了一个ReflectiveFeign,然后执行newInstancereturn build().newInstance(target);}public Feign build() {return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);}}public class ReflectiveFeign extends Feign {public <T> T newInstance(Target<T> target) {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初始化的流程分析完了。
然后我们断点验证下:

确实如分析所说。
Feign生成流程分析完毕。
接下来我们分析下Feign使用流程
@Autowiredprivate DemoServiceFeignClient demoServiceFeginClient;@GetMapping("hello")public ResultBean hello(){String msg = "api进入的版本号是:"+version;log.info(msg);return demoServiceFeginClient.hello();}
我们知道Autowired是通过类型查找的,所以这块一定会找到一个DemoServiceFeignClient的Proxy对象,Proxy的handler是FeignInvocationHandler
我们梳理下我们从上面得到的信息:
Feign使用的是: ReflectiveFeignProxy的源也是: ReflectiveFeignProxy的handler是: ReflectiveFeign.FeignInvocationHandlerhandler 持有的dispatch是:SynchronousMethodHandlertarget是:HardCodedTargetclient是:LoadBalancerFeignClient
我们看下handler的源码
static class FeignInvocationHandler implements InvocationHandler {//要执行的对象private final Target target;//方法和SynchronousMethodHandler的映射集合private final Map<Method, MethodHandler> dispatch;public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//具体执行的的是dispatch的invokereturn dispatch.get(method).invoke(args);}}final class SynchronousMethodHandler implements MethodHandler {public Object invoke(Object[] argv) throws Throwable {//构建一个RequestTemplate由ReflectiveFeign.BuildEncodedTemplateFromArgs,主要是解析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) {}continue;}}}Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {//执行所有RequestInterceptor并构建一个Request对象Request request = targetRequest(template);Response response;long start = System.nanoTime();try {//client是LoadBalancerFeignClient,我们看下源码response = client.execute(request, options);// ensure the request is set. TODO: remove in Feign 12response = response.toBuilder().request(request).requestTemplate(template).build();} catch (IOException e) {throw errorExecuting(request, e);}long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);CompletableFuture<Object> resultFuture = new CompletableFuture<>();asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,metadata.returnType(),elapsedTime);return resultFuture.join();}Request targetRequest(RequestTemplate template) {//遍历所有的拦截器并执行for (RequestInterceptor interceptor : requestInterceptors) {interceptor.apply(template);}//最终在return Request.create(this.method, this.url(), this.headers(), this.body, this);return target.apply(template);}}public final class RequestTemplate implements Serializable {public Request request() {if (!this.resolved) {throw new IllegalStateException("template has not been resolved.");}return Request.create(this.method, this.url(), this.headers(), this.body, this);}}public class LoadBalancerFeignClient implements Client {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);//构建RibbonRequestFeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(this.delegate, request, uriWithoutHost);// 获取配置信息IClientConfig requestConfig = getClientConfig(options, clientName);//找到return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();}}private FeignLoadBalancer lbClient(String clientName) {return this.lbClientFactory.create(clientName);}}public class CachingSpringLoadBalancerFactory {public FeignLoadBalancer create(String clientName) {// 缓存有直接返回FeignLoadBalancer client = this.cache.get(clientName);if (client != null) {return client;}IClientConfig config = this.factory.getClientConfig(clientName);//获取ILoadBalancer实例ILoadBalancer lb = this.factory.getLoadBalancer(clientName);ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,ServerIntrospector.class);//构建一个FeignLoadBalancerclient = this.loadBalancedRetryFactory != null? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,this.loadBalancedRetryFactory): new FeignLoadBalancer(lb, config, serverIntrospector);//缓存起来this.cache.put(clientName, client);return client;}}FeignLoadBalancer 继承了AbstractLoadBalancerAwareClient,最终executeWithLoadBalancerpublic abstract class AbstractLoadBalancerAwareClient<S extends ClientRequest, T extends IResponse> extends LoadBalancerContext implements IClient<S, T>, IClientConfigAware {public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException {LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);try {return command.submit(new ServerOperation<T>() {public Observable<T> call(Server server) {// 这个时候从注册中心里获取到了真实的调用urlURI 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) {Throwable t = e.getCause();if (t instanceof ClientException) {throw (ClientException) t;} else {throw new ClientException(e);}}}}
看下具体的执行流程

至于怎么去做负载,后期再分析。
原文作者:5ycode;如有侵权,请联系管理员删除。