微服务-@FeignClient 与 Feign 隔离

FeignClient 扫描

FeignClientsRegistrar#registerBeanDefinitions

java 复制代码
public void registerBeanDefinitions(AnnotationMetadata metadata,
	BeanDefinitionRegistry registry) {
// 注册默认配置
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
java 复制代码
/**
*  默认配置描述如下
 * A custom <code>@Configuration</code> for all feign clients. Can contain override
 * <code>@Bean</code> definition for the pieces that make up the client, for instance
 * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
 *
 * @see FeignClientsConfiguration for the defaults
 * @return list of default configurations
 */
Class<?>[] defaultConfiguration() default {};

FeignClientsRegistrar#registerFeignClients

java 复制代码
ClassPathScanningCandidateComponentProvider scanner = getScanner();
			scanner.setResourceLoader(this.resourceLoader);
			scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
			Set<String> basePackages = getBasePackages(metadata);
			for (String basePackage : basePackages) {
				
// 获取候选类 被FeignClient标注			candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
			}

FeignClientsRegistrar#registerClientConfiguration

FeignClientsRegistrar#registerFeignClient

java 复制代码
// 这段逻辑会在真正获取对象的时候调用
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));
					}
				    // 获取实例对象的核心逻辑
					return factoryBean.getObject();
				});
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
		definition.setLazyInit(true);
		validate(attributes);

我们断点打在factoryBean.getObject() 追进去

java 复制代码
return (T) loadBalance(builder, context,
					new HardCodedTarget<>(type, name, url));

然后继续往下走:HystrixTargeter -> Feign -> ReflectiveFeign

java 复制代码
public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
    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);
    // JDK 动态代理创建了真正的对象  handler类为HystrixInvocationHandler implements InvocationHandler

    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
  }

调用的时候

java 复制代码
 HystrixCommand<Object> hystrixCommand =
        new HystrixCommand<Object>(setterMethodMap.get(method)) {
          @Override
          protected Object run() throws Exception {
            try {
             // 这里真正的去调用远程方法
              return HystrixInvocationHandler.this.dispatch.get(method).invoke(args);
            } catch (Exception e) {
              throw e;
            } catch (Throwable t) {
              throw (Error) t;
            }
          }
		  // 这里是异常处理
          @Override
          protected Object getFallback() {
            if (fallbackFactory == null) {
              return super.getFallback();
            }
            try {
              Object fallback = fallbackFactory.create(getExecutionException());
              Object result = fallbackMethodMap.get(method).invoke(fallback, args);
              if (isReturnsHystrixCommand(method)) {
                return ((HystrixCommand) result).execute();
              } else if (isReturnsObservable(method)) {
                // Create a cold Observable
                return ((Observable) result).toBlocking().first();
              } else if (isReturnsSingle(method)) {
                // Create a cold Observable as a Single
                return ((Single) result).toObservable().toBlocking().first();
              } else if (isReturnsCompletable(method)) {
                ((Completable) result).await();
                return null;
              } else if (isReturnsCompletableFuture(method)) {
                return ((Future) result).get();
              } else {
                return result;
              }
            } catch (IllegalAccessException e) {
              // shouldn't happen as method is public due to being an interface
              throw new AssertionError(e);
            } catch (InvocationTargetException | ExecutionException e) {
              // Exceptions on fallback are tossed by Hystrix
              throw new AssertionError(e.getCause());
            } catch (InterruptedException e) {
              // Exceptions on fallback are tossed by Hystrix
              Thread.currentThread().interrupt();
              throw new AssertionError(e.getCause());
            }
          }
        };

我们在调用的时候在选择实例NacosNamingService#selectInstances,发现会选择实例,这就是 OpenFeign 首次发起远程调用时,会很慢的原因。

Feign 隔离


源代码

org.springframework.cloud.openfeign.FeignClientFactoryBean#get

org.springframework.cloud.context.named.NamedContextFactory#getInstance

java 复制代码
public <T> T getInstance(String name, Class<T> type) {
	AnnotationConfigApplicationContext context = getContext(name);
	try {
		return context.getBean(type);
	}
	catch (NoSuchBeanDefinitionException e) {
		// ignore
	}
	return null;
}

org.springframework.cloud.context.named.NamedContextFactory#getContext

java 复制代码
protected AnnotationConfigApplicationContext getContext(String name) {
	if (!this.contexts.containsKey(name)) {
	synchronized (this.contexts) {
		if (!this.contexts.containsKey(name)) {
			this.contexts.put(name, createContext(name));
		}
	}
	}
	return this.contexts.get(name);
}

org.springframework.cloud.context.named.NamedContextFactory#createContext

java 复制代码
protected AnnotationConfigApplicationContext createContext(String name) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
		if (this.configurations.containsKey(name)) {
			for (Class<?> configuration : this.configurations.get(name)
					.getConfiguration()) {
				context.register(configuration);
			}
		}
		for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
			if (entry.getKey().startsWith("default.")) {
				for (Class<?> configuration : entry.getValue().getConfiguration()) {
					context.register(configuration);
				}
			}
		}
		context.register(PropertyPlaceholderAutoConfiguration.class,
				this.defaultConfigType);
		context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
				this.propertySourceName,
				Collections.<String, Object>singletonMap(this.propertyName, name)));
		if (this.parent != null) {
			// Uses Environment from parent as well as beans
			context.setParent(this.parent);
			// jdk11 issue
			// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
			context.setClassLoader(this.parent.getClassLoader());
		}
		context.setDisplayName(generateDisplayName(name));
		// 重点在这里,创建了一个迷你的上下文对象,并且刷新了迷你上下文对象进行实例化操作 这会影响OpenFeing的效率变慢
		context.refresh();
		return context;
	}
相关推荐
小小小妮子~5 分钟前
深入理解 MySQL 架构
数据库·mysql·架构
Tester_孙大壮21 分钟前
Python爬虫技术科普
开发语言·爬虫·python
诚威_lol_中大努力中1 小时前
关于pytorch3d的安装
人工智能·pytorch·python
GISer_Jing1 小时前
神经网络、深度学习、卷积神经网络
python
雪球不会消失了1 小时前
MVC架构模式
架构·mvc
onejason1 小时前
深度解析:利用Python爬虫获取亚马逊商品详情
前端·python
小王子10242 小时前
数据结构与算法Python版 二叉查找树
数据结构·python·算法·二叉查找树
编程阿布2 小时前
Python基础——多线程编程
java·数据库·python
又蓝2 小时前
使用 Python 操作 MySQL 数据库的实用工具类:MySQLHandler
数据库·python·mysql
dundunmm2 小时前
机器学习之pandas
人工智能·python·机器学习·数据挖掘·pandas