一文看懂Openfeign服务调用原理

您所在的位置:网站首页 feign的实现原理 一文看懂Openfeign服务调用原理

一文看懂Openfeign服务调用原理

2024-05-17 04:27| 来源: 网络整理| 查看: 265

一、概述

OpenFeign是Spring Cloud 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。

OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

二、简单使用引入依赖代码语言:javascript复制 org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.cloud spring-cloud-starter-openfeign 编写服务引用代码语言:javascript复制@Component @Slf4j public class DemoAPIProxy implements InitializingBean { @Autowired private DemoAPI demoAPI; @Override public void afterPropertiesSet() throws Exception { log.info(this.demoAPI.hello()); } } @FeignClient(name = "openfeign-provider") @RequestMapping("demo") public interface DemoAPI { @GetMapping("hello") String hello(); }服务启动类代码语言:javascript复制@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(basePackages = {"org.example.api"}) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class,args); } }

这样我们就完成了openfeign的服务引用。

三、生成代理

开启Openfeign能力是通过EnableFeignClients来实现:

代码语言:javascript复制@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { String[] value() default {}; String[] basePackages() default {}; Class[] basePackageClasses() default {}; Class[] defaultConfiguration() default {}; Class[] clients() default {}; }

value是basePackages别名,指向basePackages,是通过类的方式定义扫描路径,defaultConfiguration是默认配置,clients通过具体类的方式指定扫描范围,与basePackages互斥,我们通常使用basePackages方式。

EnableFeignClients导入了FeignClientsRegistrar类,看一下类继承关系:

FeignClientsRegistrar本质是一个ImportBeanDefinitionRegistrar,并且持有环境变量和资源加载器的能力,FeignClientsRegistrar重写了registerBeanDefinitions方法,该方法在容器上下文刷新(启动时调用refresh)时被调用,调用时机此处不展开分析,我们看一下FeignClientsRegistrar#registerBeanDefinitions实现:

代码语言:javascript复制public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); }

先注册默认配置,然后注册FeignClient相关类,接着看注册默认配置:

代码语言:javascript复制//注册默认配置 private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { Map defaultAttrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); } } //将配置注册到容器中 private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { BeanDefinitionBuilder builder = BeanDefinitionBuilder .genericBeanDefinition(FeignClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); registry.registerBeanDefinition( name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); }

很简单,从EnableFeignClients注解中取出默认配置,然后注册成FeignClientSpecification类定义备用,FeignClientSpecification是包装了名称和配置的类:

代码语言:javascript复制class FeignClientSpecification implements NamedContextFactory.Specification { private String name; private Class[] configuration; }

接着看注册FeignClient:

代码语言:javascript复制public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader); Set basePackages; 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))); } 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); } } } }

先调用getScanner方法生成类扫描器:

代码语言:javascript复制protected ClassPathScanningCandidateComponentProvider getScanner() { return new ClassPathScanningCandidateComponentProvider(false, this.environment) { @Override protected boolean isCandidateComponent( AnnotatedBeanDefinition beanDefinition) { boolean isCandidate = false; if (beanDefinition.getMetadata().isIndependent()) { if (!beanDefinition.getMetadata().isAnnotation()) { isCandidate = true; } } return isCandidate; } }; }

扫描器初始条件限定为被注解标注的独立接口.

然后设置过滤条件为带FeignClient注解的接口,然后设置扫描包路径或者指定类集合,然后调用registerFeignClient方法执行注册逻辑:

代码语言:javascript复制private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map attributes) { String className = annotationMetadata.getClassName(); BeanDefinitionBuilder definition = BeanDefinitionBuilder .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); }

将属性处理后封装成FeignClientFactoryBean注册到上下文容器中,而它又是个FactoryBean:

在之前文章《@Autowired注解原理分析》有讲到对于FactoryBean类型的类定义,会在注入的时候调用getObject来返回具体实例,具体实现后边分析。@EnableFeignClient生成接口代理的时序图如下:

四、代理注入

继续用前边的例子来说:

代码语言:javascript复制@FeignClient(name = "openfeign-provider") @RequestMapping("demo") public interface DemoAPI { @GetMapping("hello") String hello(); } @Autowired private DemoAPI demoAPI;

为什么DemoAPI是二方服务定义的接口,消费方是看不到具体实现的,为什么可以直接拿来注入并且使用呢?

我们前边说被FeignClient标注的接口会包装成FeignClientFactoryBean注册到容器中,在使用的时候通过getObject来注入,那么我们来看一下FeignClientFactoryBean的getObject实现:

代码语言:javascript复制public Object getObject() throws Exception { return getTarget(); } T getTarget() { FeignContext context = this.applicationContext.getBean(FeignContext.class); Feign.Builder builder = feign(context); if (!StringUtils.hasText(this.url)) { if (!this.name.startsWith("http")) { this.url = "http://" + this.name; } else { this.url = this.name; } this.url += cleanPath(); return (T) loadBalance(builder, context, new HardCodedTarget(this.type, this.name, this.url)); } if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) { this.url = "http://" + this.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 unwrap client = ((LoadBalancerFeignClient) client).getDelegate(); } builder.client(client); } Targeter targeter = get(context, Targeter.class); return (T) targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url)); }

getObject会调用getTarget,先从容器上下文获取FeignContext实例,那么FeignContext是个什么东西呢?

代码语言:javascript复制public class FeignContext extends NamedContextFactory { public FeignContext() { super(FeignClientsConfiguration.class, "feign", "feign.client.name"); } }

他是创建Feign实例的工厂,继承于NamedContextFactory类(后边分析),那么FeignContext什么时候生成的呢?

来看另一个配置类:

代码语言:javascript复制@Configuration @ConditionalOnClass(Feign.class) @EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class }) public class FeignAutoConfiguration { @Autowired(required = false) private List configurations = new ArrayList(); @Bean public HasFeatures feignFeature() { return HasFeatures.namedFeature("Feign", Feign.class); } @Bean public FeignContext feignContext() { FeignContext context = new FeignContext(); context.setConfigurations(this.configurations); return context; } @Configuration @ConditionalOnClass(name = "feign.hystrix.HystrixFeign") protected static class HystrixFeignTargeterConfiguration { @Bean @ConditionalOnMissingBean public Targeter feignTargeter() { return new HystrixTargeter(); } } @Configuration @ConditionalOnMissingClass("feign.hystrix.HystrixFeign") protected static class DefaultFeignTargeterConfiguration { @Bean @ConditionalOnMissingBean public Targeter feignTargeter() { return new DefaultTargeter(); } } ...... }

FeignAutoConfiguration是Feign的自动配置类,创建了FeignContext,并且选择性创建了Targeter,以及省略掉的feignClient配置.

回到getTarget,获取到FeignContext之后,调用feign方法使用FeignContext生成Feign.Builder:

代码语言:javascript复制protected Feign.Builder feign(FeignContext context) { FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class); Logger logger = loggerFactory.create(this.type); // @formatter:off Feign.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); return builder; }

这里边从上下文里边获取Feign.Builder实例,那么它又是什么?什么时候生成的?

代码语言:javascript复制public static class Builder { private final List requestInterceptors = new ArrayList(); private Logger.Level logLevel = Logger.Level.NONE; private Contract contract = new Contract.Default(); private Client client = new Client.Default(null, null); private Retryer retryer = new Retryer.Default(); private Logger logger = new NoOpLogger(); private Encoder encoder = new Encoder.Default(); private Decoder decoder = new Decoder.Default(); private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default(); private ErrorDecoder errorDecoder = new ErrorDecoder.Default(); private Options options = new Options(); private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default(); private boolean decode404; private boolean closeAfterDecode = true; private ExceptionPropagationPolicy propagationPolicy = NONE; }

Builder是Feign的一个静态内部类,封装了Client、Retryer、Encoder、Decoder和InvocationHandlerFactory等相关组件.

在FeignClientsConfiguration配置类中定义和注入:

代码语言:javascript复制@Configuration public class FeignClientsConfiguration { @Autowired(required = false) private Logger logger; @Bean @ConditionalOnMissingBean public Decoder feignDecoder() { return new OptionalDecoder( new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); } @Bean @ConditionalOnMissingBean @ConditionalOnMissingClass("org.springframework.data.domain.Pageable") public Encoder feignEncoder() { return new SpringEncoder(this.messageConverters); } @Bean @ConditionalOnClass(name = "org.springframework.data.domain.Pageable") @ConditionalOnMissingBean public Encoder feignEncoderPageable() { return new PageableSpringEncoder(new SpringEncoder(this.messageConverters)); } @Bean @ConditionalOnMissingBean public Contract feignContract(ConversionService feignConversionService) { return new SpringMvcContract(this.parameterProcessors, feignConversionService); } @Bean public FormattingConversionService feignConversionService() { FormattingConversionService conversionService = new DefaultFormattingConversionService(); for (FeignFormatterRegistrar feignFormatterRegistrar : this.feignFormatterRegistrars) { feignFormatterRegistrar.registerFormatters(conversionService); } return conversionService; } @Bean @ConditionalOnMissingBean public Retryer feignRetryer() { return Retryer.NEVER_RETRY; } @Bean @Scope("prototype") @ConditionalOnMissingBean public Feign.Builder feignBuilder(Retryer retryer) { return Feign.builder().retryer(retryer); } ...... }

FeignClientsConfiguration中也定义了默认编码解码和重试组件,Builder的作用于为prototype(不同的二方服务不共享)。

回到feign方法,从容器中拿到Feign.Builder,然后再拿到编解码器和springmvc注解契约装配Builder并返回。

接着看getTarget方法,如果url没有内容,则会走到loadBalance方法,否则从FeignContext获取Targeter并执行

Targeter.target返回调用。其实loadBalance最终也会调用Targeter.target,所以我们直接看loadBalance实现:

代码语言:javascript复制protected T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget target) { Client client = getOptional(context, Client.class); if (client != null) { builder.client(client); 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-netflix-ribbon?"); }

首先从context中获取Client,可以理解成最终执行网络请求的客户端,可以自己实现也可以使用FeignAutoConfiguration中定义的ApacheHttpClient或者OkHttpClient,具体使用哪一种classpath中引入了哪种实现和相关依赖,ApacheHttpClient需要引入feign.httpclient.ApacheHttpClient和httpclient,而OkHttpClient需要引入feign.okhttp.OkHttpClient和OkHttp,看一下我们前边FeignAutoConfiguration代码中省略的Client相关配置:

ApacheHttpClient

代码语言:javascript复制@Configuration @ConditionalOnClass(ApacheHttpClient.class) @ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer") @ConditionalOnMissingBean(CloseableHttpClient.class) @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true) protected static class HttpClientFeignConfiguration { private final Timer connectionManagerTimer = new Timer( "FeignApacheHttpClientConfiguration.connectionManagerTimer", true); @Autowired(required = false) private RegistryBuilder registryBuilder; private CloseableHttpClient httpClient; @Bean @ConditionalOnMissingBean(HttpClientConnectionManager.class) public HttpClientConnectionManager connectionManager( ApacheHttpClientConnectionManagerFactory connectionManagerFactory, FeignHttpClientProperties httpClientProperties) { final HttpClientConnectionManager connectionManager = connectionManagerFactory .newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(), httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(), httpClientProperties.getTimeToLiveUnit(), this.registryBuilder); this.connectionManagerTimer.schedule(new TimerTask() { @Override public void run() { connectionManager.closeExpiredConnections(); } }, 30000, httpClientProperties.getConnectionTimerRepeat()); return connectionManager; } @Bean public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) { RequestConfig defaultRequestConfig = RequestConfig.custom() .setConnectTimeout(httpClientProperties.getConnectionTimeout()) .setRedirectsEnabled(httpClientProperties.isFollowRedirects()) .build(); this.httpClient = httpClientFactory.createBuilder() .setConnectionManager(httpClientConnectionManager) .setDefaultRequestConfig(defaultRequestConfig).build(); return this.httpClient; } @Bean @ConditionalOnMissingBean(Client.class) public Client feignClient(HttpClient httpClient) { return new ApacheHttpClient(httpClient); } @PreDestroy public void destroy() throws Exception { this.connectionManagerTimer.cancel(); if (this.httpClient != null) { this.httpClient.close(); } } }

OkHttpClient

代码语言:javascript复制@Configuration @ConditionalOnClass(OkHttpClient.class) @ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer") @ConditionalOnMissingBean(okhttp3.OkHttpClient.class) @ConditionalOnProperty("feign.okhttp.enabled") protected static class OkHttpFeignConfiguration { private okhttp3.OkHttpClient okHttpClient; @Bean @ConditionalOnMissingBean(ConnectionPool.class) public ConnectionPool httpClientConnectionPool( FeignHttpClientProperties httpClientProperties, OkHttpClientConnectionPoolFactory connectionPoolFactory) { Integer maxTotalConnections = httpClientProperties.getMaxConnections(); Long timeToLive = httpClientProperties.getTimeToLive(); TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit(); return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit); } @Bean public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) { Boolean followRedirects = httpClientProperties.isFollowRedirects(); Integer connectTimeout = httpClientProperties.getConnectionTimeout(); Boolean disableSslValidation = httpClientProperties.isDisableSslValidation(); this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation) .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) .followRedirects(followRedirects).connectionPool(connectionPool) .build(); return this.okHttpClient; } @PreDestroy public void destroy() { if (this.okHttpClient != null) { this.okHttpClient.dispatcher().executorService().shutdown(); this.okHttpClient.connectionPool().evictAll(); } } @Bean @ConditionalOnMissingBean(Client.class) public Client feignClient(okhttp3.OkHttpClient client) { return new OkHttpClient(client); } }

回到loadBalance,从上下文拿到Client后配置到Builder中,然后从上下文中拿到Targeter并调用target方法,Targeter实现有两个,分别是DefaultTargeter和HystrixTargeter,后者具有熔断和Fallback调用能力,我们直接看DefaultTargeter#target:

代码语言:javascript复制class DefaultTargeter implements Targeter { @Override public T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget target) { return feign.target(target); } }

调用Feign.Builder的target方法,继续看实现:

代码语言:javascript复制public T target(Target target) { return build().newInstance(target); }

先调用build返回Feign实例,然后调用newInstance方法,看build实现:

代码语言:javascript复制public Feign build() { SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); }

创建SynchronousMethodHandler.Factory和ParseHandlersByName然后封装成ReflectiveFeign(继承了Feign)并返回,然后看newInstance实现:

代码语言:javascript复制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))); } } 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; }

看到这里就比较清晰了,把接口中的方法和默认实现放到Map中然后使用InvocationHandlerFactory.Default()创建InvocationHandler,然后使用jdk动态代理生成接口的代理并返回,这里主要看一下Feign的InvocationHandler实现:

代码语言:javascript复制 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 { 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(); } return dispatch.get(method).invoke(args); } }

到这里二方服务定义的接口就被@EnableFeignClient通过动态代理的方式注入到了服务中,整流程大致如下:

五、服务调用

正如我们前边所说,通过@Autowired或者@Resource注入的时候,注入的是被封装之后的代理类实现,

jdk动态代理持有的是ReflectiveFeign.FeignInvocationHandler类型的InvocationHandler,那么具体调用的时候,会调用FeignInvocationHandler#invoke:

代码语言:javascript复制@Override 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(); } return dispatch.get(method).invoke(args); }

前几个判断分支都是调用了Object的基本方法,最后dispatch.get(method).invoke(args)才是接口的业务方法调用,dispatch的类型是Map,是接口中方法与MethodHandler的映射关系,而MethodHandler又被SynchronousMethodHandler.Factory封装成SynchronousMethodHandler(实现了MethodHandler):

代码语言:javascript复制public MethodHandler create(Target target, MethodMetadata md, RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder) { return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger, logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, decode404, closeAfterDecode, propagationPolicy); }

那么dispatch.get(method).invoke(args)最终调用的就是SynchronousMethodHandler#invoke:

代码语言:javascript复制 @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 { 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; } } }

可以看到调用的时候默认是带有重试能力,默认是5次,具体调用交给executeAndDecode来实现:

代码语言:javascript复制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); } .... //省略 }

核心是client.execute,我们选择性看一下OkHttpClient的实现:

代码语言:javascript复制 @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(); }

很明显,最终服务调用会委托给okhttp3.OkHttpClient来执行,然后组装结果和状态码返回调用,到这里openfeign的服务调用就分析完了,为了帮助理解和有更直观的概念,我们看一下服务调用时序图:

六、组件支撑FeignClientsRegistrar:扫描@FeignClient注解的接口并注册成BeanDefinition到应用容器FeignClientFactoryBean:注入远程服务接口代理实现FeignContext:维护服务维度上下文,存储服务调用相关工具Feign.Builder:用于构建服务调用的Client,维护服务调用相关组件(编解码器、重试组件、代理创建工厂等)Targeter:用于组装服务接口代理Feign:用于创建服务接口代理ReflectiveFeign:接口代理实现Client:封装网络调用工具,并提供请求调用能力(Default、ApacheHttpClient和OkHttpClient等)总结

本文分析了Openfeign的服务调用流程,通过源码从生成代理、代理注入和服务调用维度分析了服务调用的具体实现,后边接涉及到了整个流程中涉及到的一些核心概念和组件,中间涉及到负载均衡部分没有展开深入分析,如果感兴趣可以自己翻阅相关资料或者源码,希望通过本篇文章的介绍,对大家Openfeign的使用和原理理解所有帮助,如若文中有错误或者理解偏差的地方,希望联系作者及时修改。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3