【Gateway】微服务网关组件 - Spring Cloud Gateway之LoadBalancerClientFilter原理

文章目录

  • 概述
  • 参考

概述

官网文档 LoadBalancerClientFilter

LoadBalancerClientFilter是一个全局过滤器,系统自动注入,LoadBalancerClientFilter 根据 lb:// 前缀过滤处理,使用 serviceId 选择一个服务实例,从而实现负载均衡。

1: public class LoadBalancerClientFilter implements GlobalFilter, Ordered {
 2: 
 3: 	private static final Log log = LogFactory.getLog(LoadBalancerClientFilter.class);
 4: 	public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10100;
 5: 
 6: 	private final LoadBalancerClient loadBalancer;
 7: 
 8: 	public LoadBalancerClientFilter(LoadBalancerClient loadBalancer) {
 9: 		this.loadBalancer = loadBalancer;
10: 	}
11: 
12: 	@Override
13: 	public int getOrder() {
14: 		return LOAD_BALANCER_CLIENT_FILTER_ORDER;
15: 	}
16: 
17: 	@Override
18: 	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
19: 		// 获得 URL
20: 		URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
21: 		if (url == null || !url.getScheme().equals("lb")) {   //核心,根据前缀进行判断
22: 			return chain.filter(exchange);
23: 		}
24: 		// 添加 原始请求URI 到 GATEWAY_ORIGINAL_REQUEST_URL_ATTR
25: 		//preserve the original url
26: 		addOriginalRequestUrl(exchange, url);
27: 
28: 		log.trace("LoadBalancerClientFilter url before: " + url);
29: 
30: 		// 获取 服务实例
31: 		final ServiceInstance instance = loadBalancer.choose(url.getHost());
32: 		if (instance == null) {
33: 			throw new NotFoundException("Unable to find instance for " + url.getHost());
34: 		}
35: 
36: 		/*URI uri = exchange.getRequest().getURI();
37: 		URI requestUrl = loadBalancer.reconstructURI(instance, uri);*/
38: 		//
39: 		URI requestUrl = UriComponentsBuilder.fromUri(url)
40: 				.scheme(instance.isSecure()? "https" : "http") //TODO: support websockets
41: 				.host(instance.getHost())
42: 				.port(instance.getPort())
43: 				.build(true)
44: 				.toUri();
45: 		log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
46: 
47: 		// 添加 请求URI 到 GATEWAY_REQUEST_URL_ATTR
48: 		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
49: 
50: 		// 提交过滤器链继续过滤
51: 		return chain.filter(exchange);
52: 	}
53: 
54: }

核心是21行代码,根据lb://前缀进行判断

  • 第 19 至 23 行 :获得 URL 。只处理 lb:// 为前缀( Scheme )的地址。

  • 第 26 行 :调用 ServerWebExchangeUtils#addOriginalRequestUrl(…) 添加原始请求 URI 到 GATEWAY_ORIGINAL_REQUEST_URL_ATTR 。代码如下 :

    public static void addOriginalRequestUrl(ServerWebExchange exchange, URI url) {
    		exchange.getAttributes().computeIfAbsent(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, s -> new LinkedHashSet<>()); // 数组,考虑多次重写
        LinkedHashSet<URI> uris = exchange.getRequiredAttribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
        uris.add(url);
    }
    

    为什么使用 LinkedHashSet ?因为可以使用 RewritePathGatewayFilterFactory / PrefixPathGatewayFilterFactory 多次重写。

  • 第 30 至 34 行 :调用 LoadBalancerClient#choose(String) 方法,获得一个服务实例( ServiceInstance ) ,从而实现负载均衡。

    • 熟悉 Spring Cloud 的同学都知道,一般情况下 LoadBalancerClient 实现类为 org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient

      举个 instance 的值例子 :

  • 第 39 至 45 行 :创建 requestUrl 。举个例子 :
    【Gateway】微服务网关组件 - Spring Cloud Gateway之LoadBalancerClientFilter原理_第1张图片

  • 第 48 行 :设置 requestUrl 到 GATEWAY_REQUEST_URL_ATTR 。后面 Routing 相关的 GatewayFilter 会通过该属性,发起请求。

  • 第 51 行 :提交过滤器链继续过滤。注意,这里不需要创建新的 ServerWebExchange

参考

SPRING-CLOUD-GATEWAY 源码解析 —— 过滤器 (4.4) 之 LOADBALANCERCLIENTFILTER 负载均衡

你可能感兴趣的