SpringCloud源码解析-gatewayopenFeign

news2024/11/26 19:24:42

SpringCloud高级应用-源码解析

  • 1. gateway 源码解析
    • 1.1 自动装配
    • 1.2 核心装配
      • 1.2.1 GatewayClassPathWarningAutoConfiguration
      • 1.2.2 GatewayAutoConfiguration
      • 1.2.3 GatewayLoadBalancerClientAutoConfiguration
      • 1.2.4 GatewayRedisAutoConfiguration
    • 1.3 Gateway 工作机制
      • 1.3.1 Gateway的三个核心组件
      • 1.3.2 请求处理流程(图)
      • 1.3.3 入口&流程
      • 1.3.4 主要过滤器
        • 1.3.4.1 RouteToRequestUrlFilter(全局处理区)
        • 1.3.4.2 LoadBalancerClientFilter(负载均衡)
        • 1.3.4.3 LoadBalancerClientFilter(远程调用)
  • 2. open Feign 源码解析
    • 2.1 openfeign 基础模型
    • 2.2 入口&流程
      • 2.2.* feign请求调用

1. gateway 源码解析

1.1 自动装配

springcloud是基于springboot的,gateway各个组件的初始化入口在自动装配
在这里插入图片描述

1.2 核心装配

1.2.1 GatewayClassPathWarningAutoConfiguration

主要作用是校验依赖 在GatewayAutoConfiguration之前被加载

校验是否导入了spring-boot-starter-web, gateway非web容器,不需要导入 spring-boot-starter-web
校验是否缺少spring-boot-starter-webflux依赖, DispatcherHandler是gateway接收请求的入口组件

@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(GatewayAutoConfiguration.class)
public class GatewayClassPathWarningAutoConfiguration {

	private static final Log log = LogFactory
			.getLog(GatewayClassPathWarningAutoConfiguration.class);

	private static final String BORDER = "\n\n**********************************************************\n\n";


	/**
	 * 非web容器,不需要导入 spring-boot-starter-web
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
	protected static class SpringMvcFoundOnClasspathConfiguration {

		public SpringMvcFoundOnClasspathConfiguration() {
			log.warn(BORDER
					+ "Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. "
					+ "Please remove spring-boot-starter-web dependency." + BORDER);
		}

	}

	/**
	 * 判断是否缺少  spring-boot-starter-webflux 依赖
	 */
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.springframework.web.reactive.DispatcherHandler")
	protected static class WebfluxMissingFromClasspathConfiguration {

		public WebfluxMissingFromClasspathConfiguration() {
			log.warn(BORDER + "Spring Webflux is missing from the classpath, "
					+ "which is required for Spring Cloud Gateway at this time. "
					+ "Please add spring-boot-starter-webflux dependency." + BORDER);
		}

	}

}

1.2.2 GatewayAutoConfiguration

  1. 网关的开启与关闭
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
  1. 初始化 NettyConfiguration
/**
 * 初始化 NettyConfiguration
 */

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HttpClient.class)
protected static class NettyConfiguration {
	...
}
  1. 初始化 GlobalFilter
	// GlobalFilter beans  初始化各种内置的  GlobalFilter

	@Bean
	public AdaptCachedBodyGlobalFilter adaptCachedBodyGlobalFilter() {
		return new AdaptCachedBodyGlobalFilter();
	}
  1. 初始化 FilteringWebHandler
@Bean
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
	return new FilteringWebHandler(globalFilters);
}
  1. 初始化 GatewayProperties
  2. 初始化 PrefixPathGatewayFilterFactory
  3. 初始化 RoutePredicateFactory
  4. 初始化 RouteDefinitionLocator
  5. 初始化 RouteLocator
  6. 初始化 RoutePredicateHandlerMapping
  7. 初始化 RoutePredicateHandlerMapping
    以上是核心组件GatewayAutoConfiguration的内容, 部分就不介绍了, 都是一些初始化的工作

1.2.3 GatewayLoadBalancerClientAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({LoadBalancerClient.class, RibbonAutoConfiguration.class, DispatcherHandler.class})
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@EnableConfigurationProperties(LoadBalancerProperties.class)
public class GatewayLoadBalancerClientAutoConfiguration {

	@Bean
	@ConditionalOnBean(LoadBalancerClient.class)
	@ConditionalOnMissingBean({LoadBalancerClientFilter.class, ReactiveLoadBalancerClientFilter.class})
	public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client,
															 LoadBalancerProperties properties) {
		return new LoadBalancerClientFilter(client, properties);
	}

}

内容比较简单, 初始化负载均衡过滤器, 在RibbonAutoConfiguration在后加载

1.2.4 GatewayRedisAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(RedisReactiveAutoConfiguration.class)
@AutoConfigureBefore(GatewayAutoConfiguration.class)
@ConditionalOnBean(ReactiveRedisTemplate.class)
@ConditionalOnClass({ RedisTemplate.class, DispatcherHandler.class })
class GatewayRedisAutoConfiguration {

	@Bean
	@SuppressWarnings("unchecked")
	public RedisScript redisRequestRateLimiterScript() {
		DefaultRedisScript redisScript = new DefaultRedisScript<>();
		redisScript.setScriptSource(new ResourceScriptSource(
				new ClassPathResource("META-INF/scripts/request_rate_limiter.lua")));
		redisScript.setResultType(List.class);
		return redisScript;
	}

	@Bean
	@ConditionalOnMissingBean
	public RedisRateLimiter redisRateLimiter(ReactiveStringRedisTemplate redisTemplate,
			@Qualifier(RedisRateLimiter.REDIS_SCRIPT_NAME) RedisScript<List<Long>> redisScript,
			ConfigurationService configurationService) {
		return new RedisRateLimiter(redisTemplate, redisScript, configurationService);
	}

}

Redis整合Lua脚本实现网管的限流

1.3 Gateway 工作机制

在这里插入图片描述

  1. Gateway 接收客户端请求
  2. 客户端请求与路由信息进行匹配,匹配成功的才能够被路由转发到相应的下游服务
  3. 请求经过 Filter 过滤器链,执行 pre 处理逻辑,如修改请求头信息等
  4. 请求被转发至下游服务并返回响应
  5. 响应经过 Filter 过滤器链,执行 post 处理逻辑
  6. 向客户端响应应答

1.3.1 Gateway的三个核心组件

Route(路由) RoutePredicate(路由断言) GatewayFilter(过滤器)

在这里插入图片描述

1.3.2 请求处理流程(图)

在这里插入图片描述

1.3.3 入口&流程

入口:spring-web:ReactorHttpHandlerAdapter.apply()

public class ReactorHttpHandlerAdapter implements BiFunction<HttpServerRequest, HttpServerResponse, Mono<Void>> {

	private static final Log logger = HttpLogging.forLogName(ReactorHttpHandlerAdapter.class);


	private final HttpHandler httpHandler;


	public ReactorHttpHandlerAdapter(HttpHandler httpHandler) {
		Assert.notNull(httpHandler, "HttpHandler must not be null");
		this.httpHandler = httpHandler;
	}


	@Override
	public Mono<Void> apply(HttpServerRequest reactorRequest, HttpServerResponse reactorResponse) {
		NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(reactorResponse.alloc());
		try {
			ReactorServerHttpRequest request = new ReactorServerHttpRequest(reactorRequest, bufferFactory);
			ServerHttpResponse response = new ReactorServerHttpResponse(reactorResponse, bufferFactory);

			if (request.getMethod() == HttpMethod.HEAD) {
				response = new HttpHeadResponseDecorator(response);
			}

			return this.httpHandler.handle(request, response)
					.doOnError(ex -> logger.trace(request.getLogPrefix() + "Failed to complete: " + ex.getMessage()))
					.doOnSuccess(aVoid -> logger.trace(request.getLogPrefix() + "Handling completed"));
		}
		catch (URISyntaxException ex) {
			if (logger.isDebugEnabled()) {
				logger.debug("Failed to get request URI: " + ex.getMessage());
			}
			reactorResponse.status(HttpResponseStatus.BAD_REQUEST);
			return Mono.empty();
		}
	}

}

对原始HttpServerRequest 和HttpServerResponse 进行封装, 交由HttpWebHandlerAdapter进行处理, 如下

@Override
public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) {
	if (this.forwardedHeaderTransformer != null) {
		request = this.forwardedHeaderTransformer.apply(request);
	}
	ServerWebExchange exchange = createExchange(request, response);

	LogFormatUtils.traceDebug(logger, traceOn ->
			exchange.getLogPrefix() + formatRequest(exchange.getRequest()) +
					(traceOn ? ", headers=" + formatHeaders(exchange.getRequest().getHeaders()) : ""));

	return getDelegate().handle(exchange)
			.doOnSuccess(aVoid -> logResponse(exchange))
			.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
			.then(Mono.defer(response::setComplete));
}

ServerWebExchange web上下文对象, 贯穿整个请求; getDelegate()获取的是ExceptionHandlingWebHandler(全局异常处理器)
最终交由DispatcherHandler.handle进行处理, 参考**请求处理流程(图)**的第一个缓解, 该包是spring-webflux下的

public class DispatcherHandler implements WebHandler, ApplicationContextAware {
	...
	@Override
	public Mono<Void> handle(ServerWebExchange exchange) {
		if (this.handlerMappings == null) {
			return createNotFoundError();
		}
		return Flux.fromIterable(this.handlerMappings)
				.concatMap(mapping -> mapping.getHandler(exchange))
				.next()
				.switchIfEmpty(createNotFoundError())
				.flatMap(handler -> invokeHandler(exchange, handler))
				.flatMap(result -> handleResult(exchange, result));
	}
	...
}

this.handlerMappings加载所有的处理器mapping, 通过mapping(最终获取的是路由断言的mapping)获取handler
在这里插入图片描述
RoutePredicateHandlerMapping.getHandler 在父类AbstractHandlerMapping#getHandler
继续查看RoutePredicateHandlerMapping.getHandler—>getHandlerInternal

protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
	// don't handle requests on management port if set and different than server port
	if (this.managementPortType == DIFFERENT && this.managementPort != null
			&& exchange.getRequest().getURI().getPort() == this.managementPort) {
		return Mono.empty();
	}
	exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());

	return lookupRoute(exchange) // 查找route
			// .log("route-predicate-handler-mapping", Level.FINER) //name this
			.flatMap((Function<Route, Mono<?>>) r -> {
				exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
				if (logger.isDebugEnabled()) {
					logger.debug(
							"Mapping [" + getExchangeDesc(exchange) + "] to " + r);
				}

				exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
				return Mono.just(webHandler);
			}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
				exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
				if (logger.isTraceEnabled()) {
					logger.trace("No RouteDefinition found for ["
							+ getExchangeDesc(exchange) + "]");
				}
			})));
}

RoutePredicateHandlerMapping.lookupRoute获取路由信息, 也就是配置文件里面routes下面配置的路由
getPredicate().apply(exchange)路由断言对路由进行过滤

	protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
		// 拿到所有的route
		return this.routeLocator.getRoutes()
				// 单独过滤路由,这样filterWhen错误延迟就不是问题
				.concatMap(route -> Mono.just(route).filterWhen(r -> {
					// add the current route we are testing
					exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
					// 进行路由断言 Predicate
					return r.getPredicate().apply(exchange);
				})...

		/*
		 * TODO: trace logging if (logger.isTraceEnabled()) {
		 * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
		 */
	}

gateway:AbstractHandlerMapping#getHandler 获得handler之后(最终获得的handler是FilteringWebHandler,里面包含了globalFilters)

然后执行handler, 处理结果, 主要含执行hander部分, DispatcherHandler中的invokeHandler() 方法

private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) {
	if (this.handlerAdapters != null) {
		for (HandlerAdapter handlerAdapter : this.handlerAdapters) {
			if (handlerAdapter.supports(handler)) {
				return handlerAdapter.handle(exchange, handler);
			}
		}
	}
	return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler));
}

继续往下走, SimpleHandlerAdapter执行handle, 传入上下文(exchange)以及FilteringWebHandler, 方法内部比较简单, 继续调用FilteringWebHandler的handle方法,

public class SimpleHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return WebHandler.class.isAssignableFrom(handler.getClass());
	}

	@Override
	public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
		WebHandler webHandler = (WebHandler) handler;
		Mono<Void> mono = webHandler.handle(exchange);
		return mono.then(Mono.empty());
	}

}
public class FilteringWebHandler implements WebHandler {
	...
	@Override
	public Mono<Void> handle(ServerWebExchange exchange) {
		Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
		List<GatewayFilter> gatewayFilters = route.getFilters();
		List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
		combined.addAll(gatewayFilters);
		// TODO: needed or cached?
		AnnotationAwareOrderComparator.sort(combined);
		if (logger.isDebugEnabled()) {
			logger.debug("Sorted gatewayFilterFactories: " + combined);
		}
		// 创建过滤器链并执行过滤器
		return new DefaultGatewayFilterChain(combined).filter(exchange);
	}
	...
}

以上可以看出将普通过滤器和全局过滤器进行合并成过滤器链, 然后执行过滤器
接下来看FilteringWebHandler.DefaultGatewayFilterChain(内部类)#filter方法, 执行过滤器

	private static class DefaultGatewayFilterChain implements GatewayFilterChain {

		private final int index;
		// 整合过后的所有过滤器
		private final List<GatewayFilter> filters;

		DefaultGatewayFilterChain(List<GatewayFilter> filters) {
			this.filters = filters;
			this.index = 0;
		}

		private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
			this.filters = parent.getFilters();
			this.index = index;
		}

		public List<GatewayFilter> getFilters() {
			return filters;
		}
		
		// 执行过滤器
		@Override
		public Mono<Void> filter(ServerWebExchange exchange) {
			return Mono.defer(() -> {
				if (this.index < filters.size()) {
					GatewayFilter filter = filters.get(this.index);
					DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this,
							this.index + 1);
					return filter.filter(exchange, chain);
				}
				else {
					return Mono.empty(); // complete
				}
			});
		}

	}

filter.filter(exchange, chain)依次执行过滤器, 前端获取过滤器时会对过滤器进行排序(实现了Order接口)

局部过滤器(GatewayFilter):应用到单个路由或者一个分组的路由上。
全局过滤器(GlobalFilter):应用到所有的路由上。
在组装过滤器链时会将GlobalFilter通过GatewayFilterAdapter(GatewayFilterAdapter implements GatewayFilter)转换成GatewayFilter进行链路调用

1.3.4 主要过滤器

1.3.4.1 RouteToRequestUrlFilter(全局处理区)

RouteToRequestUrlFilter根据路由查找服务
public class RouteToRequestUrlFilter implements GlobalFilter, Ordered

	/**
	 * 真实服务查找
	 * @param exchange the current server exchange
	 * @param chain provides a way to delegate to the next filter
	 * @return
	 */
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); //获取当前的route
		if (route == null) {
			return chain.filter(exchange);
		}
		log.trace("RouteToRequestUrlFilter start");
		//得到uri  = http://localhost:8001/driver/info/1?token=123456
		URI uri = exchange.getRequest().getURI();
		
		... 代码省略

		if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {
			// Load balanced URIs should always have a host. If the host is null it is
			// most
			// likely because the host name was invalid (for example included an
			// underscore)
			throw new IllegalStateException("Invalid host: " + routeUri.toString());
		}
		//将uri换成 lb://hailtaxi-driver/driver/info/1?token=123456
		URI mergedUrl = UriComponentsBuilder.fromUri(uri)
				// .uri(routeUri)
				.scheme(routeUri.getScheme()).host(routeUri.getHost())
				.port(routeUri.getPort()).build(encoded).toUri();
		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, mergedUrl);
		// 继续下一个过滤器
		return chain.filter(exchange);
	}

1.3.4.2 LoadBalancerClientFilter(负载均衡)

通过负载均衡得到真实服务的实例

	@Override
	@SuppressWarnings("Duplicates")
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); // url:lb://hailtaxi-driver/driver/info/1?token=123456
		String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
		if (url == null
				|| (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
			return chain.filter(exchange);
		}
		// preserve the original url
		addOriginalRequestUrl(exchange, url);

		if (log.isTraceEnabled()) {
			log.trace("LoadBalancerClientFilter url before: " + url);
		}
		// 负载均衡选择服务实例
		final ServiceInstance instance = choose(exchange);

		if (instance == null) {
			throw NotFoundException.create(properties.isUse404(),
					"Unable to find instance for " + url.getHost());
		}
		//用户提交的URI = http://localhost:8001/driver/info/1?token=123456
		URI uri = exchange.getRequest().getURI();

		// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
		// if the loadbalancer doesn't provide one.
		String overrideScheme = instance.isSecure() ? "https" : "http";
		if (schemePrefix != null) {
			overrideScheme = url.getScheme();
		}
		// 真正要请求的url = http://172.16.17.251:18081/driver/info/1?token=123456
		URI requestUrl = loadBalancer.reconstructURI(
				new DelegatingServiceInstance(instance, overrideScheme), uri);

		if (log.isTraceEnabled()) {
			log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
		}
		// 将真正要请求的url设置到上下文中
		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
		return chain.filter(exchange);
	}

1.3.4.3 LoadBalancerClientFilter(远程调用)

代码太多就不说了, 主要是通过上面过滤器得到的服务实例, 发起远程调用的过程

2. open Feign 源码解析

2.1 openfeign 基础模型

对于使用openfeign而言,我们将对服务的http调用转换成对接口方法的调用。涉及的相关技术栈有:代理,http请求响应…
在这里插入图片描述

2.2 入口&流程

入口在@EnableFeignClients, 其中主要的是@Import注解中的FeignClientsRegistrar, 也就是open feign初始化的入口

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class) // 开启了 `FeignClient`扫描
public @interface EnableFeignClients {

接着进入FeignClientsRegistrar看, 关注其 registerBeanDefinitions 方法

/**
 *  ImportBeanDefinitionRegistrar:用于向容器中注入bean  关注其 registerBeanDefinitions 方法
 * @author Spencer Gibb
 * @author Jakub Narloch
 * @author Venil Noronha
 * @author Gang Li
 */
class FeignClientsRegistrar  implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
	...
		/**
	 * bean注入的 入口函数
	 * @param metadata
	 * @param registry
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		// 完成 Feign 框架相关的配置注册
		registerDefaultConfiguration(metadata, registry);
		// 注册由 @FeignClient 修饰的接口 bean *****核心*****
		registerFeignClients(metadata, registry);
	}
	...
}

继续进入registerFeignClients

/**
	 * 向容器中注册由 @FeignClient 修饰的接口bean
	 * @param metadata  包含了@EnableFeignClients 注解的元信息
	 * @param registry
	 */
	public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		// ClassPath Scanner
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		scanner.setResourceLoader(this.resourceLoader);
		// 接收 @EnableFeignClients(basePackages = {"com.itheima.driver.feign"})
		Set<String> basePackages;
		Map<String, Object> 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);
			// 获取 @EnableFeignClients中配置的 @FeignClient 接口扫描路径
			basePackages = getBasePackages(metadata);
		}else {
			... 代码省略
		}

		// 拿到  @EnableFeignClients 中配置的 @FeignClient 接口扫描路径 后开始 扫描
		for (String basePackage : basePackages) {
			// 查找 basePackage 包路径下所有 由 @FeignClient 修饰的候选bean,返回其 BeanDefinition 的集合
			Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);

			// 针对每个标注了 @FeignClient 的候选 BeanDefinition (接口的BeanDefinition) 准备向容器中注册
			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");// @FeignClient标注的必须是接口
					// 获取 @FeignClient 注解的相关属性信息
					Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());
					// 获取@FeignClient(value = "hailtaxi-driver"),name属性,name属性和value属性是相同的含义,都是配置服务名
					String name = getClientName(attributes);// name = hailtaxi-driver
					registerClientConfiguration(registry, name, attributes.get("configuration"));
					//  针对当前标注了 @FeignClient 注解的候选接口 BeanDefinition   向容器中注册bean信息
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}

这里面有一个有意思的就是通过spring的扫描器, 来扫描我们自定义的注解的接口

继续进入registerFeignClient(registry, annotationMetadata, attributes), 注册标注了 @FeignClient 注解的候选接口 BeanDefinition到spring容器

/**
 * 向容器中注册 每个标注了 @FeignClient 的接口bean
 * @param registry
 * @param annotationMetadata
 * @param attributes
 */
private void registerFeignClient(BeanDefinitionRegistry registry,
								 AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
	// 接口全路径
	String className = annotationMetadata.getClassName();// className=com.demo.driver.feign.DriverFeign
	/**
	 * 每个标注了@FeignClient 的接口,真正向容器中注册的其实是一个 FeignClientFactoryBean(实现了FactoryBean, 在dubbo中讲过)
	 * 1、创建 FeignClientFactoryBean的 BeanDefinition
	 * 2、向 BeanDefinition 中填充相关属性,属性来源于接口上@FeignClient的属性信息
	 */
	BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
	validate(attributes);// 验证fallback和fallbackFactory是不是接口
	
	// 添加发起请求需要的元数据信息
	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
	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);
	/**
	 * 由于标注了@FeignClient的每个接口真正向容器中注册时注册的是与该接口相关的:FeignClientFactoryBean
	 * 而 FeignClientFactoryBean 实现了 FactoryBean 接口,也就是说当需要从容器中获取 这个bean时,获取出来的bean其实是由它的getObject方法返回的bean
	 *
	 * 所以:下一个入口是: FeignClientFactoryBean#getObject
	 */

}

生成接口代理的逻辑在FeignClientFactoryBean中, 也就是上面BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class), 关注FeignClientFactoryBean.getObject->getTarget

/**
 * 代理创建的入口,返回接口真正的实例
 * @return
 * @throws Exception
 */

@Override
public Object getObject() throws Exception {
	return getTarget();
}

<T> T getTarget() {
	/**
	 * FeignContext注册到容器是在 FeignAutoConfiguration 上完成的; 在初始化FeignContext时,会把 configurations 放入FeignContext中。
	 * configurations 的来源就是在前面 registerFeignClients 方法中 @FeignClient的配置 configuration。
	 */
	FeignContext context = this.applicationContext.getBean(FeignContext.class);
	//构建出Builder对象 用于构造代理对象,builder将会构建出 feign 的代理,
	Feign.Builder builder = feign(context);// builder= Feign$Builder
	if (!StringUtils.hasText(this.url)) { // 没在@FigenClient注解中配 url
		if (!this.name.startsWith("http")) {
			this.url = "http://" + this.name;
		}else {
			this.url = this.name;
		}
		this.url += cleanPath(); // this.url= http://hailtaxi-driver
		/**
		 * HardCodedTarget里封装了:接口type Class,服务名称,服务地址url ; 根据 Feign.Builder ,FeignContext,HardCodedTarget 构建 返回的对象
		 */
		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();
		}
		if (client instanceof FeignBlockingLoadBalancerClient) {
			// not load balancing because we have a url,
			// but Spring Cloud LoadBalancer is on the classpath, so unwrap
			client = ((FeignBlockingLoadBalancerClient) 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));
}

在这里插入图片描述

因为没在@FigenClient注解中配 url, 所以会进入!StringUtils.hasText(this.url)中
FeignClientFactoryBean#loadBalance

protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
	// 获取feign 客户端,得到的是:LoadBalancerFeignClient  loadbalance基于 RibbonLoadBalanced  用于执行http请求
	Client client = getOptional(context, Client.class);
	if (client != null) {
		// 将 feign 的 Client 对象设置进 Feign.Builder
		builder.client(client);
		// Targeter默认是 HystrixTargeter 在 FeignAutoConfiguration 中有配置
		Targeter targeter = get(context, Targeter.class);
		/**
		 * 实例创建(开启熔断后具有熔断降级效果)
		 * this= FeignClientFactoryBean
		 * builder= Feign$Builder
		 * context = FeignContext
		 * target = HardCodedTarget
		 */
		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?");
}

HystrixTargeter#target—>Feign.Builder#target

class HystrixTargeter implements Targeter {

	@Override
	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
			FeignContext context, Target.HardCodedTarget<T> target) {
		// 默认情况下 feign传递是:Feign$Builder
		if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
			return feign.target(target); // 直接进入到 Feign$Builder#target 方法中查看
		}
		feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
		String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()  : factory.getContextId();
		SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
		if (setterFactory != null) {
			builder.setterFactory(setterFactory);
		}
		Class<?> fallback = factory.getFallback();
		if (fallback != void.class) {
			return targetWithFallback(name, context, target, builder, fallback);
		}
		Class<?> fallbackFactory = factory.getFallbackFactory();
		if (fallbackFactory != void.class) {
			return targetWithFallbackFactory(name, context, target, builder,fallbackFactory);
		}

		return feign.target(target);
	}
]

因为默认就是feign.hystrix.HystrixFeign.Builder, 所以直接进入HystrixFeign.Builder#target

public <T> T target(Target<T> target) {
  return build().newInstance(target);
}

1. Feign.Builder#build

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);
}

在这里插入图片描述
2. ReflectiveFeign#newInstance

  @Override
  public <T> T newInstance(Target<T> target) {
  	// 获取方法名到方法处理器的映射
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    // 方法到方法处理器的映射(Method中包含@RequestMapping @RequestParms等注解)
    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)));
      }
    }
    // 根据目标对象生成代理拦截执行的handler
    InvocationHandler handler = factory.create(target, methodToHandler);
    // 使用jdk动态代理生成代理类, 重点关注InvocationHandler的invoic
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {target.type()}, handler);

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

在这里插入图片描述

2.2.* feign请求调用

ReflectiveFeign.FeignInvocationHandler#invoke
FeignInvocationHandler为ReflectiveFeign的内部类

static class FeignInvocationHandler implements InvocationHandler {

  private final Target target;
  private final Map<Method, MethodHandler> dispatch;

  FeignInvocationHandler(Target target, Map<Method, MethodHandler> 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);
  }
}

dispatch中存储的是方法到方法处理器的映射
从Map中根据Method获取该方法的处理器(SynchronousMethodHandler), 然后执行

SynchronousMethodHandler#invoke

@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;
    }
  }
}

在这里插入图片描述
继续看executeAndDecode方法, 从名字可以看出实执行真正的请求
后面正对请求的代码就不看了, 无非就是请求负载均衡之类的, 总体openFeign的代码流程比较清晰

小插曲: feign前面为什么要加open, 是因为feign中有自己的注解, 很不方便; springcloud整合后, 可以正常使用@RequestMapping等注解, 就叫openfeign了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/621136.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式任务调度XXL-JOB

XXL-JOB 分布式任务调度平台特点 职责分离&#xff0c;任务调度&#xff0c;任务执行解耦 执行一致性&#xff0c;任务执行不会多次重复执行 丰富的路由策略&#xff08;指定那个执行实例执行&#xff09; 阻塞处理 &#xff08;触发的任务&#xff0c;上一次没有执行完&am…

Android Studio类ChatGpt的免费AI编程助手

ChatGpt大火&#xff0c;带动了AI工具的发展&#xff0c;介绍两款免费的AI编程助手&#xff0c;一款用于输入关键字自动输出代码&#xff0c;一款则是自动补全提示&#xff0e; 可支持大部分代码编辑器&#xff0c;这里主要介绍Android Studio上安装使用&#xff0e; Bito 支…

来阿里面试,问我未来3-5年规划,我给领导画个大饼...

在面试的过程中是不是经常被面试官问未来几年的职业规划?你会答吗&#xff1f;是不是经常脑袋里一片空白&#xff0c;未来规划&#xff1f;我只是想赚更多的钱啊&#xff0c;哈哈哈&#xff0c;今天我来教大家&#xff0c;如何给面试官画一个大饼&#xff0c;让他吃的不亦乐乎…

c++day4 ——homework

1.思维导图 2. 整理类中特殊成员函数&#xff1a;构造函数&#xff0c;析构函数&#xff0c;拷贝构造函数&#xff0c;拷贝赋值函数的使用和实现 特殊成员函数的使用和实现&#xff1a; ① 构造函数 功能&#xff1a;当使用类实例化对象时&#xff0c;给类对象初始化空间以及初…

基于RK3399+FPGA的地面测试台多参数数据记录仪方案(一)硬件设计

地面测试台属于某型号数据记录仪的配套测试设备&#xff0c;主要工作包括&#xff1a;飞行前对记录 仪的功能检查&#xff0c;测试其工作状态和稳定性&#xff1b;实验结束后对已存储到记录仪中的数据进行 回读和进一步处理&#xff0c;通过数据分析得出导弹各项参数在飞行试…

苹果的首款“头显设备”Vision Pro在短期内必将被Meta Quest超越

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;Vision Pro是苹果在今年的WWDC2023开发者大会上发布的一款结合了增强现实(AR)和虚拟现实(VR)的“头显设备”&#xff0c;Vision Pro 的最终售价高达 3499 美元&#xff08;约合…

观察级水下机器人使用系列之二多普勒速度记录仪(DVL)

本文主要讲述观察级水下机器人所使用的多普勒速度记录仪器&#xff08;DVL&#xff09;&#xff0c;见下图。多普勒测速技术自1960年开始研究&#xff0c;1980年开始实现商业化&#xff0c;80年代中后期&#xff0c;窄带多普勒测速技术研究已日趋成熟&#xff0c;90年代以后&am…

初学者应该怎么学git-上

初学者应该怎么学git-上 Git 下载&安装 官网 地址: https://git-scm.com/ 下载版本: Git-2.33.0.2-64-bit.exe 下载慢&#xff0c;可以到镜像下载: http://npm.taobao.org/mirrors/git-for-windows/ 卸载 说明&#xff1a;如果安装过&#xff0c;可以卸载和老师版本…

详解Java中static的使用及其注意事项

1.可以用来修饰的结构&#xff1a; 主要用来修饰类的内部结构 属性、方法、代码块、内部类 2.static修饰属性&#xff1a;静态变量&#xff08;或类变量&#xff09; ​ 2.1 属性&#xff0c;是否使用static修饰&#xff0c;又分为&#xff1a;静态属性 vs 非静态属性(实例…

Yolov5更换上采样方式( 最近邻 / 双线性 / 双立方 / 三线性 / 转置卷积)

原文地址: https://www.iotword.com/3138.html 1. 常用上采样方式介绍 1. 1 最近邻插值(Nearest neighbor interpolation) >>> input torch.arange(1, 5, dtypetorch.float32).view(1, 1, 2, 2) >>> input tensor([[[[ 1., 2.],[ 3., 4.]]]])>>&g…

【goframe】(4):使用goframe 接入grpc服务,非常的方便,可以简单的构建和生成服务代码,并且启动方法也特别简单,使用代码本地调用成功

目录 前言1&#xff0c;关于grpc微服务2&#xff0c;修改生成代码3&#xff0c;相关的goframe的grpc配置4&#xff0c;总结 前言 本文的原文连接是: https://blog.csdn.net/freewebsys/article/details/108971807 未经博主允许不得转载。 博主CSDN地址是&#xff1a;https://b…

3.变量|Java学习笔记

文章目录 数据类型整型的类型浮点型的类型字符类型boolean类型 基本数据类型转换自动类型转换强制类型转换 基本数据类型和String类型的转换 变量 变量名 值 数据类型 https://www.matools.com/# 数据类型 整型的类型 浮点型的类型 关于浮点数在机器中存放形式的简单说明&…

Ibatis与Mybatis的区别—侧重于Ibatis

目录 一、什么是Ibatis&#xff1f; 1、iBatis是一款轻量级的持久化框架 2、iBatis最大的特点是将SQL语句与Java代码分离 3、iBatis具有以下几个关键组成部分&#xff1a; 二、Ibatis与Mybatis的区别 1、基本信息不同 2、开发时间不同 3、配置方法不同 三、Ibatis与My…

HotSpot虚拟机垃圾回收算法及收集器

目录 一、对象引用 二、堆区和方法区回收 1. 堆区回收 2. 方法区回收 三、垃圾回收算法 1. 算法总结 2. 算法相关细节 四、垃圾收集器 1. 新生代收集器 2. 老年代收集器 3. 混合式收集器G1 4. 低延迟收集器 五、参考资料 一、对象引用 判定对象是否存活和引用离…

Unix/Linux编程:Unix domain socket

〇、前言 socket 是一种 IPC &#xff08;Inter-Process Communication&#xff0c;进程间通信&#xff09;方法&#xff0c;它允许位于同一主机&#xff08;计算机&#xff09;或使用网络连接起来的不同主机上的应用程序之间交换数据。通过使用Socket&#xff0c;开发人员可以…

在VSCode中使用LaTex,语法检测插件grammarly

整个文章分为以下几个内容&#xff0c;打 * 的是必须要安装的 LaTex 安装*VSCode 安装*在 VSCode 中配置 LaTexGrammarly语法检测插件 LaTex 安装* latex的下载安装可参考&#xff1a;LaTex&#xff08;2021&#xff09;安装教程 VSCode 安装* VSCode下载&#xff1a;VSCo…

带你用WePY框架提升开发效率

在小程序开发中&#xff0c;提高开发效率、优化代码质量和增强用户体验是每位开发者都追求的目标。而wepy作为一种基于Vue.js的小程序开发框架&#xff0c;提供了更好的开发体验和更高效的开发方式。本文将介绍wepy的基本功能和特性&#xff0c;分享一些实际的代码案例&#xf…

图像的匹配

2023.6.7 图像的匹配 模板匹配是一种在较大图像中搜索和查找模板图像位置的方法。为此&#xff0c;OpenCV 带有一个函数 cv.matchTemplate()。它只是在输入图像上滑动模板图像&#xff08;如在 2D 卷积中&#xff09;&#xff0c;并比较模板图像下的模板和输入图像的补丁。在…

STM32——CAN通信

1、CAN通信概述 STM32F103有两个CAN&#xff0c;都分别有自己的发送接收邮箱。 发送邮箱共有3个来发送报文&#xff0c;发送调度器根据优先级决定哪个邮箱的报文先被发送。 共有2个接收FIFO&#xff0c;每个FIFO都可以存放3个完整的报文。它们完全由硬件来管理。 CAN通信通过…

Mujoco 控制器接口(四)

目录 .1 简介 1.1 控制器实现 .2 sim.step() .3 实例 References .1 简介 control看到下面的图中就是mjtNum类型 mjtNum实际上就是浮点数 ctrl是底层的输入 外界扰动就是通过上图的qfrc和xfrc来添加的 1.1 控制器实现 实际上加上控制器就是xml里添加actuator joint就是…