SpringCloudGateway(二)-SpringCloudGateWay的路由原理分析

/ 默认分类 / 0 条评论 / 1142浏览

SpringCloudGateway(二)-SpringCloudGateWay的路由原理分析

一.ApplicationContextAware

当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。
在启动的时候,会自动执行ApplicationContextUtil的setApplicationContext方法,赋值ApplicationContext成员变量之后这样ApplicationContextUtil就可以 操作容器了


import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
 
public class ApplicationContextUtil implements ApplicationContextAware{
 
    private static ApplicationContext applicationContext;
 
    /**
     * 实现ApplicationContextAware接口的回调方法,设置上下文环境
     *
     * @param applicationContext spring上下文对象
     * @throws BeansException 抛出spring异常
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    	ApplicationContextUtil.applicationContext = applicationContext;
    }
 
    /**
     * @return ApplicationContext
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }
 
    /**
     * 获取对象
     *
     * @param name spring配置文件中配置的bean名或注解的名称
     * @return 一个以所给名字注册的bean的实例
     * @throws BeansException 抛出spring异常
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) throws BeansException {
        return (T) applicationContext.getBean(name);
    }
 
    /**
     * 获取类型为requiredType的对象
     *
     * @param clazz 需要获取的bean的类型
     * @return 该类型的一个在ioc容器中的bean
     * @throws BeansException 抛出spring异常
     */
    public static <T> T getBean(Class<T> clazz) throws BeansException {
        return applicationContext.getBean(clazz);
    }
 
    /**
     * 如果ioc容器中包含一个与所给名称匹配的bean定义,则返回true否则返回false
     *
     * @param name ioc容器中注册的bean名称
     * @return 存在返回true否则返回false
     */
    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }
}

springclouddateway的执行流程

先来看一张总体流程图

首先请求会到达DispatcherHandler,相当于springmvc中的DispatcherServlet

 * Central dispatcher for HTTP request handlers/controllers. Dispatches to
 * registered handlers for processing a request, providing convenient mapping
 * facilities.
 
 public class DispatcherHandler implements WebHandler, ApplicationContextAware {  
 	@Nullable
 	private List<HandlerMapping> handlerMappings;
 
 	@Nullable
 	private List<HandlerAdapter> handlerAdapters;
 
 	@Nullable
 	private List<HandlerResultHandler> resultHandlers;

可以看到其实现了ApplicationContextAware接口,这样可以在网关启动的时候获取操作spring容器的功能

启动时会执行setApplicationContext方法,然后initStrategies方法中会使用applicationContext来访问容器, 获取整理匹配的bean,然后将获取的bean初始化到对应的集合中,也就是上面那三个成员变量

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		initStrategies(applicationContext);
	}
	
	protected void initStrategies(ApplicationContext context) {
    		Map<String, HandlerMapping> mappingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
    				context, HandlerMapping.class, true, false);
    
    		ArrayList<HandlerMapping> mappings = new ArrayList<>(mappingBeans.values());
    		AnnotationAwareOrderComparator.sort(mappings);
    		this.handlerMappings = Collections.unmodifiableList(mappings);
    
    		Map<String, HandlerAdapter> adapterBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
    				context, HandlerAdapter.class, true, false);
    
    		this.handlerAdapters = new ArrayList<>(adapterBeans.values());
    		AnnotationAwareOrderComparator.sort(this.handlerAdapters);
    
    		Map<String, HandlerResultHandler> beans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
    				context, HandlerResultHandler.class, true, false);
    
    		this.resultHandlers = new ArrayList<>(beans.values());
    		AnnotationAwareOrderComparator.sort(this.resultHandlers);
    	}

之后,我们访问http://localhost:11101/v1/service_test/api1(前面文章配置的服务)
首先会到达DispatcherHandler的handle方法 ,这里使用的是WebFlux中的函数式流式编程,webflux是spring框架中用来和springmvc进行互补的, 一套全新的非阻塞响应式流处理框架。其中响应式流处理的依赖包,webflux选择的是Reactor,关于Reactor流式编程的方式(Reactor 是一个基于 JVM 之上的异步应用基础库。为 Java 、Groovy 和其他 JVM 语言提供了构建基于事件和数据驱动应用的抽象库。 Reactor 性能相当高,在最新的硬件平台上,使用无堵塞分发器每秒钟可处理 1500 万事件。),可以参考这篇文章
Reactors思维图
Reactor中文文档

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

这里遍历handlerMappings这个保存HandlerMapping的集合

在这个流式处理中,会一次调用匹配到的Handler,并且按照支持的HandlerAdapter来执行,详细见下面的方法:

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

其中不同类型的handlerAdapter判断是否支持当前的Handler,通过下面的方法,以其中一个Adapter实现类为例

	@Override
	public boolean supports(Object handler) {
		return handler instanceof HandlerFunction;
	}

首先需要了解下ServerWebExchange:封装了在spring中一次Http请求的上下文,可以访问当前的Request对象和Response对象以及可以作为请求的容器存储一些本次请求需要使用的参数变量等

ServerWebExchange是一个接口,这里主要需要使用的实现类是DefaultServerWebExchange

public class DefaultServerWebExchange implements ServerWebExchange {
        .....
	private final ServerHttpRequest request;

	private final ServerHttpResponse response;

	private final Map<String, Object> attributes = new ConcurrentHashMap<>();

	private final Mono<WebSession> sessionMono;

	private final LocaleContextResolver localeContextResolver;

	private final Mono<MultiValueMap<String, String>> formDataMono;

	private final Mono<MultiValueMap<String, Part>> multipartDataMono;

	@Nullable
	private final ApplicationContext applicationContext;

	private volatile boolean notModified;

	private Function<String, String> urlTransformer = url -> url;

	@Nullable
	private Object logId;

	private String logPrefix = "";

相应的HandlerMapping会调用getHandler()方法,getHandler()方法是其继承的AbstractHandlerMapping中的方法

	@Override
	public Mono<Object> getHandler(ServerWebExchange exchange) {
	    //这里调用的是抽象类的子类的方法实现
		return getHandlerInternal(exchange).map(handler -> {
			if (logger.isDebugEnabled()) {
				logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
			}
			ServerHttpRequest request = exchange.getRequest();
			if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
				CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(exchange) : null);
				CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange);
				config = (config != null ? config.combine(handlerConfig) : handlerConfig);
				if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
					return REQUEST_HANDLED_HANDLER;
				}
			}
			return handler;
		});
	}

在springcloudgateway中我们主要来看RoutePredicateHandlerMapping类,按照实现类源码,首先会执行下面的方法

@Override
	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)
				// .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);  //下面的方法执行完之后,就会将这个返回过来的路由(需要使用的),设置到Http请求上下文封装类的属性中
					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) + "]");
					}
				})));
	}

为当前请求处理路由相关信息调用的是lookupRoute()方法

protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
		return this.routeLocator.getRoutes()  //获取所有路由(路由通过RouteLocator获取,有很多种routeLocator的实现,默认使用的是按照配置文件获取路由信息)
				// individually filter routes so that filterWhen error delaying is not a
				// problem
				.concatMap(route -> Mono.just(route).filterWhen(r -> {
					// add the current route we are testing
					exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
					return r.getPredicate().apply(exchange);  //按照请求路径,过滤出匹配上的路由
				})
						// instead of immediately stopping main flux due to error, log and
						// swallow it
						.doOnError(e -> logger.error(
								"Error applying predicate for route: " + route.getId(),
								e))
						.onErrorResume(e -> Mono.empty()))
				// .defaultIfEmpty() put a static Route not found
				// or .switchIfEmpty()
				// .switchIfEmpty(Mono.<Route>empty().log("noroute"))
				.next()//返回一个匹配上的路由
				// TODO: error handling
				.map(route -> {
					if (logger.isDebugEnabled()) {
						logger.debug("Route matched: " + route.getId());
					}
					validateRoute(route, exchange);
					return route;
				});

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

RoutePredicateHandlerMapping中执行完路由的匹配之后,下面开始执行不同的Handler,其中过滤器
FilteringWebHandler是重点,首先会执行handle方法:

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

开始逐个执行过滤器:

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); //每一个Filter过滤器依次执行  
				}
				else {
					return Mono.empty(); // complete
				}
			});
		}

调试可以看到,有以下过滤器,我们主要分析画线的两个

按照过滤器链的执行,首先来看下ForwardPathFilter, 当前的封装请求对象的属性exchange变量的attributes集合的数据如下:

{
org.springframework.cloud.gateway.support.ServerWebExchangeUtils.uriTemplateVariables={},
org.springframework.cloud.gateway.support.ServerWebExchangeUtils.routeWeight={}, 
org.springframework.cloud.gateway.support.ServerWebExchangeUtils.gatewayHandlerMapper=RoutePredicateHandlerMapping, 
org.springframework.web.server.ServerWebExchange.LOG_ID=809f24cc-2,
org.springframework.cloud.gateway.support.ServerWebExchangeUtils.gatewayRoute=Route{id='id-service-test', uri=lb://service-test, order=0, predicate=Paths: [/*/service_test/**], match trailing slash: true, gatewayFilters=[], metadata={}}
}
public class ForwardPathFilter implements GlobalFilter, Ordered {

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
	    //获取到这条Route{id='id-service-test', uri=lb://service-test, order=0, predicate=Paths: [/*/service_test/**], match trailing slash: true, gatewayFilters=[], metadata={}
		Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
		URI routeUri = route.getUri();
		String scheme = routeUri.getScheme();
		if (isAlreadyRouted(exchange) || !"forward".equals(scheme)) {
			return chain.filter(exchange);
		}
		exchange = exchange.mutate()
				.request(exchange.getRequest().mutate().path(routeUri.getPath()).build())
				.build();
		return chain.filter(exchange);
	}


  1. 首先执行的过滤器是ForwardPathFilter,其方法如下:
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取路由,路由在前面的RoutePredicateHandlerMapping中已经赋值了
		Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
		URI routeUri = route.getUri();
		String scheme = routeUri.getScheme();
		//如果已经路由了,或者路由协议不包含forward转发才会继续正常走下面的过滤器
		if (isAlreadyRouted(exchange) || !"forward".equals(scheme)) {
			return chain.filter(exchange);
		}
		//否则如果是转发forward,那么不会走路由,直接走的网关的当前请求url地址,也就不会按照路由拼接地址  
		exchange = exchange.mutate()
				.request(exchange.getRequest().mutate().path(routeUri.getPath()).build())
				.build();
		return chain.filter(exchange);
	}

下面测试下,不同的路由协议,调试时的情况:

    @GetMapping("/service-test")
    public String test1(){
        return "okservice-testasa";
    }

网关中的路由配置如下:

    gateway:
      routes:
        - id: id-service-test
          uri: forward:/service-test
          predicates:
            - Path=/*/service_test/**
        - id: id1-service-test
          uri: forward:/service1-test
          predicates:
            - Path=/*/service_test/**

调试情况:
调试情况

请求响应如下 请求响应如下

  1. 执行RouteToRequestUrlFilter
@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
		if (route == null) {
			return chain.filter(exchange);
		}
		log.trace("RouteToRequestUrlFilter start");
		URI uri = exchange.getRequest().getURI();
		boolean encoded = containsEncodedParts(uri);
		URI routeUri = route.getUri();

		if (hasAnotherScheme(routeUri)) {
			// this is a special url, save scheme to special attribute
			// replace routeUri with schemeSpecificPart
			exchange.getAttributes().put(GATEWAY_SCHEME_PREFIX_ATTR,
					routeUri.getScheme());
			routeUri = URI.create(routeUri.getSchemeSpecificPart());
		}
        //如果时lb协议(服务负载均衡),并且服务主机名不为空,其实就是判断服务主机是否合法,这里有个很坑的地方,就是如果你的
        //微服务的服务名有下滑线或者其他在域名中时禁止使用的字符,那么解析出来的Uri对象的host就是null。因为这里会将服务名作为
        //URI对象的host,而在URI中设置host是会检查是否符合域名规范(可以是ip),如果不符合则设置为null,所以会报下面的异常    
		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());
		}
		//  http://localhost:11101/v1/service_test/api1  + 路由lb://service-test得到mergedUrl如下
        //  lb://service-test/v1/service_test/api1
		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. LoadBalancerClientFilter
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //lb://service-test/v1/service_test/api1  
		URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
		String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);
		//如果不是服务负载均衡协议(lb)则不执行负载均衡
		if (url == null
				|| (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
			return chain.filter(exchange);
		}
		// preserve the original url
		//保存原始的url地址到exchange请求上下文封装对象的属性中  (lb://service-test/v1/service_test/api1) 
		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 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();
		}

		URI requestUrl = loadBalancer.reconstructURI(
				new DelegatingServiceInstance(instance, overrideScheme), uri);

		if (log.isTraceEnabled()) {
			log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
		}

		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
		return chain.filter(exchange);
	}

//进行负载均衡选择

	protected ServiceInstance choose(ServerWebExchange exchange) {
	    //即return loadBalancer.choose("service-test")  service-test即为服务id,服务唯一名 
		return loadBalancer.choose(
				((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost());
	}
protected final LoadBalancerClient loadBalancer;

loadBalancer即为负载均衡器;下面我们来看下SpringCloud中的负载均衡器的实现情况:

springcloud中的负载均衡器的原始接口ServiceInstanceChooser

package org.springframework.cloud.client.loadbalancer;

import org.springframework.cloud.client.ServiceInstance;

/**
 * Implemented by classes which use a load balancer to choose a server to send a request
 * to.
 *
 * @author Ryan Baxter
 */
public interface ServiceInstanceChooser {

	/**
	 * Chooses a ServiceInstance from the LoadBalancer for the specified service.
	 * @param serviceId The service ID to look up the LoadBalancer.
	 * @return A ServiceInstance that matches the serviceId.
	 */
	ServiceInstance choose(String serviceId);

}

子接口

package org.springframework.cloud.client.loadbalancer;

import java.io.IOException;
import java.net.URI;

import org.springframework.cloud.client.ServiceInstance;

/**
 * Represents a client-side load balancer.
 *
 * @author Spencer Gibb
 */
public interface LoadBalancerClient extends ServiceInstanceChooser {
    
    //按照服务id,负载均衡执行
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

	<T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException;

	URI reconstructURI(ServiceInstance instance, URI original);

}

package org.springframework.cloud.netflix.ribbon.support;
public abstract class AbstractLoadBalancingClient<S extends ContextAwareRequest, T extends IResponse, D>
		extends AbstractLoadBalancerAwareClient<S, T> implements ServiceInstanceChooser {
    	@Override
    	public ServiceInstance choose(String serviceId) {
    		Server server = this.getLoadBalancer().chooseServer(serviceId);
    		if (server != null) {
    			return new RibbonLoadBalancerClient.RibbonServer(serviceId, server);
    		}
    		return null;
    	}

LoadBalancerClient只有一个实现类: RibbonLoadBalancerClient

	@Override
	public ServiceInstance choose(String serviceId) {
        //未指定执行的服务
		return choose(serviceId, null);
	}
	
	public ServiceInstance choose(String serviceId, Object hint) {
    		Server server = getServer(getLoadBalancer(serviceId), hint);
    		if (server == null) {
    			return null;
    		}
    		return new RibbonServer(serviceId, server, isSecure(server, serviceId),
    				serverIntrospector(serviceId).getMetadata(server));
    	}

RibbonLoadBalancerClient(负载均衡客户端)可以来选择本次执行的服务实例,也就是其实现的choose()方法,但是choose方法中真正用来进行负载均衡计算的是使用了getLoadBalancer 获取一个负载均衡器,会先去容器中获取,如果没有实例化好的就初始化一个默认的负载均衡器ZoneAwareLoadBalancer

	public ILoadBalancer getLoadBalancer(String name) {
		return getInstance(name, ILoadBalancer.class);
	}
package com.netflix.loadbalancer;

public interface ILoadBalancer {

	public void addServers(List<Server> newServers);
	
	public Server chooseServer(Object key);
	
	public void markServerDown(Server server);
	
	@Deprecated
	public List<Server> getServerList(boolean availableOnly);

    public List<Server> getReachableServers();

	public List<Server> getAllServers();
}

com.netflix.loadbalancer.ILoadBalancer的实现类如下:

AbstractLoadBalancer (com.netflix.loadbalancer)
BaseLoadBalancer (com.netflix.loadbalancer)
DynamicServerListLoadBalancer (com.netflix.loadbalancer)
NoOpLoadBalancer (com.netflix.loadbalancer)
ZoneAwareLoadBalancer (com.netflix.loadbalancer)

得到的负载均衡器是: loadBalancer.getClass(),getName()="com.netflix.loadbalancer.ZoneAwareLoadBalancer",

public class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T>

然后执行getServer()
因为没有指定执行的服务实例hint为null,

	protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
		if (loadBalancer == null) {
			return null;
		}
		// Use 'default' on a null hint, or just pass it on?
		return loadBalancer.chooseServer(hint != null ? hint : "default");
	}

DynamicServerListLoadBalancer是BaseLoadBalancer的一个子类,DynamicServerListLoadBalancer主要是实现了服务实例清单在运行期间的动态更新能力,同时提供了对服务实例清单的过滤功能。 ZoneAwareLoadBalancer是DynamicServerListLoadBalancer的子类,ZoneAwareLoadBalancer的出现主要是为了弥补DynamicServerListLoadBalancer的不足。由于DynamicServerListLoadBalancer中并没有重写chooseServer方法, 所以DynamicServerListLoadBalancer中负责均衡的策略依然是我们在BaseLoadBalancer中分析出来的线性轮询策略,这种策略不具备区域感知功能,这样当需要跨区域调用时, 可能会产生高延迟。ZoneAwareLoadBalancer重写了setServerListForZones方法,该方法在其父类中的功能主要是根据区域Zone分组的实例列表, 为负载均衡器中的LoadBalancerStats对象创建ZoneStats并存入集合中,ZoneStats是一个用来存储每个Zone的状态和统计信息。 重写之后的setServerListForZones方法主要做了两件事:一件是调用getLoadBalancer方法来创建负载均衡器,同时创建服务选择策略;另一件是对Zone区域中的实例清单进行检查, 如果对应的Zone下已经没有实例了,则将Zone区域的实例列表清空,防止节点选择时出现异常。

执行ZoneAwareLoadBalancer中的chooseServer的方法

    @Override
    public Server chooseServer(Object key) {
    //默认开启区域选择,但是因为目前只有一个区域,所以还是走的父类,即DynamicServerListLoadBalancer的chooseServer,由上面分析可知其实执行的
    //是BaseLoadBalancer的chooseServer方法
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            logger.debug("Zone aware logic disabled or there is only one zone");
            return super.chooseServer(key);
        }

默认使用的轮询方式

public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }
    
        @Override
        public Server choose(Object key) {
            ILoadBalancer lb = getLoadBalancer();
            Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
            if (server.isPresent()) {
                return server.get();
            } else {
                return null;
            }       
        }
        
        
            public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) {
                List<Server> eligible = getEligibleServers(servers, loadBalancerKey);
                if (eligible.size() == 0) {
                    return Optional.absent();
                }
                return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));
            }
            
            
                private int incrementAndGetModulo(int modulo) {
                    for (;;) {
                        int current = nextIndex.get();
                        int next = (current + 1) % modulo;
                        if (nextIndex.compareAndSet(current, next) && current < modulo)
                            return current;
                    }
                }

那么服务实例列表是什么时候拿到的呢?

前面我们知道,会自动创建了ZoneAwareLoadBalancer负载均衡器,所以会执行下面的构造器

//这里的serverList包含了网关中配置的nacos注册中心的数据
    public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
                                 IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
                                 ServerListUpdater serverListUpdater) {
        //执行父类构造
        super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
    }
    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                         ServerList<T> serverList, ServerListFilter<T> filter,
                                         ServerListUpdater serverListUpdater) {
        super(clientConfig, rule, ping);
        this.serverListImpl = serverList;
        this.filter = filter;
        this.serverListUpdater = serverListUpdater;
        if (filter instanceof AbstractServerListFilter) {
            ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
        }
        restOfInit(clientConfig);
    }
    void restOfInit(IClientConfig clientConfig) {
        boolean primeConnection = this.isEnablePrimingConnections();
        // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
        this.setEnablePrimingConnections(false);
        enableAndInitLearnNewServersFeature();
        //这里就会请求nacos获取服务列表
        updateListOfServers();
        if (primeConnection && this.getPrimeConnections() != null) {
            this.getPrimeConnections()
                    .primeConnections(getReachableServers());
        }
        this.setEnablePrimingConnections(primeConnection);
        LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
    }

最终执行的是ServerList的实现类NacosServerList中的方法

	private List<NacosServer> getServers() {
		try {
		    //获取配置中心的基础配置信息
			String group = discoveryProperties.getGroup();
			List<Instance> instances = discoveryProperties.namingServiceInstance()
					.selectInstances(serviceId, group, true);
			//获取指定的服务名的服务列表
			return instancesToServerList(instances);
		}
		catch (Exception e) {
			throw new IllegalStateException(
					"Can not get service instances from nacos, serviceId=" + serviceId,
					e);
		}
	}

总结

的确,springcloud真的是一套完美的框架,具备极高的可拓展性,所以例如gateway,ribbon,nacos等等这些优秀的框架才能聚合在一起完美配合工作,上面梳理了 很多,我们来总结一下。

首先,springcloudgateway是一个微服务网关,请求首先会来到网关,之后交给DispatcherHandler处理,其中会执行容器中已经注册的handerMapping,我们分析的是RoutePredicateHandlerMapping, 他会将配置中的路由(默认配置文件)初始化并匹配后保存到请求上下文封装对象(Exchange)中,之后会进入一系列的过滤器进行处理,这些过滤器器中,主要讲解的就是ForwardPathFilter,用来判断 路由是否需要进行服务路由或者是否为Forward转发路由,之后是RouteToRequestUrlFilter ,会按照匹配到的路由和请求的url拼接组装目的服务请求url存放到Exchange的属性中,之后是 LoadBalancerClientFilter,前面组装好的地址为lb://service-test/v1/service_test/api1,在LoadBalancerClientFilter会先负载均衡获取到本次使用的服务实例 然后最终的请求地址就是ip:port/v1/service_test/api1

上面我们分析的是全局过滤器,在这之前,解析路由配置的时候会执行一系列的请求过滤器,这些可以在spring官方详细了解到,这里我想说一句就是,真的详细到爆炸,并且你打开相应 的过滤器源码查看也很容易理解,主要要搞清楚springcloud中的请求封装上下文都是在Exchange中.也就是请求交换机,包含了当前请求的所有信信息;

参考:
自定义负载均衡器
官方文档
自定义负载均衡器