Spring Cloud Gateway網關實戰及原理解析


API 網關

API 網關出現的原因是微服務架構的出現,不同的微服務一般會有不同的網絡地址,而外部客戶端可能需要調用多個服務的接口才能完成一個業務需求,如果讓客戶端直接與各個微服務通信,會有以下的問題:
  • 客戶端會多次請求不同的微服務,增加了客戶端的復雜性。
  • 存在跨域請求,在一定場景下處理相對復雜。
  • 認證復雜,每個服務都需要獨立認證。
  • 難以重構,隨着項目的迭代,可能需要重新划分微服務。例如,可能將多個服務合並成一個或者將一個服務拆分成多個。如果客戶端直接與微服務通信,那么重構將會很難實施。
  • 某些微服務可能使用了防火牆 / 瀏覽器不友好的協議,直接訪問會有一定的困難。
以上這些問題可以借助 API 網關解決。API 網關是介於客戶端和服務器端之間的中間層,所有的外部請求都會先經過 API 網關這一層。也就是說,API 的實現方面更多的考慮業務邏輯,而安全、性能、監控可以交由 API 網關來做,這樣既提高業務靈活性又不缺安全性,典型的架構圖如圖所示:
使用 API 網關后的優點如下:
  • 易於監控。可以在網關收集監控數據並將其推送到外部系統進行分析。
  • 易於認證。可以在網關上進行認證,然后再將請求轉發到后端的微服務,而無須在每個微服務中進行認證。
  • 減少了客戶端與各個微服務之間的交互次數。

API 網關選型

業界的情況:
 
我前面的文章<Netflix網關zuul(1.x和2.x)全解析>已經介紹了zuul1 和zuul2,現在就嘗試從實例入手介紹一下spring cloud gateway
首先我們一步步實現一個最簡單的網關例子
步驟1:在http://start.spring.io網站上創建一個spring-cloud-gateway-example項目,依賴spring-cloud-gateway,如下圖所示
 
此時生產了一個spring-cloud-gateway-example的空項目包,pom.xml文件如下
[XML]  純文本查看 復制代碼
?
1
<? 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 [url]http://maven.apache.org/xsd/maven-4.0.0.xsd[/url]" >    < 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 >[url]https://repo.spring.io/milestone</ url >[/url]        </ repository >    </ repositories ></ project >[url=]

 

2.創建一個Route實例的配置類GatewayRoutes
[Java]  純文本查看 復制代碼
?
1
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; @Configurationpublic class GatewayRoutes {    @Bean    public RouteLocator routeLocator(RouteLocatorBuilder builder) {        return builder.routes()                .route(r ->                        r.path( "/java/**" )                                .filters(                                        f -> f.stripPrefix( 1 )                                )                                .uri( "http://localhost:8090/helloWorld" )                )                .build();    }}

 

當然,也可以不適用配置類,使用配置文件,如下圖所示
[Java]  純文本查看 復制代碼
?
1
spring:  cloud:    gateway:      routes:         - predicates:            - Path=/java/**          filters:            - StripPrefix= 1          uri: "http://localhost:8090/helloWorld"


不過,為了調試方便,我們使用配置類方式。
此時項目已經完成,足夠簡單吧。
3.啟動此項目
  >>因api網關需要轉發到一個服務上,本文為http://localhost:8090/helloWorld,那需要先啟動我上文<spring boot整合spring5-webflux從0開始的實戰及源碼解析>,你也可以創建一個普通的web項目,啟動端口設置為8090,然后啟動。
[Shell]  純文本查看 復制代碼
?
1
. ____ _ __ _ _/\\ / ___ '_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | ' _ | '_| | ' _ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/= /_/_/_/ :: 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: default2019-02-21 09:29:09.409 INFO 11704 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 80902019-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方式啟動spring-cloud-gateway-example項目,日志如下
[Shell]  純文本查看 復制代碼
?
1
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: default2019-02-21 10:34:34.219  INFO 8580 --- [           main] o.s.cloud.context.scope.GenericScope     : BeanFactory id=d98183ec-3e46-38ba-ba4c-e976a1017dce2019-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): 80802019-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.從上面的代碼和配置及實例中,我們可以看出spring cloud gateway處理request請求的流程如下所示:
 
即在最前端,啟動一個netty server(默認端口為8080)接受請求,然后通過Routes(每個Route由Predicate(等同於HandlerMapping)和Filter(等同於HandlerAdapter))處理后通過Netty Client發給響應的微服務。
那么在gateway本身最重要的應該是Route(Netty Server和Client已經封裝好了),它由RouteLocatorBuilder構建,內部包含Predicate和Filter,
[Java]  純文本查看 復制代碼
?
1
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;    }


那么我們就來探討一下這兩個組件吧
5.1.Predicate
Predicte由PredicateSpec來構建,主要實現有:
 
以path為例
[Java]  純文本查看 復制代碼
?
1
/**     * 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中執行
[Java]  純文本查看 復制代碼
?
1
@Override    public 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構建,GatewayFilter的構建器
 
5.2.2 GlobalFilter
 
5.3 GlobalFilter和GatewayFilter的聯系
FilteringWebHandler.GatewayFilterAdapter代理了GlobalFilter
6.總結
  本文從一個spring-cloud-gateway實例入手,深入淺出的介紹了spring-cloud-gateway的組件,並從源碼角度給出了實現的原理。
   spring-cloud-gateway在最前端,啟動一個netty server(默認端口為8080)接受請求,然后通過Routes(每個Route由Predicate(等同於HandlerMapping)和Filter(等同於HandlerAdapter))處理后通過Netty Client發給響應的微服務。
Predicate和Filter的各個實現定義了spring-cloud-gateway擁有的功能。
參考資料:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM