Spring Cloud Gateway使用簡介

banq發表於2019-02-01

Spring Cloud Gateway是類似Nginx的閘道器路由代理,有替代原來Spring cloud zuul之意:
Spring 5 推出了自己的Spring Cloud Gateway,支援Java 8、Reactor API,可在Spring Boot 2 使用,看到了響應式元件Reactor,可以理解這個閘道器方案目標之一是能夠採用Reactive 來實現高效率的閘道器。
想要建立一個Spring Cloud Gateway 的話,在Spring Tool Suite 上可以選擇「Gateway」這個Starter,為了能註冊到服務發現伺服器,也為了能開放gateway/routes 端點,以便觀察路由資訊,就順便加入Eureka與Actuator 的Starter,比如在build.gradle 中可以包含:

implementation('org.springframework.boot:spring-boot-starter-actuator')  
implementation('org.springframework.cloud:spring-cloud-starter-gateway')
implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')


Spring Cloud Gateway 可以在註冊伺服器上註冊的服務ID,自動建立路由資訊,為此,可以如下設定bootstrap.properties:

server.port=5555

spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true

eureka.instance.preferIpAddress=true
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

management.endpoints.web.exposure.include: gateway


spring.cloud.gateway.discovery.locator.enabled啟用了自動根據服務ID建立路由,路由的路徑對應會使用大寫ID,若想要使用小寫ID,可將spring.cloud.gateway.discovery.locator.lowerCaseServiceId設為true;在設定中也開放了gateway端點。必要時,可以使用RouteLocator實現自定義路由的方式。

接下來啟動相關服務,啟動Spring Cloud Gateway,預設會跑在Netty上,如果測試請求http://localhost:5555/actuator/gateway/routes的話,就可以看到以下:

[
    {
        "route_id": "CompositeDiscoveryClient_ACCTSVI",
        "route_definition": {
            "id": "CompositeDiscoveryClient_ACCTSVI",
            "predicates": [
                {
                    "name": "Path",
                    "args": {
                        "pattern": "/acctsvi/**"
                    }
                }
            ],
            "filters": [
                {
                    "name": "RewritePath",
                    "args": {
                        "regexp": "/acctsvi/(?<remaining>.*)",
                        "replacement": "/${remaining}"
                    }
                }
            ],
            "uri": "lb://ACCTSVI",
            "order": 0
        },
        "order": 0
    },
    ...
]


每個路由設定會有個route_id作為識別,在路由定義的predicates中,可以看到設定了Path,這是Spring Cloud Gateway內建的斷言器工廠Bean名稱,pattern這個設定表示對於http://localhost:5555/acctsvi/xxxx的請求會轉給uri設定的值:lb://ACCTSVI,也就是說路由轉給了服務ID為ACCTSVI的服務。

filters中設定了RewritePath,這是個過濾器工廠Bean名稱,依照regexp的規則,會捕捉請求中的/acctsvi/之後的部份,套用至服務的URI上,也就是http://localhost:5555/acctsvi/xxxx的請求,將會路由轉發至http://acctsvi-uri/xxxx。

predicates與filters是Spring Cloud Gateway的重要特性,predicates斷言哪些路徑符合路由定義,filters設定具體哪些路徑適用什麼樣的具體過濾器,除了設定之外,必要時,都可以程式碼自己定義。

Spring Cloud Gateway也內建了一些斷言器工廠過濾器工廠,這些工廠類別,是可以透過屬性檔來定義的,必要時,也可以自定義工廠類別

就以上的設定來說,請求http://localhost:5555/acctsvi/accountByName?username=caterpillar就可以得到以下回應:

{
    "name": "caterpillar",
    "email": "caterpillar@openhome.cc",
    "password": "$2a$10$CEkPOmd.Uid2FpIOHA6Cme1G.mvhWfelv2hPu7cxZ/vq2drnXaVo.",
    "_links": {
        "self": {
            "href": "http://Justin-2017:8084/accountByNameEmail?username=caterpillar"
        }
    }
}

如果想要自定義路由,可以寫個application.yml(若不想自動建立路由,可以將spring.cloud.gateway.discovery.locator.enabled與spring.cloud.gateway.discovery.locator.lowerCaseServiceId註解掉):

spring:
    application:
            name: gateway
    cloud:
        gateway:
            routes: 
                - predicates:
                    - Path=/acct/**
                  filters:
                      - StripPrefix=1
                  uri: lb://acctsvi
                - predicates:
                    - Path=/msg/**
                  filters:
                      - StripPrefix=1
                  uri: lb://msgsvi     
                - predicates:
                    - Path=/email/**
                  filters:
                      - StripPrefix=1
                  uri: lb://email         


上述配置filters中的StripPrefix也是內建的過濾器工廠Bean名稱,設定值為1表示將路由中的第一個層去除,其餘保留用來轉發請求,請求http://localhost:5555/actuator/gateway/routes的話,就可以看到以下:

[
    {
        "route_id": "545d278b-192b-4370-8156-161815957f91",
        "route_definition": {
            "id": "545d278b-192b-4370-8156-161815957f91",
            "predicates": [
                {
                    "name": "Path",
                    "args": {
                        "_genkey_0": "/acct/**"
                    }
                }
            ],
            "filters": [
                {
                    "name": "StripPrefix",
                    "args": {
                        "_genkey_0": "1"
                    }
                }
            ],
            "uri": "lb://acctsvi",
            "order": 0
        },
        "order": 0
    },
    ...
]


也就是對http://localhost:5555/acct/accountByName?username=caterpillar的請求,會轉給http://acctsvi-url/accountByName?username=caterpillar。

如果想要設定api前置路徑,就是修改一下StripPrefix=1為StripPrefix=2:

spring:
    application:
            name: gateway
    cloud:
        gateway:
            default-filters:
                - StripPrefix=2
            routes: 
                - predicates:
                    - Path=/api/acct/**
                  uri: lb://acctsvi
                - predicates:
                    - Path=/api/msg/**
                  uri: lb://msgsvi     
                - predicates:
                    - Path=/api/email/**
                  uri: lb://email               


對於每個路由都要套用的過濾器,可以使用default-filters來設定,就以上設定來說,可以請求http://localhost:5555/api/acct/accountByName?username=caterpillar來取得使用者資訊。
一開始自動根據服務ID建立路由時,可以看到RewritePath,它也是內建的過濾器工廠,可以運用規則表示式來進行路徑重寫,因此,也可以這麼設定api前置:

spring:
    application:
            name: gateway
    cloud:
        gateway:
            default-filters:
                - RewritePath=/api/.*?/(?<remaining>.*), /$\{remaining}
            routes: 
                - predicates:
                    - Path=/api/acct/**
                  uri: lb://acctsvi
                - predicates:
                    - Path=/api/msg/**
                  uri: lb://msgsvi     
                - predicates:
                    - Path=/api/email/**
                  uri: lb://email            



就目前的設定來說,在客戶端的部份,〈使用Zuul〉中的gossip就可以了,畢竟互動的介面沒有改變,但是因為使用spring.application.gateway作為應用代理了,還是要記得改一下@FeignClient中的服務ID為gateway。

可以在Gateway中找到以上的原始碼

相關文章