欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringCloud Gateway路由核心原理解析

 更新時間:2024年10月28日 10:22:51   作者:普通人zzz~  
本文主要介紹了SpringCloudGateway的基礎構建塊、工作原理以及核心原理解析,SpringCloudGateway是Spring官方基于SpringSpringBoot和ProjectReactor等技術開發(fā)的網關,旨在為微服務架構提供一種簡單而有效的統(tǒng)一的API路由管理方式

一、介紹

SpringCloud Gateway 是 Spring 官方基于 Spring Spring Boot 和 Project Reactor 等技術開發(fā)的網關,Gateway 旨在為微服務架構提供一種簡單而有效的統(tǒng)一的API路由管理方式。Gateway作為 Spring Cloud 生態(tài)系中的網關,目標是替代 ZUUL,其不僅提供統(tǒng)一的路由方式,并且基于 Filter 鏈的方式提供了關基本的功能,例如:安全,監(jiān)控/埋點,和限流等。

1.1 工作原理

1.2 重要概念

  • 路由(Route): 路由是Gateway的基礎構建塊。它由ID、目標URL、一組斷言和過濾器定義。如果斷言評估為true,則路由匹配并執(zhí)行相關的過濾器鏈。
  • 斷言(Predicate): 輸入的請求會被一組斷言評估,如果斷言為true,則路由匹配。常用的斷言包括路徑匹配、查詢參數(shù)匹配、請求頭匹配等。
  • 過濾器(Filter): 過濾器在特定的生命周期中攔截并修改請求和響應。過濾器可以作用在代理前、代理后、或者出錯時。常見的過濾器有:添加響應頭、限流、日志記錄等。

二、基本應用

2.1 構建項目

pom.xml

<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-boot.version>2.6.13</spring-boot.version>
    <spring-cloud.version>2021.0.5</spring-cloud.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2021.0.5</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>${spring-boot.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>

2.2 添加yaml路由配置

spring:
  cloud:
    gateway:
      # 網關路由配置
      routes:
        # 路由id,自定義,只要唯一即可
        - id: provider-baidu
          # 路由的目標地址
          uri: https://www.baidu.com
          predicates: # 路由斷言,判斷請求是否符合路由規(guī)則的條件
            - Path=/s/**      # 按照路徑匹配(PathRoutePredicateFactory),以/s/開頭的請求就符合要求
        - id: provider-csdn
          # 路由的目標地址
          uri: https://blog.csdn.net/
          predicates: # 路由斷言,判斷請求是否符合路由規(guī)則的條件
            - Path=/csdn/**      # 按照路徑匹配(PathRoutePredicateFactory),以/csdn/開頭的請求就符合要求 

2.3 測試

訪問 http://localhost:8080/s?wd=1,則自動顯示 https://www.baidu.com/s?wd=1 內容,結果如下:

三、核心原理解析

3.1 GatewayAutoConfiguration

根據(jù)約定大于配置思想,結合springboot start原理,可以找到 Gateway自動注入類。

結合SpringMVC底層原理分析,Url的匹配,是在 HandlerMapping 中找到對應的 HandlerAdaptor 后進行處理,Gateway也運用了這一點,核心存在一個 RoutePredicateHandlerMapping 類,用于根據(jù)請求url以及配置的所有路由,查找到一個 Route(路由),在進行相關處理后,進行Http調用。

@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
@EnableConfigurationProperties
@AutoConfigureBefore({ HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class })
@AutoConfigureAfter({ GatewayReactiveLoadBalancerClientAutoConfiguration.class,
		GatewayClassPathWarningAutoConfiguration.class })
@ConditionalOnClass(DispatcherHandler.class)
public class GatewayAutoConfiguration {
	@Bean
	@ConditionalOnMissingBean
	public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler,
			RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {
		return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);
	}
}

3.2 RoutePredicateHandlerMapping

RoutePredicateHandlerMapping 繼承了 AbstractHandlerMapping 類,實現(xiàn)了

public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
		implements HandlerMapping, Ordered, BeanNameAware {
	@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 (config != null) {
					config.validateAllowCredentials();
				}
				if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
					return NO_OP_HANDLER;
				}
			}
			return handler;
		});
	}
	/**
	 * 查找給定請求的處理程序,如果未找到特定的處理程序,則返回空的
	 */
	protected abstract Mono<?> getHandlerInternal(ServerWebExchange exchange);
}

RoutePredicateHandlerMapping#getHandlerInternal 核心代碼如下:

public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
	@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);
					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) + "]");
					}
				})));
	}
	/**
	 * 查找路由
	 */
	protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
		return this.routeLocator.getRoutes()
				// 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;
				});
	}
}

其中,final RouteLocator routeLocator; 對象存儲了所有配置的路由(route),如下:

Gateway會根據(jù)斷言Predicate進行匹配,找到對應的Route,再進行路由等其他處理。

Route

public class Route implements Ordered {
	private final String id;
	private final URI uri;
	private final int order;
	private final AsyncPredicate<ServerWebExchange> predicate;
	private final List<GatewayFilter> gatewayFilters;
	private final Map<String, Object> metadata;
	private Route(String id, URI uri, int order, AsyncPredicate<ServerWebExchange> predicate,
			List<GatewayFilter> gatewayFilters, Map<String, Object> metadata) {
		this.id = id;
		this.uri = uri;
		this.order = order;
		this.predicate = predicate;
		this.gatewayFilters = gatewayFilters;
		this.metadata = metadata;
	}
}

RoutePredicateHandlerMapping#getHandler 如果匹配成功,最終會返回一個處理 FilteringWebHandler 類,如下:

3.3 FilteringWebHandler

WebHandler

/**
 * 用于處理 Web 請求的協(xié)定。
 */
public interface WebHandler {
	/**
	 * Handle the web server exchange.
	 * @param exchange the current server exchange
	 * @return {@code Mono<Void>} to indicate when request handling is complete
	 */
	Mono<Void> handle(ServerWebExchange exchange);
}

則最終會調用到 FilteringWebHandler#handle 方法中:

public class FilteringWebHandler implements WebHandler {
	@Override
	public Mono<Void> handle(ServerWebExchange exchange) {
		Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
		// 獲取當前 Route 配置的Filter
		List<GatewayFilter> gatewayFilters = route.getFilters();
		// 獲取全局的Filter
		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);
		}
		// 構建一個FilterChain,最終進行調用
		return new DefaultGatewayFilterChain(combined).filter(exchange);
	}
	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
				}
			});
		}
	}
}

handle 方法處理邏輯為:

  • 獲取當前 Route 配置的Filter;
  • 獲取全局的Filter;
  • 將所有的Filter排序后,構建成一個FilterChain;
  • 在所有的Filter中,根據(jù)具體協(xié)議選擇最終的Filter,進行最終路由;

3.4 NettyRoutingFilter

NettyRoutingFilter 為最終進行Http路由的類。

public class NettyRoutingFilter implements GlobalFilter, Ordered {
	@Override
	@SuppressWarnings("Duplicates")
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		URI requestUrl = exchange.getRequiredAttribute(GATEWAY_REQUEST_URL_ATTR);
		String scheme = requestUrl.getScheme();
		if (isAlreadyRouted(exchange) || (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme))) {
			return chain.filter(exchange);
		}
		setAlreadyRouted(exchange);
		ServerHttpRequest request = exchange.getRequest();
		final HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
		final String url = requestUrl.toASCIIString();
		HttpHeaders filtered = filterRequest(getHeadersFilters(), exchange);
		final DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
		filtered.forEach(httpHeaders::set);
		boolean preserveHost = exchange.getAttributeOrDefault(PRESERVE_HOST_HEADER_ATTRIBUTE, false);
		Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
		// 獲取HttpClient,并進行send
		Flux<HttpClientResponse> responseFlux = getHttpClient(route, exchange).headers(headers -> {
			headers.add(httpHeaders);
			// Will either be set below, or later by Netty
			headers.remove(HttpHeaders.HOST);
			if (preserveHost) {
				String host = request.getHeaders().getFirst(HttpHeaders.HOST);
				headers.add(HttpHeaders.HOST, host);
			}
		}).request(method).uri(url).send((req, nettyOutbound) -> {
			if (log.isTraceEnabled()) {
				nettyOutbound.withConnection(connection -> log.trace("outbound route: "
						+ connection.channel().id().asShortText() + ", inbound: " + exchange.getLogPrefix()));
			}
			return nettyOutbound.send(request.getBody().map(this::getByteBuf));
		}).responseConnection((res, connection) -> {
			// 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);
			exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);
			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()));
			String contentTypeValue = headers.getFirst(HttpHeaders.CONTENT_TYPE);
			if (StringUtils.hasLength(contentTypeValue)) {
				exchange.getAttributes().put(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR, contentTypeValue);
			}
			setResponseStatus(res, response);
			// make sure headers filters run after setting status so it is
			// available in response
			HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter(getHeadersFilters(), headers, exchange,
					Type.RESPONSE);
			if (!filteredResponseHeaders.containsKey(HttpHeaders.TRANSFER_ENCODING)
					&& filteredResponseHeaders.containsKey(HttpHeaders.CONTENT_LENGTH)) {
				// It is not valid to have both the transfer-encoding header and
				// the content-length header.
				// Remove the transfer-encoding header in the response if the
				// content-length header is present.
				response.getHeaders().remove(HttpHeaders.TRANSFER_ENCODING);
			}
			exchange.getAttributes().put(CLIENT_RESPONSE_HEADER_NAMES, filteredResponseHeaders.keySet());
			response.getHeaders().addAll(filteredResponseHeaders);
			return Mono.just(res);
		});
		Duration responseTimeout = getResponseTimeout(route);
		if (responseTimeout != null) {
			responseFlux = responseFlux
					.timeout(responseTimeout,
							Mono.error(new TimeoutException("Response took longer than timeout: " + responseTimeout)))
					.onErrorMap(TimeoutException.class,
							th -> new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, th.getMessage(), th));
		}
		return responseFlux.then(chain.filter(exchange));
	}
}

到此這篇關于SpringCloud Gateway路由核心原理解析的文章就介紹到這了,更多相關SpringCloud Gateway路由內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Mybatis 入門示例代碼之 Association

    Mybatis 入門示例代碼之 Association

    這篇文章主要介紹了Mybatis 入門示例代碼之 Association,需要的的朋友參考下
    2017-02-02
  • Java解析JSON數(shù)據(jù)時報錯問題解決方案

    Java解析JSON數(shù)據(jù)時報錯問題解決方案

    這篇文章主要介紹了Java解析JSON數(shù)據(jù)時報錯問題解決方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-10-10
  • Mybatis調用Oracle存儲過程的方法圖文詳解

    Mybatis調用Oracle存儲過程的方法圖文詳解

    這篇文章主要介紹了Mybatis調用Oracle存儲過程的方法介紹,需要的朋友可以參考下
    2017-09-09
  • Java設計模式之單態(tài)模式(Singleton模式)介紹

    Java設計模式之單態(tài)模式(Singleton模式)介紹

    這篇文章主要介紹了Java設計模式之單態(tài)模式(Singleton模式)介紹,本文講解了如何使用單例模式、使用單例模式注意事項等內容,需要的朋友可以參考下
    2015-03-03
  • dubbo服務整合zipkin詳解

    dubbo服務整合zipkin詳解

    這篇文章主要介紹了dubbo服務整合zipkin,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 詳解springboot的多種配置方式

    詳解springboot的多種配置方式

    這篇文章主要介紹了springboot的多種配置方式,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • Java實現(xiàn)文件檢索系統(tǒng)的示例代碼

    Java實現(xiàn)文件檢索系統(tǒng)的示例代碼

    這篇文章主要為大家詳細介紹了如何劉Java語言實現(xiàn)簡易的文件檢索系統(tǒng),文中的示例代碼講解詳細,對我們學習Java開發(fā)有一定的幫助,需要的可以參考一下
    2022-07-07
  • Java網絡IO模型詳解(BIO、NIO、AIO)

    Java網絡IO模型詳解(BIO、NIO、AIO)

    Java支持BIO、NIO和AIO三種網絡IO模型,BIO是同步阻塞模型,適用于連接數(shù)較少的場景,NIO是同步非阻塞模型,適用于處理多個連接,支持自JDK1.4起,AIO是異步非阻塞模型,適用于異步操作多的場景,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-10-10
  • java中a=a+1和a+=1的區(qū)別介紹

    java中a=a+1和a+=1的區(qū)別介紹

    這篇文章主要介紹了java中a=a+1和a+=1的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java?DirectByteBuffer堆外內存回收詳解

    Java?DirectByteBuffer堆外內存回收詳解

    這篇文章主要為大家詳細介紹了Java中發(fā)DirectByteBuffer堆外內存回收,文中的示例代碼講解詳細,具有一定的借鑒價值,需要的可以參考一下
    2022-10-10

最新評論