Spring Cloud Gateway 2.1.0 中文官網文檔

目錄

1. How to Include Spring Cloud Gateway
2. Glossary
3. How It Works
4. Route Predicate Factories
5. GatewayFilter Factories
6. Global Filters
7. TLS / SSL
8. Configuration
9. Reactor Netty Access Logs
10. CORS Configuration
11. Actuator API
12. Developer Guide

該項目提供了一個建立在Spring Ecosystem之上的API網關,包括:Spring 5,Spring Boot 2和Project Reactor。Spring Cloud Gateway旨在提供一種簡單而有效的方式來對API進行路由,併爲他們提供切面,例如:安全性,監控/指標 和彈性等。

1. 如何在工程中引用Spring Cloud Gateway

要在項目中引入Spring Cloud Gateway,需要引用 group org.springframework.cloud 和 artifact id爲spring-cloud-starter-gateway starter。最新的Spring Cloud Release 構建信息,請參閱Spring Cloud Project page

如果應用了該starter,但由於某種原因不希望啓用網關,請進行設置spring.cloud.gateway.enabled=false

重要
Spring Cloud Gateway依賴Spring Boot和Spring Webflux提供的Netty runtime。它不能在傳統的Servlet容器中工作或構建爲WAR

2. 詞彙表

  • Route 路由:gateway的基本構建模塊。它由ID、目標URI、斷言集合和過濾器集合組成。如果聚合斷言結果爲真,則匹配到該路由。
  • Predicate 斷言:這是一個Java 8 Function Predicate。輸入類型是 Spring Framework ServerWebExchange。這允許開發人員可以匹配來自HTTP請求的任何內容,例如Header或參數。
  • Filter 過濾器:這些是使用特定工廠構建的 Spring FrameworkGatewayFilter實例。所以可以在返回請求之前或之後修改請求和響應的內容。

3. 如何工作的

5401760-aa7f28f8aa0e307a.png
Spring Cloud Gateway Diagram

客戶端向Spring Cloud Gateway發出請求。如果Gateway Handler Mapping確定請求與路由匹配,則將其發送到Gateway Web Handler。此handler通過特定於該請求的過濾器鏈處理請求。圖中filters被虛線劃分的原因是filters可以在發送代理請求之前或之後執行邏輯。先執行所有“pre filter”邏輯,然後進行請求代理。在請求代理執行完後,執行“post filter”邏輯。

注意
HTTP和HTTPS URI默認端口設置是80和443。

4. 路由斷言Factories

Spring Cloud Gateway將路由作爲Spring WebFlux HandlerMapping基礎結構的一部分進行匹配。Spring Cloud Gateway包含許多內置的路由斷言Factories。這些斷言都匹配HTTP請求的不同屬性。多個路由斷言Factories可以通過 and 組合使用。

4.1 After 路由斷言 Factory

After Route Predicate Factory採用一個參數——日期時間。在該日期時間之後發生的請求都將被匹配。

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: http://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

4.2 Before 路由斷言 Factory

Before Route Predicate Factory採用一個參數——日期時間。在該日期時間之前發生的請求都將被匹配。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: http://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

4.3 Between 路由斷言 Factory

Between 路由斷言 Factory有兩個參數,datetime1和datetime2。在datetime1和datetime2之間的請求將被匹配。datetime2參數的實際時間必須在datetime1之後。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: http://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

4.4 Cookie 路由斷言 Factory

Cookie 路由斷言 Factory有兩個參數,cookie名稱和正則表達式。請求包含次cookie名稱且正則表達式爲真的將會被匹配。

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: http://example.org
        predicates:
        - Cookie=chocolate, ch.p

4.5 Header 路由斷言 Factory

Header 路由斷言 Factory有兩個參數,header名稱和正則表達式。請求包含次header名稱且正則表達式爲真的將會被匹配。

application.yml.

spring:
 cloud:
   gateway:
     routes:
     - id: header_route
       uri: http://example.org
       predicates:
       - Header=X-Request-Id, \d+

4.6 Host 路由斷言 Factory

Host 路由斷言 Factory包括一個參數:host name列表。使用Ant路徑匹配規則,.作爲分隔符。
application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

4.7 Method 路由斷言 Factory

Method 路由斷言 Factory只包含一個參數: 需要匹配的HTTP請求方式

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://example.org
        predicates:
        - Method=GET

所有GET請求都將被路由

4.8 Path 路由斷言 Factory

Path 路由斷言 Factory 有2個參數: 一個Spring PathMatcher表達式列表和可選matchOptionalTrailingSeparator標識 .

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://example.org
        predicates:
        - Path=/foo/{segment},/bar/{segment}

例如: /foo/1 or /foo/bar or /bar/baz的請求都將被匹配

URI 模板變量 (如上例中的 segment ) 將以Map的方式保存於ServerWebExchange.getAttributes() key爲ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE. 這些值將在GatewayFilter Factories使用

可以使用以下方法來更方便地訪問這些變量。

Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);

String segment = uriVariables.get("segment");

4.9 Query 路由斷言 Factory

Query 路由斷言 Factory 有2個參數: 必選項 param 和可選項 regexp.

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://example.org
        predicates:
        - Query=baz

則包含了請求參數 baz的都將被匹配。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://example.org
        predicates:
        - Query=foo, ba.

如果請求參數裏包含foo參數,並且值匹配爲ba. 表達式,則將會被路由,如:bar and baz

4.10 RemoteAddr 路由斷言 Factory

RemoteAddr 路由斷言 Factory的參數爲 一個CIDR符號(IPv4或IPv6)字符串的列表,最小值爲1,例如192.168.0.1/16(其中192.168.0.1是IP地址並且16是子網掩碼)。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: http://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

如果請求的remote address 爲 192.168.1.10則將被路由

4.10.1 修改遠程地址的解析方式

默認情況下,RemoteAddr 路由斷言 Factory使用傳入請求中的remote address。如果Spring Cloud Gateway位於代理層後面,則可能與實際客戶端IP地址不匹配。

可以通過設置自定義RemoteAddressResolver來自定義解析遠程地址的方式。Spring Cloud Gateway網關附帶一個非默認遠程地址解析程序,它基於X-Forwarded-For header, XForwardedRemoteAddressResolver.

XForwardedRemoteAddressResolver 有兩個靜態構造函數方法,採用不同的安全方法:

  1. XForwardedRemoteAddressResolver::TrustAll返回一個RemoteAddressResolver,它始終採用X-Forwarded-for頭中找到的第一個IP地址。這種方法容易受到欺騙,因爲惡意客戶端可能會爲解析程序接受的“x-forwarded-for”設置初始值。

  2. XForwardedRemoteAddressResolver::MaxTrustedIndex獲取一個索引,該索引與在Spring Cloud網關前運行的受信任基礎設施數量相關。例如,如果SpringCloudGateway只能通過haproxy訪問,則應使用值1。如果在訪問Spring Cloud Gateway之前需要兩個受信任的基礎架構躍點,那麼應該使用2。

給定以下的header值:

X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3

下面的` maxTrustedIndex值將生成以下遠程地址:


5401760-c6e3a93338629452.png
image.png

Java 配置方式:

GatewayConfig.java

RemoteAddressResolver resolver = XForwardedRemoteAddressResolver
    .maxTrustedIndex(1);

...

.route("direct-route",
    r -> r.remoteAddr("10.1.1.1", "10.10.1.1/24")
        .uri("https://downstream1")
.route("proxied-route",
    r -> r.remoteAddr(resolver,  "10.10.1.1", "10.10.1.1/24")
        .uri("https://downstream2")
)

5. GatewayFilter Factories

過濾器允許以某種方式修改傳入的HTTP請求或返回的HTTP響應。過濾器的作用域是某些特定路由。Spring Cloud Gateway包括許多內置的 Filter工廠。

注意:有關如何使用以下任何過濾器的更詳細示例,請查看unit tests.。

5.1 AddRequestHeader GatewayFilter Factory

採用一對名稱和值作爲參數
application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://example.org
        filters:
        - AddRequestHeader=X-Request-Foo, Bar

對於所有匹配的請求,這將向下遊請求的頭中添加 x-request-foo:bar header

5.2 AddRequestParameter GatewayFilter Factory

採用一對名稱和值作爲參數
application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: http://example.org
        filters:
        - AddRequestParameter=foo, bar

對於所有匹配的請求,這將向下遊請求添加foo=bar查詢字符串

5.3 AddResponseHeader GatewayFilter Factory

採用一對名稱和值作爲參數

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://example.org
        filters:
        - AddResponseHeader=X-Response-Foo, Bar

對於所有匹配的請求,這會將x-response-foo:bar頭添加到下游響應的header中

5.4 Hystrix GatewayFilter Factory

Hystrix 是Netflix開源的斷路器組件。Hystrix GatewayFilter允許你向網關路由引入斷路器,保護你的服務不受級聯故障的影響,並允許你在下游故障時提供fallback響應。

要在項目中啓用Hystrix網關過濾器,需要添加對 spring-cloud-starter-netflix-hystrix的依賴 Spring Cloud Netflix.

Hystrix GatewayFilter Factory 需要一個name參數,即HystrixCommand的名稱。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: hystrix_route
        uri: http://example.org
        filters:
        - Hystrix=myCommandName

這將剩餘的過濾器包裝在命令名爲“myCommandName”的HystrixCommand中。

hystrix過濾器還可以接受可選的fallbackUri 參數。目前,僅支持forward: 預設的URI,如果調用fallback,則請求將轉發到與URI匹配的控制器。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: hystrix_route
        uri: lb://backing-service:8088
        predicates:
        - Path=/consumingserviceendpoint
        filters:
        - name: Hystrix
          args:
            name: fallbackcmd
            fallbackUri: forward:/incaseoffailureusethis
        - RewritePath=/consumingserviceendpoint, /backingserviceendpoint

當調用hystrix fallback時,這將轉發到/incaseoffailureusethis uri。注意,這個示例還演示了(可選)通過目標URI上的'lb`前綴,使用Spring Cloud Netflix Ribbon 客戶端負載均衡。

主要場景是使用fallbackUri 到網關應用程序中的內部控制器或處理程序。但是,也可以將請求重新路由到外部應用程序中的控制器或處理程序,如:

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: Hystrix
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback

在本例中,gateway應用程序中沒有 fallback 實現,但是另一個應用程序中有一個接口實現,註冊爲“http://localhost:9994”。

在將請求轉發到fallback的情況下,Hystrix Gateway過濾還支持直接拋出Throwable 。它被作爲ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR屬性添加到ServerWebExchange中,可以在處理網關應用程序中的fallback時使用。

對於外部控制器/處理程序方案,可以添加帶有異常詳細信息的header。可以在 FallbackHeaders GatewayFilter Factory section.中找到有關它的更多信息。

hystrix配置參數(如 timeouts)可以使用全局默認值配置,也可以使用Hystrix wiki中所述屬性進行配置。

要爲上面的示例路由設置5秒超時,將使用以下配置:

application.yml.

hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000

5.5 FallbackHeaders GatewayFilter Factory

FallbackHeaders允許在轉發到外部應用程序中的FallbackUri的請求的header中添加Hystrix異常詳細信息,如下所示:

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: Hystrix
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback
        filters:
        - name: FallbackHeaders
          args:
            executionExceptionTypeHeaderName: Test-Header

在本例中,在運行HystrixCommand發生執行異常後,請求將被轉發到 localhost:9994應用程序中的 fallback終端或程序。異常類型、消息(如果可用)cause exception類型和消息的頭,將由FallbackHeaders filter添加到該請求中。

通過設置下面列出的參數值及其默認值,可以在配置中覆蓋headers的名稱:

  • executionExceptionTypeHeaderName ("Execution-Exception-Type")
  • executionExceptionMessageHeaderName ("Execution-Exception-Message")
  • rootCauseExceptionTypeHeaderName ("Root-Cause-Exception-Type")
  • rootCauseExceptionMessageHeaderName ("Root-Cause-Exception-Message")

Hystrix 如何實現的更多細節可以參考 Hystrix GatewayFilter Factory section.

5.6 PrefixPath GatewayFilter Factory

PrefixPath GatewayFilter 只有一個 prefix 參數.

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://example.org
        filters:
        - PrefixPath=/mypath

這將給所有匹配請求的路徑加前綴/mypath。因此,向/hello發送的請求將發送到/mypath/hello

5.7 PreserveHostHeader GatewayFilter Factory

該filter沒有參數。設置了該Filter後,GatewayFilter將不使用由HTTP客戶端確定的host header ,而是發送原始host header 。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: preserve_host_route
        uri: http://example.org
        filters:
        - PreserveHostHeader

5.8 RequestRateLimiter GatewayFilter Factory

RequestRateLimiter使用RateLimiter實現是否允許繼續執行當前請求。如果不允許繼續執行,則返回HTTP 429 - Too Many Requests (默認情況下)。

這個過濾器可以配置一個可選的keyResolver 參數和rate limiter參數(見下文)。

keyResolverKeyResolver 接口的實現類.在配置中,按名稱使用SpEL引用bean。#{@myKeyResolver} 是引用名爲'myKeyResolver'的bean的SpEL表達式。

KeyResolver.java.

public interface KeyResolver {
    Mono<String> resolve(ServerWebExchange exchange);
}

KeyResolver接口允許使用可插拔策略來派生限制請求的key。在未來的里程碑版本中,將有一些KeyResolver實現。

KeyResolver的默認實現是PrincipalNameKeyResolver,它從ServerWebExchange檢索Principal並調用Principal.getName()

默認情況下,如果KeyResolver 沒有獲取到key,請求將被拒絕。此行爲可以使用 spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key (true or false) 和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code屬性進行調整。

說明
無法通過"shortcut" 配置RequestRateLimiter。以下示例無效

application.properties.

# INVALID SHORTCUT CONFIGURATION
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}

5.8.1 Redis RateLimiter

Redis的實現基於 Stripe實現。它需要使用 spring-boot-starter-data-redis-reactive Spring Boot starter。

使用的算法是Token Bucket Algorithm.。

redis-rate-limiter.replenishRate是你允許用戶每秒執行多少請求,而丟棄任何請求。這是令牌桶的填充速率。

``redis-rate-limiter.burstCapacity`是允許用戶在一秒鐘內執行的最大請求數。這是令牌桶可以保存的令牌數。將此值設置爲零將阻止所有請求。

穩定速率是通過在replenishRateburstCapacity中設置相同的值來實現的。可通過設置burstCapacity高於replenishRate來允許臨時突發流浪。在這種情況下,限流器需要在兩次突發之間留出一段時間(根據replenishRate),因爲連續兩次突發將導致請求丟失 (HTTP 429 - Too Many Requests).。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: http://example.org
        filters:
        - name: RequestRateLimiter
          args:
            redis-rate-limiter.replenishRate: 10
            redis-rate-limiter.burstCapacity: 20

Config.java.

@Bean
KeyResolver userKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}

這定義了每個用戶10個請求的限制。允許20個突發,但下一秒只有10個請求可用。KeyResolver是一個簡單的獲取user請求參數的工具(注意:不建議用於生產)。

限流器也可以定義爲RateLimiter接口的實現 bean。在配置中,按名稱使用SpEL引用bean。#{@myRateLimiter}是引用名爲'myRateLimiter'的bean的SpEL表達式。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: http://example.org
        filters:
        - name: RequestRateLimiter
          args:
            rate-limiter: "#{@myRateLimiter}"
            key-resolver: "#{@userKeyResolver}"

5.9 RedirectTo GatewayFilter Factory

該過濾器有一個 status 和一個 url參數。status是300類重定向HTTP代碼,如301。該URL應爲有效的URL,這將是 Location header的值。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://example.org
        filters:
        - RedirectTo=302, http://acme.org

這將發送一個302狀態碼和一個Location:http://acme.org header來執行重定向。

5.10 RemoveNonProxyHeaders GatewayFilter Factory

RemoveNonProxyHeaders GatewayFilter Factory 從轉發請求中刪除headers。刪除的默認頭列表來自 IETF.

The default removed headers are:

  • Connection
  • Keep-Alive
  • Proxy-Authenticate
  • Proxy-Authorization
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade
    要更改此設置,請將 spring.cloud.gateway.filter.remove-non-proxy-headers.headers屬性設置爲要刪除的header名稱。

5.11 RemoveRequestHeader GatewayFilter Factory

有一個name參數. 這是要刪除的header的名稱。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: http://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo

這將在X-Request-Foo header被髮送到下游之前刪除它。

5.12 RemoveResponseHeader GatewayFilter Factory

有一個name參數. 這是要刪除的header的名稱。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: removeresponseheader_route
        uri: http://example.org
        filters:
        - RemoveResponseHeader=X-Response-Foo

這將在返回到網關client之前從響應中刪除x-response-foo頭。

5.13 RewritePath GatewayFilter Factory

包含一個 regexp正則表達式參數和一個 replacement 參數. 通過使用Java正則表達式靈活地重寫請求路徑。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: http://example.org
        predicates:
        - Path=/foo/**
        filters:
        - RewritePath=/foo/(?<segment>.*), /$\{segment}

對於請求路徑/foo/bar,將在發出下游請求之前將路徑設置爲/bar。注意,由於YAML規範,請使用 $\替換 $

5.14 RewriteResponseHeader GatewayFilter Factory

包含 name, regexpreplacement 參數.。通過使用Java正則表達式靈活地重寫響應頭的值。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: rewriteresponseheader_route
        uri: http://example.org
        filters:
        - RewriteResponseHeader=X-Response-Foo, , password=[^&]+, password=***

對於一個/42?user=ford&password=omg!what&flag=true的header值,在做下游請求時將被設置爲/42?user=ford&password=***&flag=true,由於YAML規範,請使用 $\替換 $

5.15 SaveSession GatewayFilter Factory

SaveSession GatewayFilter Factory將調用轉發到下游之強制執行WebSession::save 操作。這在使用 Spring Session 之類時特別有用,需要確保會話狀態在進行轉發調用之前已保存。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: save_session
        uri: http://example.org
        predicates:
        - Path=/foo/**
        filters:
        - SaveSession

如果你希望要將[Spring Security](https://projects.spring.io/Spring Security/)與Spring Session集成,並確保安全詳細信息已轉發到遠程的進程,這一點至關重要。

5.16 SecureHeaders GatewayFilter Factory

SecureHeaders GatewayFilter Factory 將許多headers添加到reccomedation處的響應中,從this blog post.

添加以下標題(使用默認值分配):

  • X-Xss-Protection:1; mode=block
  • Strict-Transport-Security:max-age=631138519
  • X-Frame-Options:DENY
  • X-Content-Type-Options:nosniff
  • Referrer-Policy:no-referrer
  • Content-Security-Policy:default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
  • X-Download-Options:noopen
  • X-Permitted-Cross-Domain-Policies:none

要更改默認值,請在spring.cloud.gateway.filter.secure-headers 命名空間中設置相應的屬性:

Property to change:

  • xss-protection-header
  • strict-transport-security
  • frame-options
  • content-type-options
  • referrer-policy
  • content-security-policy
  • download-options
  • permitted-cross-domain-policies

5.17 SetPath GatewayFilter Factory

SetPath GatewayFilter Factory 採用 template路徑參數。它提供了一種通過允許路徑的模板化segments來操作請求路徑的簡單方法。使用Spring Framework中的URI模板,允許多個匹配segments。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: setpath_route
        uri: http://example.org
        predicates:
        - Path=/foo/{segment}
        filters:
        - SetPath=/{segment}

對於一個 /foo/bar請求,在做下游請求前,路徑將被設置爲/bar

5.18 SetResponseHeader GatewayFilter Factory

SetResponseHeader GatewayFilter Factory 包括 namevalue 參數.

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: setresponseheader_route
        uri: http://example.org
        filters:
        - SetResponseHeader=X-Response-Foo, Bar

此GatewayFilter使用給定的名稱替換所有header,而不是添加。因此,如果下游服務器響應爲X-Response-Foo:1234,則會將其替換爲X-Response-Foo:Bar,這是網關客戶端將接收的內容。

5.19 SetStatus GatewayFilter Factory

SetStatus GatewayFilter Factory 包括唯一的 status參數.必須是一個可用的Spring HttpStatus。它可以是整數值404或字符串枚舉NOT_FOUND

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: setstatusstring_route
        uri: http://example.org
        filters:
        - SetStatus=BAD_REQUEST
      - id: setstatusint_route
        uri: http://example.org
        filters:
        - SetStatus=401

在這個例子中,HTTP返回碼將設置爲401.

5.20 StripPrefix GatewayFilter Factory

StripPrefix GatewayFilter Factory 包括一個parts參數。 parts參數指示在將請求發送到下游之前,要從請求中去除的路徑中的節數。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: nameRoot
        uri: http://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

當通過網關發出/name/bar/foo請求時,向nameservice發出的請求將是http://nameservice/foo

5.21 Retry GatewayFilter Factory

Retry GatewayFilter Factory包括 retries, statuses, methodsseries 參數.

  • retries: 應嘗試的重試次數
  • statuses: 應該重試的HTTP狀態代碼,用org.springframework.http.HttpStatus標識
  • methods: 應該重試的HTTP方法,用 org.springframework.http.HttpMethod標識
  • series: 要重試的一系列狀態碼,用 org.springframework.http.HttpStatus.Series標識

application.yml.

spring:
cloud:
gateway:
routes:
- id: retry_test
uri: http://localhost:8080/flakey
predicates:
- Host=*.retry.com
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY

注意
retry filter 不支持body請求的重試,如通過body的POST 或 PUT請求

注意
在使用帶有前綴爲 forward:retry filter時,應仔細編寫目標端點,以便在出現錯誤時不會執行任何可能導致將響應發送到客戶端並提交的操作。例如,如果目標端點是帶註解的controller,則目標controller方法不應返回帶有錯誤狀態代碼的ResponseEntity。相反,它應該拋出一個Exception,或者發出一個錯誤信號,例如通過Mono.error(ex) 返回值,重試過濾器可以配置爲通過重試來處理。

5.22 RequestSize GatewayFilter Factory

當請求大小大於允許的限制時,RequestSize GatewayFilter Factory可以限制請求不到達下游服務。過濾器以RequestSize作爲參數,這是定義請求的允許大小限制(以字節爲單位)。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: request_size_route
      uri: http://localhost:8080/upload
      predicates:
      - Path=/upload
      filters:
      - name: RequestSize
        args:
          maxSize: 5000000

當請求因大小而被拒絕時, RequestSize GatewayFilter Factory 將響應狀態設置爲413 Payload Too Large,並帶有額外的header errorMessage 。下面是一個 errorMessage的例子。

errorMessage : Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB

注意
如果未在路由定義中作爲filter參數提供,則默認請求大小將設置爲5 MB。

5.23 Modify Request Body GatewayFilter Factory

這個過濾器被定義爲beta版本,將來API可能會改變。

此過濾器可用於在請求主體被網關發送到下游之前對其進行修改。

注意
只能使用Java DSL配置此過濾器

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
                    (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
        .build();
}

static class Hello {
    String message;

    public Hello() { }

    public Hello(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

5.24 Modify Response Body GatewayFilter Factory

這個過濾器被定義爲beta版本,將來API可能會改變。
此過濾器可用於在將響應正文發送回客戶端之前對其進行修改。

注意
只能使用Java DSL配置此過濾器

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyResponseBody(String.class, String.class,
                    (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri)
        .build();
}

6. Global Filters

GlobalFilter接口與GatewayFilter具有相同的簽名。是有條件地應用於所有路由的特殊過濾器。(此接口和用法可能在將來的里程碑版本中發生更改)。

6.1 全局Filter和GatewayFilter組合排序

當請求進入(並與路由匹配)時,篩選Web Handler 會將GlobalFilter的所有實例和所有的GatewayFilter路由特定實例添加到 filter chain。filter組合的排序由org.springframework.core.Ordered接口決定,可以通過實現getOrde()方法或使用@Order註釋來設置。

由於Spring Cloud Gateway將用於執行過濾器邏輯區分爲“前置”和“後置”階段,具有最高優先級的過濾器將是“前置”階段的第一個,而“後置”階段的最後一個。

ExampleConfiguration.java.

@Bean
@Order(-1)
public GlobalFilter a() {
    return (exchange, chain) -> {
        log.info("first pre filter");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("third post filter");
        }));
    };
}

@Bean
@Order(0)
public GlobalFilter b() {
    return (exchange, chain) -> {
        log.info("second pre filter");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("second post filter");
        }));
    };
}

@Bean
@Order(1)
public GlobalFilter c() {
    return (exchange, chain) -> {
        log.info("third pre filter");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("first post filter");
        }));
    };
}

6.2 Forward Routing Filter

ForwardRoutingFilter在exchange屬性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR中查找URI。如果URL有一個forwardscheme (如 forward:///localendpoint),它將使用Spring DispatcherHandler 來處理請求。請求URL的路徑部分將被轉發URL中的路徑覆蓋。未修改的原始URL將附加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR屬性中的列表中。

6.3 LoadBalancerClient Filter

LoadBalancerClientFilter在exchange屬性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR中查找URI。如果URL有一個lbscheme (如 lb://myservice),它將使用Spring Cloud LoadBalancerClient 將名稱(在前一個示例中爲'myservice)解析爲實際主機和端口,並替換URI。未修改的原始URL將附加到ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR屬性中的列表中。過濾器還將查看ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR屬性,查看它是否等於lb`,然後應用相同的規則。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: myRoute
        uri: lb://service
        predicates:
        - Path=/service/**

注意
默認情況下,如果一個服務實例在LoadBalancer 中沒有發現,則返回503。可以通過設置spring.cloud.gateway.loadbalancer.use404=true來讓網管返回404.

注意

LoadBalancer返回的ServiceInstanceisSecure 值將覆蓋在對網關發出的請求中指定的scheme。例如,如果請求通過HTTPS進入網關,但ServiceInstance表示它不安全,則下游請求將通過HTTP協議。相反的情況也適用。但是,如果在網關配置中爲路由指定了GATEWAY_SCHEME_PREFIX_ATTR,則前綴將被刪除,並且路由URL生成的scheme將覆蓋ServiceInstance配置。

6.4 Netty Routing Filter

如果位於 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR屬性中的URL具有httphttps 模式,則會運行Netty Routing Filter。它使用Netty HttpClient 發出下游代理請求。響應放在 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR exchange屬性中,以便在以後的過濾器中使用。(有一個實驗階段不需要Netty的相同的功能的Filter,WebClientHttpRoutingFilter

6.5 Netty Write Response Filter

如果ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR exchange屬性中存在 Netty HttpClientResponse,則運行 NettyWriteResponseFilter 。它在其他所有過濾器完成後將代理響應寫回網關客戶端響應之後運行。(有一個不需要netty的實驗性的WebClientWriteResponseFilter執行相同的功能)

6.6 RouteToRequestUrl Filter

如果ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR exchange屬性中存在Route對象,RouteToRequestUrlFilter將運行。它基於請求URI創建一個新的URI,使用Route對象的uri屬性進行更新。新的URI被放置在ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR exchange屬性中。

如果該URI有一個前綴scheme,例如lb:ws://serviceid,則會從該URI中剝離該 lb scheme,並將其放置在ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR中,以便稍後在過濾器鏈中使用。

6.7 Websocket Routing Filter

如果ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTRexchange屬性中有 wswssscheme,則Websocket Routing Filter將被運行。它使用Spring Web Socket基礎模塊將Websocket轉發到下游。

URI前綴爲lb的Websockets可以被負載均衡,如 lb:ws://serviceid.

注意
如果使用 SockJS 作爲普通HTTP的fallback,則應配置普通HTTP路由以及WebSocket路由。

application.yml.

spring:
  cloud:
    gateway:
      routes:
      # SockJS route
      - id: websocket_sockjs_route
        uri: http://localhost:3001
        predicates:
        - Path=/websocket/info/**
      # Normwal Websocket route
      - id: websocket_route
        uri: ws://localhost:3001
        predicates:
        - Path=/websocket/**

6.8 Gateway Metrics Filter

要啓用網關指標,請將spring-boot-starter-actuator添加爲項目依賴項。然後,默認情況下,只要屬性spring.cloud.gateway.metrics.enabled未設置爲false,網關指標過濾器就會運行。此過濾器添加名爲“gateway.requests”的計時器指標,並帶有以下標記:

  • routeId: The route id
  • routeUri: API 將被轉發的URI
  • outcome: 結果分類依據 HttpStatus.Series
  • status: 返回client的請求的Http Status

這些指標可以從/actuator/metrics/gateway.requests中獲取,可以很容易地與Prometheus集成以創建Grafana dashboard.

注意
要將pometheus啓用,需要添加 micrometer-registry-prometheus爲項目依賴。

6.9 Making An Exchange As Routed

網關路由ServerWebExchange之後,它將通過向Exchange屬性添加gatewayAlreadyRouted,將該exchange標記爲“routed”。一旦一個請求被標記爲routed,其他路由過濾器將不會再次路由該請求,將跳過該過濾器。有一些方便的方法可以用來將exchange標記爲routed,或者檢查exchange是否已經routed。

  • ServerWebExchangeUtils.isAlreadyRouted 有一個 ServerWebExchange對象並檢查它是否已"routed"
  • ServerWebExchangeUtils.setAlreadyRouted 有一個 ServerWebExchange 對象並將其標記爲"routed"

7. TLS / SSL

網關可以通過常規的 Spring server configuration 來偵聽HTTPS上的請求。例子:

application.yml.

server:
  ssl:
    enabled: true
    key-alias: scg
    key-store-password: scg1234
    key-store: classpath:scg-keystore.p12
    key-store-type: PKCS12

網關路由可以路由到HTTP和HTTPS後端。如果路由到HTTPS後端,則可以將網關配置爲信任所有具有證書的下游服務:

application.yml.

spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          useInsecureTrustManager: true

不建議在生產環境使用不安全的信任管理器。對於生產部署,可以使用一組已知證書配置網關,這些證書可以通過以下方式進行配置:

application.yml.

spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          trustedX509Certificates:
          - cert1.pem
          - cert2.pem

如果Spring Cloud Gateway未配置受信任證書,則使用默認信任庫(可以使用系統屬性javax.net.ssl.trustStore覆蓋)。

7.1 TLS 握手

網關維護一個用於路由到後端的client池。當通過HTTPS通信時,客戶端啓動一個TLS握手,其中可能會有很多超時。這些超時可以這樣配置(顯示默認值):

application.yml.

spring:
  cloud:
    gateway:
      httpclient:
        ssl:
          handshake-timeout-millis: 10000
          close-notify-flush-timeout-millis: 3000
          close-notify-read-timeout-millis: 0

8. Configuration

Spring Cloud Gateway的配置由RouteDefinitionLocator的集合驅動。

RouteDefinitionLocator.java.

public interface RouteDefinitionLocator {
    Flux<RouteDefinition> getRouteDefinitions();
}

默認情況下,PropertiesRouteDefinitionLocator使用Spring Boot的@ConfigurationProperties機制加載屬性。

以下兩個示例是等效的:

application.yml.

spring:
  cloud:
    gateway:
      routes:
      - id: setstatus_route
        uri: http://example.org
        filters:
        - name: SetStatus
          args:
            status: 401
      - id: setstatusshortcut_route
        uri: http://example.org
        filters:
        - SetStatus=401

對於網關的大部分用法,配置文件方式是夠用的,但一些生產用例更建議從外部源(如數據庫)加載配置。未來的里程碑版本將有基於Spring Data Repositories (如Redis、MongoDB和Cassandra)的RouteDefinitionLocator實現。

8.1 Fluent Java Routes API

爲了可以更簡單在Java中配置,在RouteLocatorBuilder bean中定義了一個fluent API。

GatewaySampleApplication.java.

// 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")
            )
            .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")
            )
            .build();
}

這種樣式還允許使用更多的自定義斷言。由RouteDefinitionLocator beans定義的斷言使用邏輯 and組合。通過使用fluent Java API,可以在 Predicate類上使用 and()or()negate()運算符。

8.2 DiscoveryClient Route Definition Locator

可以將網關配置爲基於使用兼容DiscoveryClient註冊中心註冊的服務來創建路由。

要啓用此功能,請設置spring.cloud.gateway.discovery.locator.enabled=true,並確保DiscoveryClient實現位於classpath上並已啓用(如netflix eureka、consul或zookeeper)。

8.2.1 Configuring Predicates and Filters For DiscoveryClient Routes

默認情況下,網關爲通過DiscoveryClient創建的路由定義單個斷言和過濾器。

默認斷言是使用/serviceId/**定義的path斷言,其中serviceIdDiscoveryClient中服務的ID。

默認過濾器是使用正則表達式 /serviceId/(?<remaining>.*)和替換的/${remaining}進行重寫。這只是在請求被髮送到下游之前從路徑中截取掉 service id 。

可以通過設置spring.cloud.gateway.discovery.locator.predicates[x] and spring.cloud.gateway.discovery.locator.filters[y]來實現自定義DiscoveryClient路由使用的斷言and/or過濾器。當你這樣做時,如果你想要保留這個功能,你需要確保包括上面的默認斷言和過濾器。下面是這樣一個例子。

application.properties.

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}'"

9. Reactor Netty Access Logs

設置 -Dreactor.netty.http.server.accessLogEnabled=true 來開啓Reactor Netty access logs,注意必須是Java System Property而不是Spring Boot property。

logging 模塊也可以通過配置單獨輸出一個access log文件,下面是logback的配置例子:

logback.xml.

 <appender name="accessLog" class="ch.qos.logback.core.FileAppender">
        <file>access_log.log</file>
        <encoder>
            <pattern>%msg%n</pattern>
        </encoder>
    </appender>
    <appender name="async" class="ch.qos.logback.classic.AsyncAppender">
        <appender-ref ref="accessLog" />
    </appender>

    <logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
        <appender-ref ref="async"/>
    </logger>

10. CORS Configuration

我們可以通過配置網關來控制CORS行爲,全局CORS配置是 Spring Framework CorsConfiguration模式的URL MAP。

application.yml.

spring:
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "http://docs.spring.io"
            allowedMethods:
            - GET

例子中將允許從docs.spring.io發出的所有GET請求進行CORS請求。

11. Actuator API

/gateway的actuator端點允許監視Spring Cloud Gateway應用程序並與之交互。要進行遠程訪問,必須在應用程序屬性中暴露HTTP或JMX 端口。

application.properties.

management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway

11.1 Retrieving route filters

11.1.1 Global Filters

要檢索應用於所有路由的 [global filters],請get請求 /actuator/gateway/globalfilters。返回的結果類似於以下內容:

{
  "org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@77856cc5": 10100,
  "org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@4f6fd101": 10000,
  "org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@32d22650": -1,
  "org.springframework.cloud.gateway.filter.ForwardRoutingFilter@106459d9": 2147483647,
  "org.springframework.cloud.gateway.filter.NettyRoutingFilter@1fbd5e0": 2147483647,
  "org.springframework.cloud.gateway.filter.ForwardPathFilter@33a71d23": 0,
  "org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@135064ea": 2147483637,
  "org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@23c05889": 2147483646
}

返回結果包含已就緒的global filters的詳細信息(如 org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@77856cc5)。對於每個global filters,返回結果字符串對應過濾器鏈中的相應順序。

11.1.2 Route Filters

要檢索應用於路由的 [GatewayFilter factories] ,請get請求/actuator/gateway/routefilters。返回結果類似於以下內容:

{
  "[AddRequestHeaderGatewayFilterFactory@570ed9c configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
  "[SecureHeadersGatewayFilterFactory@fceab5d configClass = Object]": null,
  "[SaveSessionGatewayFilterFactory@4449b273 configClass = Object]": null
}

返回結果包含應用於所有路由的GatewayFilter的詳細信息。顯示每個工廠提供字符串格式的相應對象(例如, [SecureHeadersGatewayFilterFactory@fceab5d configClass = Object])。請注意,null值是由於endpoint controller實現不完整造成的,因爲它嘗試在filter chain中設置對象的順序,這不適用於GatewayFilter工廠對象。

11.2 Refreshing the route cache

如果要清理路由的緩存,請POST請求/actuator/gateway/refresh。該請求將返回一個沒有body的200返回碼。

11.3 Retrieving the routes defined in the gateway

要檢索網關中定義的路由,發送GET請求/actuator/gateway/routes,返回結果如下所示:

[{
  "route_id": "first_route",
  "route_object": {
    "predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/1736826640@1e9d7e7d",
    "filters": [
      "OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.PreserveHostHeaderGatewayFilterFactory$$Lambda$436/674480275@6631ef72, order=0}"
    ]
  },
  "order": 0
},
{
  "route_id": "second_route",
  "route_object": {
    "predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/1736826640@cd8d298",
    "filters": []
  },
  "order": 0
}]

返回結果中包含網關中所有定義的路由信息,下面表格中描述了返回結果信息:

5401760-57eac45c43635614.png
image.png

11.4 Retrieving information about a particular route

要獲取單個路由的信息,發送GET請求 /actuator/gateway/routes/{id} (如: /actuator/gateway/routes/first_route),返回結果如下所示:

{
  "id": "first_route",
  "predicates": [{
    "name": "Path",
    "args": {"_genkey_0":"/first"}
  }],
  "filters": [],
  "uri": "http://www.uri-destination.org",
  "order": 0
}]

下面表格中描述了返回結果信息:

5401760-f33926489bd18796.png
image.png

11.5 Creating and deleting a particular route

要創建一個路由,發送POST請求 /gateway/routes/{id_route_to_create},參數爲JSON結構,具體參數數據結構參考上面章節。

要刪除一個路由,發送 DELETE請求 /gateway/routes/{id_route_to_delete}

11.6 Recap: list of all endpoints

下表總結了Spring Cloud Gateway actuator endpoints。注意,每個endpoint都是/actuator/gateway作爲基本路徑。

5401760-d8ed813e3ae521b0.png
image.png

12. Developer Guide

TODO: overview of writing custom integrations

12.1 Writing Custom Route Predicate Factories

TODO: document writing Custom Route Predicate Factories

12.2 Writing Custom GatewayFilter Factories

如果要自定義一個GatewayFilter,需要實現GatewayFilterFactory。下面是一個你需要集成的抽象類 AbstractGatewayFilterFactory

PreGatewayFilterFactory.java.

public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {

    public PreGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // grab configuration from Config object
        return (exchange, chain) -> {
            //If you want to build a "pre" filter you need to manipulate the
            //request before calling chain.filter
            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
            //use builder to manipulate the request
            return chain.filter(exchange.mutate().request(request).build());
        };
    }

    public static class Config {
        //Put the configuration properties for your filter here
    }

}

PostGatewayFilterFactory.java.

public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {

    public PostGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // grab configuration from Config object
        return (exchange, chain) -> {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                ServerHttpResponse response = exchange.getResponse();
                //Manipulate the response in some way
            }));
        };
    }

    public static class Config {
        //Put the configuration properties for your filter here
    }

}

12.3 Writing Custom Global Filters

TODO: document writing Custom Global Filters

12.4 Writing Custom Route Locators and Writers

TODO: document writing Custom Route Locators and Writers


歡迎關注 高廣超的簡書博客 與 收藏文章 !
歡迎關注 頭條號:互聯網技術棧

個人介紹:

高廣超:多年一線互聯網研發與架構設計經驗,擅長設計與落地高可用、高性能、可擴展的互聯網架構。目前從事大數據相關研發與架構工作。

本文首發在 高廣超的簡書博客 轉載請註明!

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