博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
聊聊spring cloud gateway的LoadBalancerClientFilter
阅读量:5914 次
发布时间:2019-06-19

本文共 9021 字,大约阅读时间需要 30 分钟。

本文主要研究一下spring cloud gateway的LoadBalancerClientFilter

GatewayLoadBalancerClientAutoConfiguration

spring-cloud-gateway-core-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/gateway/config/GatewayLoadBalancerClientAutoConfiguration.java

@Configuration@ConditionalOnClass({LoadBalancerClient.class, RibbonAutoConfiguration.class, DispatcherHandler.class})@AutoConfigureAfter(RibbonAutoConfiguration.class)public class GatewayLoadBalancerClientAutoConfiguration {    // GlobalFilter beans    @Bean    @ConditionalOnBean(LoadBalancerClient.class)    public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client) {        return new LoadBalancerClientFilter(client);    }}
如果检测到有ribbon,则开启LoadBalancerClientFilter

LoadBalancerClientFilter

spring-cloud-gateway-core-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/gateway/filter/LoadBalancerClientFilter.java

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
filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); 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); 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(); // if the `lb:
` mechanism was used, use `
` as the default, // if the loadbalancer doesn't provide one. String overrideScheme = null; if (schemePrefix != null) { overrideScheme = url.getScheme(); } URI requestUrl = loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri); log.trace("LoadBalancerClientFilter url chosen: " + requestUrl); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl); return chain.filter(exchange); } //......}
  • 这里首先获取scheme,如果不是lb,则直接往下一个filter传递
  • 之后通过loadBalancer.choose(url.getHost())来选取服务实例
  • 最后构造好requestUrl,设置到GATEWAY_REQUEST_URL_ATTR属性中

GATEWAY_SCHEME_PREFIX_ATTR

spring-cloud-gateway-core-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/gateway/filter/RouteToRequestUrlFilter.java

GATEWAY_SCHEME_PREFIX_ATTR这个属性是在RouteToRequestUrlFilter里头设置进去的。

public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {    private static final Log log = LogFactory.getLog(RouteToRequestUrlFilter.class);    public static final int ROUTE_TO_URL_FILTER_ORDER = 10000;    private static final String SCHEME_REGEX = "[a-zA-Z]([a-zA-Z]|\\d|\\+|\\.|-)*:.*";    static final Pattern schemePattern = Pattern.compile(SCHEME_REGEX);    @Override    public int getOrder() {        return ROUTE_TO_URL_FILTER_ORDER;    }    @Override    public Mono
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()); } URI requestUrl = UriComponentsBuilder.fromUri(uri) .uri(routeUri) .build(encoded) .toUri(); exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl); return chain.filter(exchange); } /* for testing */ static boolean hasAnotherScheme(URI uri) { return schemePattern.matcher(uri.getSchemeSpecificPart()).matches() && uri.getHost() == null && uri.getRawPath() == null; }}

GATEWAY_REQUEST_URL_ATTR

spring-cloud-gateway-core-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/gateway/filter/NettyRoutingFilter.java

public class NettyRoutingFilter implements GlobalFilter, Ordered {    private final HttpClient httpClient;    private final ObjectProvider
> headersFilters; public NettyRoutingFilter(HttpClient httpClient, ObjectProvider
> headersFilters) { this.httpClient = httpClient; this.headersFilters = headersFilters; } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } @Override public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR); String scheme = requestUrl.getScheme(); if (isAlreadyRouted(exchange) || (!"http".equals(scheme) && !"https".equals(scheme))) { return chain.filter(exchange); } setAlreadyRouted(exchange); ServerHttpRequest request = exchange.getRequest(); final HttpMethod method = HttpMethod.valueOf(request.getMethod().toString()); final String url = requestUrl.toString(); HttpHeaders filtered = filterRequest(this.headersFilters.getIfAvailable(), exchange); final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders(); filtered.forEach(httpHeaders::set); String transferEncoding = request.getHeaders().getFirst(HttpHeaders.TRANSFER_ENCODING); boolean chunkedTransfer = "chunked".equalsIgnoreCase(transferEncoding); boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false); return this.httpClient.request(method, url, req -> { final HttpClientRequest proxyRequest = req.options(NettyPipeline.SendOptions::flushOnEach) .headers(httpHeaders) .chunkedTransfer(chunkedTransfer) .failOnServerError(false) .failOnClientError(false); if (preserveHost) { String host = request.getHeaders().getFirst(HttpHeaders.HOST); proxyRequest.header(HttpHeaders.HOST, host); } return proxyRequest.sendHeaders() //I shouldn't need this .send(request.getBody().map(dataBuffer -> ((NettyDataBuffer)dataBuffer).getNativeBuffer())); }).doOnNext(res -> { ServerHttpResponse response = exchange.getResponse(); // put headers and status so filters can modify the response HttpHeaders headers = new HttpHeaders(); res.responseHeaders().forEach(entry -> headers.add(entry.getKey(), entry.getValue())); exchange.getAttributes().put("original_response_content_type", headers.getContentType()); HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter( this.headersFilters.getIfAvailable(), headers, exchange, Type.RESPONSE); response.getHeaders().putAll(filteredResponseHeaders); HttpStatus status = HttpStatus.resolve(res.status().code()); if (status != null) { response.setStatusCode(status); } else if (response instanceof AbstractServerHttpResponse) { // https://jira.spring.io/browse/SPR-16748 ((AbstractServerHttpResponse) response).setStatusCodeValue(res.status().code()); } else { throw new IllegalStateException("Unable to set status code on response: " +res.status().code()+", "+response.getClass()); } // Defer committing the response until all route filters have run // Put client response as ServerWebExchange attribute and write response later NettyWriteResponseFilter exchange.getAttributes().put(CLIENT_RESPONSE_ATTR, res); }).then(chain.filter(exchange)); }}
在NettyRoutingFilter中根据GATEWAY_REQUEST_URL_ATTR属性读取requestUrl,然后进行相应请求。

小结

LoadBalancerClientFilter会作用在url以lb开头的路由,然后利用loadBalancer来获取服务实例,构造目标requestUrl,设置到GATEWAY_REQUEST_URL_ATTR属性中,供NettyRoutingFilter使用。

doc

转载地址:http://hnwvx.baihongyu.com/

你可能感兴趣的文章
leetcode-WordLadder
查看>>
elf,基于flexbox的响应式CSS框架
查看>>
javascript 的 eval()——编写可维护的javascript读书笔记
查看>>
CSS魔法堂:hasLayout原来是这样的!
查看>>
SegmentFault D-Day 2015 北京前端场回顾
查看>>
网页缓存 cache 二三事
查看>>
数据库设计-逻辑设计
查看>>
Linux基础命令---lp打印机命令
查看>>
redux中文文档
查看>>
Android多线程下操作sqlite数据库解决方案
查看>>
神州优车拟41亿元收购宝沃汽车67%股权
查看>>
移动直播app软件直播平台怎么创建
查看>>
深思考CEO杨志明:多模态深度语义理解开启人机交互新时代
查看>>
面向业务开发者的 k8s 基本命令
查看>>
Linux 去掉名字最后一个字段
查看>>
理解Kubernetes网络之Flannel网络
查看>>
weebox弹出窗口不居中显示?
查看>>
CakePHP 4.0.0 alpha1 发布,PHP 快速开发框架
查看>>
功能强大的jQuery图片查看器插件
查看>>
JDBC链接数据库
查看>>