javascript
从0开始构建你的api网关--Spring Cloud Gateway网关实战及原理解析
API 網(wǎng)關(guān)
API 網(wǎng)關(guān)出現(xiàn)的原因是微服務(wù)架構(gòu)的出現(xiàn),不同的微服務(wù)一般會(huì)有不同的網(wǎng)絡(luò)地址,而外部客戶端可能需要調(diào)用多個(gè)服務(wù)的接口才能完成一個(gè)業(yè)務(wù)需求,如果讓客戶端直接與各個(gè)微服務(wù)通信,會(huì)有以下的問題:
以上這些問題可以借助 API 網(wǎng)關(guān)解決。API 網(wǎng)關(guān)是介于客戶端和服務(wù)器端之間的中間層,所有的外部請求都會(huì)先經(jīng)過 API 網(wǎng)關(guān)這一層。也就是說,API 的實(shí)現(xiàn)方面更多的考慮業(yè)務(wù)邏輯,而安全、性能、監(jiān)控可以交由 API 網(wǎng)關(guān)來做,這樣既提高業(yè)務(wù)靈活性又不缺安全性,典型的架構(gòu)圖如圖所示:
使用 API 網(wǎng)關(guān)后的優(yōu)點(diǎn)如下:
- 易于監(jiān)控。可以在網(wǎng)關(guān)收集監(jiān)控?cái)?shù)據(jù)并將其推送到外部系統(tǒng)進(jìn)行分析。
- 易于認(rèn)證。可以在網(wǎng)關(guān)上進(jìn)行認(rèn)證,然后再將請求轉(zhuǎn)發(fā)到后端的微服務(wù),而無須在每個(gè)微服務(wù)中進(jìn)行認(rèn)證。
- 減少了客戶端與各個(gè)微服務(wù)之間的交互次數(shù)。
API 網(wǎng)關(guān)選型
業(yè)界的情況:
我前面的文章<Netflix網(wǎng)關(guān)zuul(1.x和2.x)全解析>已經(jīng)介紹了zuul1 和zuul2,現(xiàn)在就嘗試從實(shí)例入手介紹一下spring cloud gateway
首先我們一步步實(shí)現(xiàn)一個(gè)最簡單的網(wǎng)關(guān)例子
步驟1:在http://start.spring.io網(wǎng)站上創(chuàng)建一個(gè)spring-cloud-gateway-example項(xiàng)目,依賴spring-cloud-gateway,如下圖所示
此時(shí)生產(chǎn)了一個(gè)spring-cloud-gateway-example的空項(xiàng)目包,pom.xml文件如下
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>spring-cloud-gateway-example</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-cloud-gateway-example</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><spring-cloud.version>Greenwich.RELEASE</spring-cloud.version></properties> <dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies> <dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url></repository></repositories></project>2.創(chuàng)建一個(gè)Route實(shí)例的配置類GatewayRoutes
package com.example.springcloudgatewayexample;import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class GatewayRoutes {@Beanpublic RouteLocator routeLocator(RouteLocatorBuilder builder) {return builder.routes().route(r ->r.path("/java/**").filters(f -> f.stripPrefix(1)).uri("http://localhost:8090/helloWorld")).build();} }當(dāng)然,也可以不適用配置類,使用配置文件,如下圖所示
spring:cloud:gateway:routes: - predicates:- Path=/java/**filters:- StripPrefix=1uri: "http://localhost:8090/helloWorld"不過,為了調(diào)試方便,我們使用配置類方式。
此時(shí)項(xiàng)目已經(jīng)完成,足夠簡單吧。
3.啟動(dòng)此項(xiàng)目
? >>因api網(wǎng)關(guān)需要轉(zhuǎn)發(fā)到一個(gè)服務(wù)上,本文為http://localhost:8090/helloWorld,那需要先啟動(dòng)我上文<spring boot整合spring5-webflux從0開始的實(shí)戰(zhàn)及源碼解析>,你也可以創(chuàng)建一個(gè)普通的web項(xiàng)目,啟動(dòng)端口設(shè)置為8090,然后啟動(dòng)。
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.3.RELEASE)2019-02-21 09:29:07.450 INFO 11704 --- [ main] c.e.demo.Spring5WebfluxApplication : Starting Spring5WebfluxApplication on DESKTOP-405G2C8 with PID 11704 (E:\workspaceForCloud\spring5-webflux\target\classes started by dell in E:\workspaceForCloud\spring5-webflux) 2019-02-21 09:29:07.455 INFO 11704 --- [ main] c.e.demo.Spring5WebfluxApplication : No active profile set, falling back to default profiles: default 2019-02-21 09:29:09.409 INFO 11704 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8090 2019-02-21 09:29:09.413 INFO 11704 --- [ main] c.e.demo.Spring5WebfluxApplication : Started Spring5WebfluxApplication in 2.304 seconds (JVM running for 7.311)?>>以spring boot方式啟動(dòng)spring-cloud-gateway-example項(xiàng)目,日志如下
2019-02-21 10:34:33.435 INFO 8580 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$1e059320] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.1.3.RELEASE)2019-02-21 10:34:33.767 INFO 8580 --- [ main] e.s.SpringCloudGatewayExampleApplication : No active profile set, falling back to default profiles: default 2019-02-21 10:34:34.219 INFO 8580 --- [ main] o.s.cloud.context.scope.GenericScope : BeanFactory id=d98183ec-3e46-38ba-ba4c-e976a1017dce 2019-02-21 10:34:34.243 INFO 8580 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$1e059320] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2019-02-21 10:34:44.367 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [After] 2019-02-21 10:34:44.367 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Before] 2019-02-21 10:34:44.367 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Between] 2019-02-21 10:34:44.367 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Cookie] 2019-02-21 10:34:44.367 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Header] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Host] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Method] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Path] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Query] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [ReadBodyPredicateFactory] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [RemoteAddr] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [Weight] 2019-02-21 10:34:44.368 INFO 8580 --- [ main] o.s.c.g.r.RouteDefinitionRouteLocator : Loaded RoutePredicateFactory [CloudFoundryRouteService] 2019-02-21 10:34:44.920 INFO 8580 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 8080 2019-02-21 10:34:44.923 INFO 8580 --- [ main] e.s.SpringCloudGatewayExampleApplication : Started SpringCloudGatewayExampleApplication in 12.329 seconds (JVM running for 13.126)4.測試,瀏覽器訪問http://localhost:8080/java/helloWorld
返回hello world !
5.從上面的代碼和配置及實(shí)例中,我們可以看出spring cloud gateway處理request請求的流程如下所示:
即在最前端,啟動(dòng)一個(gè)netty server(默認(rèn)端口為8080)接受請求,然后通過Routes(每個(gè)Route由Predicate(等同于HandlerMapping)和Filter(等同于HandlerAdapter))處理后通過Netty Client發(fā)給響應(yīng)的微服務(wù)。
那么在gateway本身最重要的應(yīng)該是Route(Netty Server和Client已經(jīng)封裝好了),它由RouteLocatorBuilder構(gòu)建,內(nèi)部包含Predicate和Filter,
private Route(String id, URI uri, int order, AsyncPredicate<ServerWebExchange> predicate, List<GatewayFilter> gatewayFilters) {this.id = id;this.uri = uri;this.order = order;this.predicate = predicate;this.gatewayFilters = gatewayFilters;}那么我們就來探討一下這兩個(gè)組件吧
5.1.Predicate
Predicte由PredicateSpec來構(gòu)建,主要實(shí)現(xiàn)有:
以path為例
/*** A predicate that checks if the path of the request matches the given pattern* @param patterns the pattern to check the path against.* The pattern is a {@link org.springframework.util.PathMatcher} pattern* @return a {@link BooleanSpec} to be used to add logical operators*/public BooleanSpec path(String... patterns) {return asyncPredicate(getBean(PathRoutePredicateFactory.class).applyAsync(c -> c.setPatterns(Arrays.asList(patterns))));}PathRoutePredicateFactory中執(zhí)行
@Overridepublic Predicate<ServerWebExchange> apply(Config config) {final ArrayList<PathPattern> pathPatterns = new ArrayList<>();synchronized (this.pathPatternParser) {pathPatternParser.setMatchOptionalTrailingSeparator(config.isMatchOptionalTrailingSeparator());config.getPatterns().forEach(pattern -> {PathPattern pathPattern = this.pathPatternParser.parse(pattern);pathPatterns.add(pathPattern);});}return exchange -> {PathContainer path = parsePath(exchange.getRequest().getURI().getPath());Optional<PathPattern> optionalPathPattern = pathPatterns.stream().filter(pattern -> pattern.matches(path)).findFirst();if (optionalPathPattern.isPresent()) {PathPattern pathPattern = optionalPathPattern.get();traceMatch("Pattern", pathPattern.getPatternString(), path, true);PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());return true;}else {traceMatch("Pattern", config.getPatterns(), path, false);return false;}};}5.2.Filter
Filter分兩種,一種GatewayFilter,一種GlobalFilter
5.2.1 GatewayFilter
GatewayFilter由GatewayFilterSpec構(gòu)建,GatewayFilter的構(gòu)建器
?
5.2.2?GlobalFilter
5.3 GlobalFilter和GatewayFilter的聯(lián)系
FilteringWebHandler.GatewayFilterAdapter代理了GlobalFilter
6.總結(jié)
? 本文從一個(gè)spring-cloud-gateway實(shí)例入手,深入淺出的介紹了spring-cloud-gateway的組件,并從源碼角度給出了實(shí)現(xiàn)的原理。
? ?spring-cloud-gateway在最前端,啟動(dòng)一個(gè)netty server(默認(rèn)端口為8080)接受請求,然后通過Routes(每個(gè)Route由Predicate(等同于HandlerMapping)和Filter(等同于HandlerAdapter))處理后通過Netty Client發(fā)給響應(yīng)的微服務(wù)。
?Predicate和Filter的各個(gè)實(shí)現(xiàn)定義了spring-cloud-gateway擁有的功能。
參考資料:
【1】https://www.infoq.cn/article/comparing-api-gateway-performances
【2】https://dzone.com/articles/spring-cloud-gateway-configuring-a-simple-route
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/10411451.html
總結(jié)
以上是生活随笔為你收集整理的从0开始构建你的api网关--Spring Cloud Gateway网关实战及原理解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring boot整合spring5
- 下一篇: Lucene 中的Tokenizer,