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。
可以在首頁qq群加群獲取代碼