摘要: 原创出处 http://www.iocoder.cn/Spring-Cloud-Gateway/filter-load-balancer-client/ 「芋道源码」欢迎转载,保留摘要,谢谢!
本文主要基于 Spring-Cloud-Gateway 2.0.X M4
阅读源码最好的方式,是使用 IDEA 进行调试 Spring Cloud Gateway 源码,不然会一脸懵逼。
1. 概述
本文主要分享 LoadBalancerClientFilter 的代码实现。
LoadBalancerClientFilter 根据 lb:// 前缀过滤处理,使用 serviceId 选择一个服务实例,从而实现负载均衡。
推荐 Spring Cloud 书籍:
请支持正版。下载盗版,等于主动编写低级 BUG 。
程序猿DD —— 《Spring Cloud微服务实战》
两书齐买,京东包邮。
2. 环境搭建
在 《Spring-Cloud-Gateway 源码解析 —— 路由(1.4)之 DiscoveryClientRouteDefinitionLocator 注册中心》「2. 环境搭建」 有详细教程。
3. LoadBalancerClientFilter
org.springframework.cloud.gateway.filter.LoadBalancerClientFilter ,代码如下 :
public class LoadBalancerClientFilter implements GlobalFilter, Ordered {
private static final Log log = LogFactory.getLog(LoadBalancerClientFilter.class);
public static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10100;
private final LoadBalancerClient loadBalancer;
public LoadBalancerClientFilter(LoadBalancerClient loadBalancer) {
this.loadBalancer = loadBalancer;
}
@Override
public int getOrder() {
return LOAD_BALANCER_CLIENT_FILTER_ORDER;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获得 URL
URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);
if (url == null || !url.getScheme().equals("lb")) {
return chain.filter(exchange);
}
// 添加 原始请求URI 到 GATEWAY_ORIGINAL_REQUEST_URL_ATTR
//preserve the original url
addOriginalRequestUrl(exchange, url);
log.trace("LoadBalancerClientFilter url before: " + url);
// 获取 服务实例
final ServiceInstance instance = loadBalancer.choose(url.getHost());
if (instance == null) {
throw new NotFoundException("Unable to find instance for " + url.getHost());
}
/*URI uri = exchange.getRequest().getURI();
URI requestUrl = loadBalancer.reconstructURI(instance, uri);*/
//
URI requestUrl = UriComponentsBuilder.fromUri(url)
.scheme(instance.isSecure()? "https" : "http") //TODO: support websockets
.host(instance.getHost())
.port(instance.getPort())
.build(true)
.toUri();
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
// 添加 请求URI 到 GATEWAY_REQUEST_URL_ATTR
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
// 提交过滤器链继续过滤
return chain.filter(exchange);
}
}第 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。举个例子 :
第 48 行 :设置
requestUrl到GATEWAY_REQUEST_URL_ATTR。后面 Routing 相关的 GatewayFilter 会通过该属性,发起请求。第 51 行 :提交过滤器链继续过滤。注意,这里不需要创建新的 ServerWebExchange
4. 高能
我们回过头看 《Spring-Cloud-Gateway 源码解析 —— 路由(1.4)之 DiscoveryClientRouteDefinitionLocator 注册中心》「4. 高能」
相同服务( serviceId 相同) ,服务实例的注册或下线,Ribbon 已经处理,所以不用担心。