javascript
Spring Cloud Gateway介绍(二)
Spring Cloud Gateway介紹(一)
?
全局Filters
GlobalFilter與GatewayFilter具有一樣的接口。
這些全局過(guò)濾器都有對(duì)應(yīng)的配置類,例如:
- org.springframework.cloud.gateway.config.GatewayAutoConfiguration
- org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration
- org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration
?
聚合全局Filter和GatewayFilter并排序
當(dāng)一個(gè)Request匹配路由,則會(huì)把所有全局Filter和GatewayFilter組合成一個(gè)Filter Chain,并通過(guò)org.springframework.core.Ordered接口進(jìn)行排序,數(shù)值越小優(yōu)先級(jí)越高,高優(yōu)先級(jí)在pre階段排在前面,在post階段排在后面。
Foward Routing Filter
用于本地forward,也就是將請(qǐng)求在Gateway服務(wù)內(nèi)進(jìn)行轉(zhuǎn)發(fā),而不是轉(zhuǎn)發(fā)到下游服務(wù)。
當(dāng)請(qǐng)求進(jìn)來(lái)時(shí),ForwardRoutingFilter?會(huì)查看一個(gè)URL,該URL為 exchange 屬性?ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR?的值,如果該 url 的 scheme(協(xié)議) 是?forward(例如:forward://localendpoint),那么該Filter會(huì)使用Spirngd的DispatcherHandler?來(lái)處理這個(gè)請(qǐng)求。該請(qǐng)求的URL路徑部分,會(huì)被forward URL中的路徑覆蓋掉。而未修改過(guò)的原始URL,會(huì)被追加到?ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR?屬性中。
LoadBalancerClientFilter?
這個(gè)Filter是用來(lái)整合Ribbon的,其核心就是解析 scheme 為lb的 url,以此獲取微服務(wù)的名稱,然后再通過(guò)Ribbon獲取實(shí)際的調(diào)用地址。
當(dāng)請(qǐng)求進(jìn)來(lái)時(shí),LoadBalancerClientFilter?會(huì)查看一個(gè)URL,該URL為 exchange 的屬性?ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR?的值,如果該 url 的 scheme 是?lb,(例如:lb://myservice ),那么該Filter會(huì)使用Spring Cloud的?LoadBalancerClient?來(lái)將?myservice?解析成實(shí)際的host 和 port ,并替換掉原本?ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR?屬性的值。而原始 url 會(huì)追加到?ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR?屬性中。該過(guò)濾器還會(huì)查看?ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR?屬性,如果發(fā)現(xiàn)該屬性的值是?lb?,也會(huì)執(zhí)行相同邏輯。
spring:cloud:gateway:routes:- id: myRouteuri: lb://servicepredicates:- Path=/service/**默認(rèn)情況下,如果無(wú)法通過(guò)?LoadBalancer?找到指定服務(wù)的實(shí)例,那么會(huì)返回503(如上配置示例, 若?LoadBalancer?找不到名為 service 的實(shí)例時(shí),就會(huì)返回503);可使用配置:?spring.cloud.gateway.loadbalancer.use404=true?,讓其返回404。
LoadBalancer?返回的?ServiceInstance?的?isSecure?的值,會(huì)覆蓋請(qǐng)求的scheme。舉個(gè)例子,如果請(qǐng)求打到Gateway上使用的是 HTTPS ,但?ServiceInstance?的?isSecure?是false,那么下游微服務(wù)接收到的則是HTTP請(qǐng)求,反之亦然。另外,如果該路由指定了?GATEWAY_SCHEME_PREFIX_ATTR?屬性,那么前綴將會(huì)被剝離,并且路由URL中的scheme會(huì)覆蓋?ServiceInstance?的配置。
LoadBalancerClientFilter?默認(rèn)使用的是一個(gè)阻塞式的LoadBalancerClient?,建議使用ReactiveLoadBalancerClientFilter來(lái)替換,可以通過(guò)設(shè)置spring.cloud.loadbalancer.ribbon.enabled=false來(lái)切換。
ReactiveLoadBalancerClientFilter?
功能同LoadBalancerClientFilter?。
NettyRoutingFilter
當(dāng)請(qǐng)求進(jìn)來(lái)時(shí),NettyRoutingFilter?會(huì)查看一個(gè)URL,該URL是 exchange 的屬性?ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR?的值,如果該 url 的 scheme 是?http?或?https?,那么該Filter會(huì)使用 Netty 的?HttpClient?向下游的服務(wù)發(fā)送代理請(qǐng)求。獲得的響應(yīng)將放在 exchange 的?ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR?屬性中,以便在后面的 Filter 里使用。(有一個(gè)實(shí)驗(yàn)性的過(guò)濾器:?WebClientHttpRoutingFilter?可實(shí)現(xiàn)相同功能,但無(wú)需Netty)
NettyWriteResponseFilter?
NettyWriteResponseFilter?用于將代理響應(yīng)寫回網(wǎng)關(guān)的客戶端側(cè),所以該過(guò)濾器會(huì)在所有其他過(guò)濾器執(zhí)行完成后才執(zhí)行,并且執(zhí)行的條件是 exchange 中?ServerWebExchangeUtils.CLIENT_RESPONSE_CONN_ATTR?屬性的值不為空,該值為 Netty 的?Connection?實(shí)例。(有一個(gè)實(shí)驗(yàn)性的過(guò)濾器:?WebClientWriteResponseFilter?可實(shí)現(xiàn)相同功能,但無(wú)需Netty)。
RouteToRequestUrlFilter
這個(gè)過(guò)濾器用于將從request里獲取的原始url轉(zhuǎn)換成Gateway進(jìn)行請(qǐng)求轉(zhuǎn)發(fā)時(shí)所使用的url。當(dāng)請(qǐng)求進(jìn)來(lái)時(shí),RouteToRequestUrlFilter?會(huì)從 exchange 中獲取?ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR?屬性的值,該值是一個(gè)?Route?對(duì)象。若該對(duì)象不為空的話,RouteToRequestUrlFilter?會(huì)基于請(qǐng)求 URL 及?Route?對(duì)象里的 URL 來(lái)創(chuàng)建一個(gè)新的 URL。新 URL 會(huì)被放到 exchange 的?ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR?屬性中。
如果 URL 具有 scheme 前綴,例如?lb:ws://serviceid?,該?lb?scheme將從URL中剝離,并放到?ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR?中,方便后面的過(guò)濾器使用。
WebSocket Routing Filter
該過(guò)濾器的作用與?NettyRoutingFilter?類似。當(dāng)請(qǐng)求進(jìn)來(lái)時(shí),WebsocketRoutingFilter?會(huì)查看一個(gè)URL,該URL是 exchange 中?ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR?屬性的值,如果該 url 的 scheme 是?ws?或者?wss,那么該Filter會(huì)使用 Spring Web Socket 將 Websocket 請(qǐng)求轉(zhuǎn)發(fā)到下游。
另外,如果 Websocket 請(qǐng)求需要負(fù)載均衡的話,可為URL添加 lb 前綴以實(shí)現(xiàn)負(fù)載均衡,例如?lb:ws://serviceid?。
Gateway Metrics Filter
想要啟用Gateway Metrics Filter,需在項(xiàng)目中添加?spring-boot-starter-actuator?依賴,然后在配置文件中配置?spring.cloud.gateway.metrics.enabled?的值為true。該過(guò)濾器會(huì)添加名為?gateway.requests?的時(shí)序度量(timer metric),其中包含以下標(biāo)記:
- routeId:路由ID
- routeUri:API將路由到的URI
- outcome:由 HttpStatus.Series 分類
- status:返回給客戶端的Http Status
- httpStatusCode:返回給客戶端的請(qǐng)求的Http Status
- httpMethod:請(qǐng)求所使用的Http方法
這些指標(biāo)暴露在?/actuator/metrics/gateway.requests?端點(diǎn)中,并且可以輕松與 Prometheus 整合,從而創(chuàng)建一個(gè) Grafana dashboard。
Marking An Exchange As Routed
當(dāng)一個(gè)請(qǐng)求走完整條過(guò)濾器鏈后,負(fù)責(zé)轉(zhuǎn)發(fā)請(qǐng)求到下游的那個(gè)過(guò)濾器會(huì)在 exchange 中添加一個(gè)?gatewayAlreadyRouted?屬性,從而將 exchange 標(biāo)記為?routed(已路由)。一旦請(qǐng)求被標(biāo)記為?routed?,其他路由過(guò)濾器將不會(huì)再次路由該請(qǐng)求,而是直接跳過(guò)。
了解了以上所有內(nèi)置的全局過(guò)濾器后,我們知道不同協(xié)議的請(qǐng)求會(huì)由不同的過(guò)濾器轉(zhuǎn)發(fā)到下游。所以負(fù)責(zé)添加這個(gè)gatewayAlreadyRouted?屬性的過(guò)濾器就是最終負(fù)責(zé)轉(zhuǎn)發(fā)請(qǐng)求的過(guò)濾器:
- http、https請(qǐng)求會(huì)由NettyRoutingFilter或WebClientHttpRoutingFilter添加這個(gè)屬性
- forward請(qǐng)求會(huì)由ForwardRoutingFilter添加這個(gè)屬性
- websocket請(qǐng)求會(huì)由WebsocketRoutingFilter添加這個(gè)屬性
這些過(guò)濾器調(diào)用了以下方法將 exchange 標(biāo)記為?routed?,或檢查 exchange 是否是?routed: - ServerWebExchangeUtils.isAlreadyRouted:檢查exchange是否為routed狀態(tài)
- ServerWebExchangeUtils.setAlreadyRouted:將exchange設(shè)置為routed狀態(tài)
簡(jiǎn)單來(lái)說(shuō),就是Gateway通過(guò)?gatewayAlreadyRouted?屬性表示這個(gè)請(qǐng)求已經(jīng)轉(zhuǎn)發(fā)過(guò)了,而無(wú)需其他過(guò)濾器重復(fù)路由,從而防止重復(fù)的路由轉(zhuǎn)發(fā)。
HttpHeadersFilters
HttpHeadersFilters 在請(qǐng)求往下游發(fā)送之前應(yīng)用到headers,例如:NettyRoutingFilter。
Fowarded Headers Filter
添加Fowarded header,或者添加host header,當(dāng)前請(qǐng)求的schema,port到已存在Fowarded header。
RemoveHopbyHop Headers Filter
從一個(gè)已Fowarded的請(qǐng)求中刪除某些header,默認(rèn)值(來(lái)源于IETF)為:
-
Connection
-
Keep-Alive
-
Proxy-Authenticate
-
Proxy-Authorization
-
TE
-
Trailer
-
Transfer-Encoding
-
Upgrade
可以通過(guò)設(shè)置spring.cloud.gateway.filter.remove-non-proxy-headers.headers來(lái)修改默認(rèn)列表。
XFowarded Headers Filter
類似 Fowarded,添加X(jué)-Forwarded-*。
通過(guò)以下屬性控制(默認(rèn)為true):
-
spring.cloud.gateway.x-forwarded.for-enabled
-
spring.cloud.gateway.x-forwarded.host-enabled
-
spring.cloud.gateway.x-forwarded.port-enabled
-
spring.cloud.gateway.x-forwarded.proto-enabled
-
spring.cloud.gateway.x-forwarded.prefix-enabled
可以通過(guò)設(shè)置以下屬性(默認(rèn)為true)來(lái)控制是否append:
-
spring.cloud.gateway.x-forwarded.for-append
-
spring.cloud.gateway.x-forwarded.host-append
-
spring.cloud.gateway.x-forwarded.port-append
-
spring.cloud.gateway.x-forwarded.proto-append
-
spring.cloud.gateway.x-forwarded.prefix-append
TSL和SSL
gateway可以通過(guò)以下配置監(jiān)聽(tīng)HTTPS請(qǐng)求:
server:ssl:enabled: truekey-alias: scgkey-store-password: scg1234key-store: classpath:scg-keystore.p12key-store-type: PKCS12網(wǎng)關(guān)路由可以路由到 http 和 https 后端。如果路由到 https 后端,則可以將網(wǎng)關(guān)配置為信任具有以下配置的所有下游證書:
spring:cloud:gateway:httpclient:ssl:useInsecureTrustManager: true使用不安全的信任管理器不適合生產(chǎn)。對(duì)于生產(chǎn)部署,可以使用以下配置配置一組可信任的已知證書:
spring:cloud:gateway:httpclient:ssl:trustedX509Certificates:- cert1.pem- cert2.pem?如果Spring Cloud Gateway未配置可信證書,則使用默認(rèn)信任庫(kù)(可以使用系統(tǒng)屬性javax.net.ssl.trustStore覆蓋)。
TSL握手
網(wǎng)關(guān)維護(hù)一個(gè)客戶端池,用于路由到后端。通過(guò)https進(jìn)行通信時(shí),客戶端會(huì)啟動(dòng)TLS握手。這次握手會(huì)有很多超時(shí)。可以配置這些超時(shí)(顯示默認(rèn)值):
cloud:gateway:httpclient:ssl:handshake-timeout-millis: 10000close-notify-flush-timeout-millis: 3000close-notify-read-timeout-millis: 0?Configuration
Spring Cloud Gateway 的配置是由一系列的 RouteDefinitionLocator 類來(lái)管理的。
public interface RouteDefinitionLocator {Flux<RouteDefinition> getRouteDefinitions(); }默認(rèn)情況下,PropertiesRouteDefinitionLocator 使用 Spring Boot 的@ConfigurationProperties 機(jī)制加載屬性。
上面的配置示例都使用了一個(gè)使用位置參數(shù)而不是命名參數(shù)的快捷符號(hào)。以下兩個(gè)例子是等效的:
spring:cloud:gateway:routes:- id: setstatus_routeuri: https://example.orgfilters:- name: SetStatusargs:status: 401- id: setstatusshortcut_routeuri: https://example.orgfilters:- SetStatus=401對(duì)于網(wǎng)關(guān)的一些用法,屬性是足夠的,但是一些生產(chǎn)用例將受益于從外部源(例如數(shù)據(jù)庫(kù))加載配置。未來(lái)的里程碑版本將RouteDefinitionLocator基于Spring Data Repositories實(shí)現(xiàn),例如:Redis,MongoDB和Cassandra。
Route Metadata Configuration
可以使用metadata為每個(gè)route增加附加屬性。
spring:cloud:gateway:routes:- id: route_with_metadatauri: https://example.orgmetadata:optionName: "OptionValue"compositeObject:name: "value"iAmNumber: 1可以通過(guò)exchange獲取所有屬性。
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); // get all metadata properties route.getMetadata(); // get a single metadata property route.getMetadata(someKey);?HTTP超時(shí)配置
全局超時(shí)
- connect-timeout:單位:毫秒.
- response-timeout?:單位:?java.time.Duration
每個(gè)路由配置
connect-timeout?:單位:毫秒.
response-timeout:單位:毫秒
JAVA代碼:
import static org.springframework.cloud.gateway.support.RouteMetadataUtils.CONNECT_TIMEOUT_ATTR; import static org.springframework.cloud.gateway.support.RouteMetadataUtils.RESPONSE_TIMEOUT_ATTR;@Beanpublic RouteLocator customRouteLocator(RouteLocatorBuilder routeBuilder){return routeBuilder.routes().route("test1", r -> {return r.host("*.somehost.org").and().path("/somepath").filters(f -> f.addRequestHeader("header1", "header-value-1")).uri("http://someuri").metadata(RESPONSE_TIMEOUT_ATTR, 200).metadata(CONNECT_TIMEOUT_ATTR, 200);}).build();}流式JAVA路由API
為了允許在Java中進(jìn)行簡(jiǎn)單配置,RouteLocatorBuilder 中定義了一個(gè)流式的API?
// static imports from GatewayFilters and RoutePredicates @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {return builder.routes().route(r -> r.host("**.abc.org").and().path("/image/png").filters(f -> f.addResponseHeader("X-TestHeader", "foobar")).uri("http://httpbin.org:80")).route(r -> r.path("/image/webp").filters(f -> f.addResponseHeader("X-AnotherHeader", "baz")).uri("http://httpbin.org:80").metadata("key", "value")).route(r -> r.order(-1).host("**.throttle.org").and().path("/get").filters(f -> f.filter(throttle.apply(1,1,10,TimeUnit.SECONDS))).uri("http://httpbin.org:80").metadata("key", "value")).build(); }這類風(fēng)格也允許更多的自定義謂詞斷言。RouteDefinitionLocator??定義的謂詞使用邏輯與組合。通過(guò)使用流式的 Java API,您可以在 Predicate 類上使用and(),or()和 negate()運(yùn)算符。
DiscoveryClient Route Definition Locator
可以將網(wǎng)關(guān)配置為基于在 DiscoveryClient 兼容服務(wù)注冊(cè)中心注冊(cè)的服務(wù)來(lái)創(chuàng)建路由。要啟用此功能,請(qǐng)?jiān)O(shè)置 spring.cloud.gateway.discovery.locator.enabled = true 并確保DiscoveryClient 實(shí)現(xiàn)類位于classpath下并已啟用(例如 Netflix Eureka,Consul 或Zookeeper)。
配置Predicates和Filters
默認(rèn)情況下,DiscoveryClient創(chuàng)建的Routes僅定義了一個(gè)predicate和filer,
predicates:- Path=/serviceId/**filters:- RewritePath=/serviceId/(?<remaining>.*), /$\{remaining}如果想定義多個(gè),則通過(guò)以下配置:
spring.cloud.gateway.discovery.locator.predicates[0].name: Path spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]: "'/'+serviceId+'/**'" spring.cloud.gateway.discovery.locator.predicates[1].name: Host spring.cloud.gateway.discovery.locator.predicates[1].args[pattern]: "'**.foo.com'" spring.cloud.gateway.discovery.locator.filters[0].name: Hystrix spring.cloud.gateway.discovery.locator.filters[0].args[name]: serviceId spring.cloud.gateway.discovery.locator.filters[1].name: RewritePath spring.cloud.gateway.discovery.locator.filters[1].args[regexp]: "'/' + serviceId + '/(?<remaining>.*)'" spring.cloud.gateway.discovery.locator.filters[1].args[replacement]: "'/${remaining}'"CORS配置
網(wǎng)關(guān)可以通過(guò)配置控制 CORS 行為。 “全局”CORS 配置是 Spring Framework?CorsConfiguration的 URL 模式映射。
spring:cloud:gateway:globalcors:cors-configurations:'[/**]':allowedOrigins: "https://docs.spring.io"allowedMethods:- GET在上面的示例中,對(duì)于所有 GET 請(qǐng)求的路徑,將允許來(lái)自 docs.spring.io 的請(qǐng)求的 CORS請(qǐng)求。
Gateway應(yīng)如何使用MVC或Webflux搭建
Spring Cloud Gateway提供了一個(gè)實(shí)用程序?qū)ο驪roxyExchange,可以在常規(guī)Spring Web處理程序中將其用作方法參數(shù)。它通過(guò)反射HTTP謂詞的方法支持基本的下游HTTP交換。使用MVC,它還支持通過(guò)該forward()方法轉(zhuǎn)發(fā)到本地處理程序。要在類路徑中使用ProxyExchange,只需包含正確的模塊(spring-cloud-gateway-mvc或者spring-cloud-gateway-webflux)。
MVC 示例(代理一個(gè)到下游遠(yuǎn)程服務(wù)器的“/test”請(qǐng)求):?
@RestController @SpringBootApplication public class GatewaySampleApplication {@Value("${remote.home}")private URI home;@GetMapping("/test")public ResponseEntity<?> proxy(ProxyExchange<byte[]> proxy) throws Exception {return proxy.uri(home.toString() + "/image/png").get();}}Flux:
@RestController @SpringBootApplication public class GatewaySampleApplication {@Value("${remote.home}")private URI home;@GetMapping("/test")public Mono<ResponseEntity<?>> proxy(ProxyExchange<byte[]> proxy) throws Exception {return proxy.uri(home.toString() + "/image/png").get();}}?
Spring MVC 或 Webflux 的所有特性都可用于 Gateway 處理方法。例如,您可以注入請(qǐng)求頭和查詢參數(shù),并且您可以使用映射注釋中的聲明來(lái)約束傳入的請(qǐng)求。
可以使用 ProxyExchange 上的 header()方法將頭信息添加到下游響應(yīng)中。
您還可以通過(guò)向 get()等方法添加映射器來(lái)操作響應(yīng)頭信息(以及響應(yīng)中你喜歡的任何其他內(nèi)容)。映射器是一個(gè)函數(shù),它接收傳入的 ResponseEntity 并將其轉(zhuǎn)換為傳出的ResponseEntity。
最高優(yōu)先級(jí)的類支持處理讓"sensitive"敏感頭信息(默認(rèn)情況下是"cookie"和"authorization")不往下游傳遞,并且對(duì)于"proxy"代理頭信息處理為(x-forwarded-*)。
?
參考
屬性設(shè)置附錄:
https://cloud.spring.io/spring-cloud-gateway/2.2.x/reference/html/appendix.html
https://cloud.spring.io/spring-cloud-gateway/2.2.x/reference/html/#actuator-api
https://cloud.spring.io/spring-cloud-gateway/2.2.x/reference/html/#recap-the-list-of-all-endpoints
總結(jié)
以上是生活随笔為你收集整理的Spring Cloud Gateway介绍(二)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring Cloud Gateway
- 下一篇: Spring Cloud Gateway