Spring Cloud服務網關:Gateway

目錄

1 傳統路由方式

1.1 pom.xml

1.2 啓動類

1.3 路由方式

1.3.1 編碼方式

1.3.2 配置方式

1.4 運行及結果

2 面向服務的方式

2.1 pom.xml

2.2 application.properties

2.3 運行及結果

3 Predicate和Filter

3.1 Predicate

3.1.1 時間匹配

3.1.2 請求方式匹配

3.1.3 請求路徑匹配

3.1.4 請求參數匹配

3.2 Filter

3.2.1 hello-service

3.2.2 feign-consumer

3.2.3 application.properties

3.2.4 運行及結果


由於Zuul 2.x的不斷跳票,Spring Cloud自行研發了另外一款服務網關產品:Spring Cloud Gateway,並且在最新版本中推薦使用,所以Gateway出現的原因就是爲了代替Zuul。相比Zuul,Gateway是Spring體系內的產物,和Spring融合更好。同時相比於Zuul 1.x的阻塞和多線程方式,Gateway採用了Netty異步非阻塞模型,佔用資源更小,性能更有優勢。同時增加了Predicate和限流等功能。

筆者使用的Java版本是jdk-8u201,IDE使用的是IntelliJ IDEA 2019.2 x64,Spring Boot的版本是2.1.7.RELEASE,Spring Cloud的版本是Greenwich.SR2。同時本文所使用的項目代碼沿用筆者之前寫過的文章《Spring Cloud服務治理:Eureka+OpenFeign(Ribbon+Hystrix)》中的項目代碼,並在此基礎上進行繼續開發。


1 傳統路由方式

創建一個Spring Boot項目,命名爲api-gateway。

1.1 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.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.hys</groupId>
    <artifactId>api-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>api-gateway</name>
    <description>Demo project for Spring Cloud</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Greenwich.SR2</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>

</project>

1.2 啓動類

package com.hys.apigateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApiGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApplication.class, args);
    }

}

1.3 路由方式

Gateway的路由方式有兩種,分別爲編碼方式和配置方式。

1.3.1 編碼方式

在上面的啓動類中加入下面的自定義RouteLocator的方法即可:

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("route_a", r -> r.path("/hello")
                        .uri("http://localhost:8081/"))
                .build();
    }

1.3.2 配置方式

將上面自定義RouteLocator的方法註釋掉,在application.properties文件中輸入下面的內容:

spring.application.name=api-gateway
server.port=5555
spring.cloud.gateway.routes[0].id=route_a
spring.cloud.gateway.routes[0].uri=http://localhost:8081/
spring.cloud.gateway.routes[0].predicates[0]=Path=/hello

其中後三行的內容和上述編碼配置的方式實現的效果是一樣的。

1.4 運行及結果

這裏採用配置文件的方式來運行,確保之前搭建的eureka-server、hello-service和feign-consumer項目都運行起來,啓動本項目,頁面輸入http://localhost:5555/hello,結果如下所示:

訪問http://localhost:5555/hello會被自動路由到http://localhost:8081/hello,這樣就驗證了路由轉發的成功。


2 面向服務的方式

顯而易見的是,傳統路由的配置方式比較繁瑣,如果路由特別多的情況下,維護起來會很麻煩。爲此,可以將Gateway與Eureka整合起來,這樣不用再寫具體的url映射,url交給Eureka的服務發現機制去自動維護。

2.1 pom.xml

pom沿用上面的配置,只需要再加入下面的Eureka依賴即可:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

2.2 application.properties

spring.application.name=api-gateway
server.port=5555
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true
eureka.client.service-url.defaultZone=http://peer2:1112/eureka/,http://peer3:1113/eureka/

其中spring.cloud.gateway.discovery.locator.enabled設置爲true表示開啓通過註冊中心進行路由轉發的功能,spring.cloud.gateway.discovery.locator.lowerCaseServiceId設置爲true表示通過小寫形式來訪問服務名稱。

2.3 運行及結果

 重啓本項目,頁面分別訪問http://localhost:5555/feign-consumer/feign-consumerhttp://localhost:5555/feign-consumer/feign-consumer2http://localhost:5555/feign-consumer/feign-consumer3,結果如下所示:

可以看到,和之前通過OpenFeign的消費者訪問的結果是一樣的,路由轉發是成功的。


3 Predicate和Filter

Predicate和Filter是Gateway中的核心,Predicate是選擇哪些請求需要處理,而Filter給選擇出來的請求做一些改動,比如參數處理和安全校驗等等。

3.1 Predicate

新建一個Spring Boot項目,命名爲test-gateway,pom文件依賴和上述第1.1節中的pom依賴一致。這裏我們用Postman來查看運行結果。

3.1.1 時間匹配

spring.application.name=gateway-test
server.port=5556
spring.cloud.gateway.routes[0].id=route_test
spring.cloud.gateway.routes[0].uri=https://www.baidu.com/
spring.cloud.gateway.routes[0].predicates[0]=After=2019-08-12T12:00:00+08:00[Asia/Shanghai]

上述After表示在2019年8月12日12點之後的請求可以被路由,而Before代表在指定時間之前可以被路由,Between則代表在指定的時間區隔之內可以被路由:

After=2019-08-12T12:00:00+08:00[Asia/Shanghai]
Before=2019-08-12T12:00:00+08:00[Asia/Shanghai]
Between=2019-08-12T12:00:00+08:00[Asia/Shanghai], 2019-08-13T12:00:00+08:00[Asia/Shanghai]

3.1.2 請求方式匹配

spring.application.name=gateway-test
server.port=5556
spring.cloud.gateway.routes[0].id=route_test
spring.cloud.gateway.routes[0].uri=https://www.baidu.com/
spring.cloud.gateway.routes[0].predicates[0]=Method=GET

上述表示只有GET請求才能被成功路由,訪問Postman得到如下結果:

狀態碼爲200,說明成功訪問,這時我們改成POST請求,再來訪問:

狀態碼爲404,說明訪問失敗。

3.1.3 請求路徑匹配

spring.application.name=gateway-test
server.port=5556
spring.cloud.gateway.routes[0].id=route_test
spring.cloud.gateway.routes[0].uri=https://www.baidu.com/
spring.cloud.gateway.routes[0].predicates[0]=Path=/foo/{segment}

由上配置了匹配的請求路徑,Postman訪問http://localhost:5556/foo/1,訪問成功:

訪問http://localhost:5556/foo/1/2,訪問失敗:

3.1.4 請求參數匹配

spring.application.name=gateway-test
server.port=5556
spring.cloud.gateway.routes[0].id=route_test
spring.cloud.gateway.routes[0].uri=https://www.baidu.com/
spring.cloud.gateway.routes[0].predicates[0]=Query=p1

上述配置了請求參數中必須含有p1參數才能路由成功,Postman訪問http://localhost:5556/?p1=1,路由成功:

訪問http://localhost:5556/?p2=2,路由失敗:

Query的值還可以使用正則表達式來進行匹配,如下面的例子:

spring.application.name=gateway-test
server.port=5556
spring.cloud.gateway.routes[0].id=route_test
spring.cloud.gateway.routes[0].uri=https://www.baidu.com/
spring.cloud.gateway.routes[0].predicates[0]=Query=p1, 1.

上面配置了參數中的鍵必須含有p1,同時它所對應的值是以1開頭的兩個字符,Postman訪問http://localhost:5556/?p1=1s,路由成功:

Postman訪問http://localhost:5556/?p1=1,路由失敗:

3.2 Filter

這裏只演示AddRequestParameter的用法,更多的用法詳見Spring官網。

AddRequestParameter是在請求的路徑中添加相應的參數,我們繼續使用上述的api-gateway項目。

3.2.1 hello-service

首先需要對之前的hello-service項目做些更改,在其中的HelloController中添加一個foo方法如下所示:

    @RequestMapping("/foo")
    public String foo(String foo) {
        return foo;
    }

3.2.2 feign-consumer

然後在feign-consumer項目中的ConsumerController中添加下面的方法:

    @RequestMapping("/foo")
    public String foo(String foo) {
        return helloService.foo(foo);
    }

在IHelloService中添加下面的方法:

    @RequestMapping("/foo")
    String foo(@RequestParam("foo") String foo);

在相應的HelloServiceImplFallback降級類中填入下面的降級方法:

    @Override
    public String foo(String foo) {
        return "訪問超時,請重新再試!";
    }

3.2.3 application.properties

api-gateway網關的配置文件需要做些修改:

spring.application.name=api-gateway
server.port=5555
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.routes[0].id=add_request_parameter_route
spring.cloud.gateway.routes[0].uri=lb://hello-service
spring.cloud.gateway.routes[0].predicates[0]=Method=GET
spring.cloud.gateway.routes[0].filters[0]=AddRequestParameter=foo, bar
eureka.client.service-url.defaultZone=http://peer2:1112/eureka/,http://peer3:1113/eureka/

其中,uri表示配置路由轉發到hello-service的服務提供者。filters表示給匹配的請求中添加了一個foo=bar的參數。需要注意的是,filters必須和predicates聯用,否則項目啓動會失敗。

3.2.4 運行及結果

隨後分別啓動eureka-server、hello-service和feign-consumer項目,然後啓動api-gateway網關項目,首先頁面訪問http://localhost:9001/foo,以OpenFeign消費者的方式來訪問服務:

由上可以看到,當沒有使用網關來訪問服務的時候,頁面上沒有結果,也就是說服務提供者沒有接收到foo參數。然後我們訪問http://localhost:5555/foo,以網關的方式來訪問服務:

以上可知,頁面上顯示了bar,foo參數被成功接收,在請求中會添加一個foo=bar的參數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章