SpringCloud源码解析-gateway&openFeign

这篇具有很好参考价值的文章主要介绍了SpringCloud源码解析-gateway&openFeign。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. gateway 源码解析

1.1 自动装配

springcloud是基于springboot的,gateway各个组件的初始化入口在自动装配
SpringCloud源码解析-gateway&openFeign

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 工作机制

SpringCloud源码解析-gateway&openFeign

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

1.3.1 Gateway的三个核心组件

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

SpringCloud源码解析-gateway&openFeign

1.3.2 请求处理流程(图)

SpringCloud源码解析-gateway&openFeign

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
SpringCloud源码解析-gateway&openFeign
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请求响应…
SpringCloud源码解析-gateway&openFeign

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

SpringCloud源码解析-gateway&openFeign

因为没在@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);
}

SpringCloud源码解析-gateway&openFeign
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;
  }

SpringCloud源码解析-gateway&openFeign

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

SpringCloud源码解析-gateway&openFeign
继续看executeAndDecode方法, 从名字可以看出实执行真正的请求
后面正对请求的代码就不看了, 无非就是请求负载均衡之类的, 总体openFeign的代码流程比较清晰

小插曲: feign前面为什么要加open, 是因为feign中有自己的注解, 很不方便; springcloud整合后, 可以正常使用@RequestMapping等注解, 就叫openfeign了文章来源地址https://www.toymoban.com/news/detail-475424.html

到了这里,关于SpringCloud源码解析-gateway&openFeign的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringCloud入门(微服务调用 OpenFeign)——从RestTemplate到OpenFeign & OpenFeign的相关配置 & 源码的分析和请求流程拆解

    在之前的博客中,我们介绍了RestTemplate的使用,博客文章如下连接。但是在使用RestTemplate的时候,需要把生产者的路径拼出来,非常繁琐,另外参数的传递的也比较繁琐,解决方案就是使用openFeign。 SpringCloud入门(RestTemplate + Ribbon)——微服务调用的方式 RestTemplate的使用 使

    2024年04月11日
    浏览(26)
  • 微服务多模块:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源码)仅需一招,520彻底拿捏你

      可能有些人会觉得这篇似曾相识,没错,这篇是由原文章进行二次开发的。 前阵子有些事情,但最近看到评论区说原文章最后实现的是单模块的验证,由于过去太久也懒得验证,所以重新写了一个完整的可以跑得动的一个。 OK,回到正题,以下是真正对应的微服务多模块

    2024年02月05日
    浏览(35)
  • 【深入解析spring cloud gateway】06 gateway源码简要分析

    上一节做了一个很简单的示例,微服务通过注册到eureka上,然后网关通过服务发现访问到对应的微服务。本节将简单地对整个gateway请求转发过程做一个简单的分析。 主要流程: Gateway Client向 Spring Cloud Gateway 发送请求 请求首先会被HttpWebHandlerAdapter 进行提取组装成网关上下文

    2024年02月10日
    浏览(32)
  • SpringCloud OpenFeign 全功能配置详解(一文吃透OpenFeign)

    OpenFeign客户端是一个web声明式http远程调用工具,直接可以根据服务名称去注册中心拿到指定的服务IP集合,提供了接口和注解方式进行调用,内嵌集成了Ribbon本地负载均衡器。 1、底层都是内置了Ribbon,去调用注册中心的服务。 2、Feign是Netflix公司写的,是SpringCloud组件中的一

    2024年02月07日
    浏览(30)
  • 【springcloud 微服务】springcloud openfeign使用详解

    目录 一、前言 二、openfeign介绍 2.1 openfeign介绍 2.2  openfeign优势 三、Spring Cloud Alibaba整合OpenFeign

    2024年02月05日
    浏览(37)
  • 【springcloud微服务】springcloud整合openfeign使用详解

    目录 一、前言 二、微服务接口之间的调用问题 2.1 Httpclient 2.2 Okhttp 2.3 HttpURLConnection 2.

    2024年02月02日
    浏览(31)
  • SpringCloud OpenFeign

    Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。 Feign是Spring Cloud组件中一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。 OpenFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如@Reques

    2024年02月08日
    浏览(26)
  • 声明式调用 —— SpringCloud OpenFeign

    Spring Cloud Feign 是一个 HTTP 请求调用的轻量级框架,可以以 Java 接口注解的方式调用 HTTP 请求,而不用通过封装 HTTP 请求报文的方式直接调用 Feign 通过处理注解,将请求模板化,当实际调用的时候传入参数,根据参数再应用到请求上,进而转化成真正的请求 本小节介绍如何通

    2024年02月08日
    浏览(41)
  • 熟练使用Nacos、GateWay、OpenFeign、Sentinel常用组件

    Nacos 面试题 : 请简述Nacos是什么,它主要解决了什么问题? Nacos提供了哪些核心功能? Nacos是如何支持服务发现的? 如何使用Nacos作为配置中心? Nacos的集群部署是如何实现的? 答案 : Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它主要解

    2024年04月13日
    浏览(26)
  • 【SpringCloud】OpenFeign服务接口调用快速入门

    官网地址:点击跳转 Feign是一个 声明性web服务客户端 。它使编写web服务客户端变得更容易。使用 Feign 创建一个接口并对其进行注释。它具有可插入的注释支持,包括Feign注释和 JAX-RS 注释。Feign 还支持可插拔编码器和解码器。Spring Cloud 添加了对 Spring MVC 注释的支持,以及对

    2024年04月25日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包