Spring Cloud Gateway使用簡介
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=<b>true</b> spring.cloud.gateway.discovery.locator.lowerCaseServiceId=<b>true</b> eureka.instance.preferIpAddress=<b>true</b> eureka.client.serviceUrl.defaultZone=http:<font><i>//localhost:8761/eureka/</i></font><font> management.endpoints.web.exposure.include: gateway </font>
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的話,就可以看到以下:
[ { <font>"route_id"</font><font>: </font><font>"CompositeDiscoveryClient_ACCTSVI"</font><font>, </font><font>"route_definition"</font><font>: { </font><font>"id"</font><font>: </font><font>"CompositeDiscoveryClient_ACCTSVI"</font><font>, </font><font>"predicates"</font><font>: [ { </font><font>"name"</font><font>: </font><font>"Path"</font><font>, </font><font>"args"</font><font>: { </font><font>"pattern"</font><font>: </font><font>"/acctsvi/**"</font><font> } } ], </font><font>"filters"</font><font>: [ { </font><font>"name"</font><font>: </font><font>"RewritePath"</font><font>, </font><font>"args"</font><font>: { </font><font>"regexp"</font><font>: </font><font>"/acctsvi/(?<remaining>.*)"</font><font>, </font><font>"replacement"</font><font>: </font><font>"/${remaining}"</font><font> } } ], </font><font>"uri"</font><font>: </font><font>"lb://ACCTSVI"</font><font>, </font><font>"order"</font><font>: 0 }, </font><font>"order"</font><font>: 0 }, ... ] </font>
每個路由設定會有個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就可以得到以下回應:
{ <font>"name"</font><font>: </font><font>"caterpillar"</font><font>, </font><font>"email"</font><font>: </font><font>"[email protected]"</font><font>, </font><font>"password"</font><font>: </font><font>"$2a$10$CEkPOmd.Uid2FpIOHA6Cme1G.mvhWfelv2hPu7cxZ/vq2drnXaVo."</font><font>, </font><font>"_links"</font><font>: { </font><font>"self"</font><font>: { </font><font>"href"</font><font>: </font><font>"http://Justin-2017:8084/accountByNameEmail?username=caterpillar"</font><font> } } } </font>
如果想要自定義路由,可以寫個application.yml(若不想自動建立路由,可以將spring.cloud.gateway.discovery.locator.enabled與spring.cloud.gateway.discovery.locator.lowerCaseServiceId註解掉):
spring: application: name: gateway cloud: gateway: routes: - predicates: - Path=/acct<font><i>/** filters: - StripPrefix=1 uri: lb://acctsvi - predicates: - Path=/msg/** filters: - StripPrefix=1 uri: lb://msgsvi - predicates: - Path=/email/** filters: - StripPrefix=1 uri: lb://email </i></font>
上述配置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 中找到以上的原始碼