天天看點

微服務網關 Spring Cloud Gateway

1.  為什麼是Spring Cloud Gateway

一句話,Spring Cloud已經放棄Netflix Zuul了。現在Spring Cloud中引用的還是Zuul 1.x版本,而這個版本是基于過濾器的,是阻塞IO,不支援長連接配接。Zuul 2.x版本跟1.x的架構大一樣,性能也有所提升。既然Spring Cloud已經不再內建Zuul 2.x了,那麼是時候了解一下Spring Cloud Gateway了。

微服務網關 Spring Cloud Gateway

可以看到,最新的Spring Cloud中的Zuul還是1.3.1版本

而且,官網中也明确說了不再維護Zuul了

微服務網關 Spring Cloud Gateway

(PS:順便補充幾個名詞: 服務發現(Eureka),斷路器(Hystrix),智能路由(Zuul),用戶端負載均衡(Ribbon))

2.  API網關

API網關是一個伺服器,是系統的唯一入口。從面向對象設計的角度看,它與外觀模式類似。API網關封裝了系統内部架構,為每個用戶端提供一個定制的API。它可能還具有其它職責,如身份驗證、監控、負載均衡、緩存、請求分片與管理、靜态響應處理。API網關方式的核心要點是,所有的用戶端和消費端都通過統一的網關接入微服務,在網關層處理所有的非業務功能。通常,網關也是提供REST/HTTP的通路API。

網關應當具備以下功能:

  • 性能:API高可用,負載均衡,容錯機制。
  • 安全:權限身份認證、脫敏,流量清洗,後端簽名(保證全鍊路可信調用),黑名單(非法調用的限制)。
  • 日志:日志記錄(spainid,traceid)一旦涉及分布式,全鍊路跟蹤必不可少。
  • 緩存:資料緩存。
  • 監控:記錄請求響應資料,api耗時分析,性能監控。
  • 限流:流量控制,錯峰流控,可以定義多種限流規則。
  • 灰階:線上灰階部署,可以減小風險。
  • 路由:動态路由規則。

目前,比較流行的網關有:Nginx 、 Kong 、Orange等等,還有微服務網關Zuul 、Spring Cloud Gateway等等

對于 API Gateway,常見的選型有基于 Openresty 的 Kong、基于 Go 的 Tyk 和基于 Java 的 Zuul。這三個選型本身沒有什麼明顯的差別,主要還是看技術棧是否能滿足快速應用和二次開發。

微服務網關 Spring Cloud Gateway
微服務網關 Spring Cloud Gateway
微服務網關 Spring Cloud Gateway

以上說的這些功能,這些開源的網關元件都有,或者借助Lua也能實作,比如:Nginx + Lua

那要Spring Cloud Gateway還有什麼用呢?

其實,我個人了解是這樣的:

  • 像Nginx這類網關,性能肯定是沒得說,它适合做那種門戶網關,是作為整個全局的網關,是對外的,處于最外層的;而Gateway這種,更像是業務網關,主要用來對應不同的用戶端提供服務的,用于聚合業務的。各個微服務獨立部署,職責單一,對外提供服務的時候需要有一個東西把業務聚合起來。
  • 像Nginx這類網關,都是用不同的語言編寫的,不易于擴充;而Gateway就不同,它是用Java寫的,易于擴充和維護
  • Gateway這類網關可以實作熔斷、重試等功能,這是Nginx不具備的

是以,你看到的網關可能是這樣的:

微服務網關 Spring Cloud Gateway

2.1.  Netflix Zuul 1.x  VS  Netflix Zuul 2.x

微服務網關 Spring Cloud Gateway
微服務網關 Spring Cloud Gateway

3.  Spring Cloud Gateway

3.1.  特性

  • 基于Spring Framework 5、Project Reactor和Spring Boot 2.0建構
  • 能夠在任意請求屬性上比對路由
  • predicates(謂詞) 和 filters(過濾器)是特定于路由的
  • 內建了Hystrix斷路器
  • 內建了Spring Cloud DiscoveryClient
  • 易于編寫謂詞和過濾器
  • 請求速率限制
  • 路徑重寫

3.2.  術語

Route : 路由是網關的基本元件。它由ID、目标URI、謂詞集合和過濾器集合定義。如果聚合謂詞為true,則比對路由

Predicate : This is a Java 8 Function Predicate

Filter : 是GatewayFilter的一個執行個體,在這裡,可以在發送下遊請求之前或之後修改請求和響應

3.3.  原理

微服務網關 Spring Cloud Gateway

(PS:看到這張圖是不是很熟悉,沒錯,很像SpringMVC的請求處理過程)

用戶端向Spring Cloud Gateway送出請求。如果Gateway Handler Mapping确定請求與路由比對,則将其發送給Gateway Web Handler。這個Handler運作通過特定于請求的過濾器鍊發送請求。過濾器可以在發送代理請求之前或之後執行邏輯。執行所有的“pre”過濾邏輯,然後發出代理請求,最後執行“post”過濾邏輯。

3.4.  Route Predicate Factories

  • Spring Cloud Gateway 包含許多内置的 Route Predicate Factories
  • 所有這些predicates用于比對HTTP請求的不同屬性
  • 多個 Route Predicate Factories 可以通過邏輯與(and)結合起來一起使用

3.4.1.  After Route Predicate Factory

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

這個路由比對“美國丹佛時間2017-01-20 17:42”之後的任意請求

3.4.2.  Header Route Predicate Factory

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

這個路由比對“請求頭包含X-Request-Id并且其值比對正規表達式\d+”的任意請求

3.4.3.  Method Route Predicate Factory

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

這個路由比對任意GET請求

3.4.4.  Path Route Predicate Factory

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

這個路由比對這樣路徑的請求,比如:/foo/1 或 /foo/bar 或 /bar/baz

3.4.5.  Query Route Predicate Factory

這個Predicate有兩個參數:一個必須的參數名和一個可選的正規表達式

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

這個路由比對“查詢參數中包含baz”的請求

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

這個路由比對“查詢參數中包含foo,并且其參數值滿足正規表達式ba.”的請求,比如:bar,baz

3.4.6.  RemoteAddr Route Predicate Factory

這個路由接受一個IP(IPv4或IPv6)位址字元串。例如:192.168.0.1/16,其中192.168.0.1,16是子網路遮罩

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

這裡路由比對遠端位址是這樣的請求,例如:192.168.1.10

3.5.  GatewayFilter Factories(網關過濾器)

路由過濾器允許以某種方式修改傳入的HTTP請求或傳出HTTP響應。路由過濾器的作用域是特定的路由。Spring Cloud Gateway包含許多内置的網關過濾器工廠。

3.5.1.  AddRequestHeader GatewayFilter Factory

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

對于所有比對的請求,将會給傳給下遊的請求添加一個請求頭 X-Request-Foo:Bar

3.5.2.  AddRequestParameter GatewayFilter Factory

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

對于所有比對的請求,将給傳給下遊的請求添加一個查詢參數 foo=bar

3.5.3.  AddResponseHeader GatewayFilter Factory

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

對于所有比對的請求,添加一個響應頭 X-Response-Foo:Bar

3.5.4.  Hystrix GatewayFilter Factory

Hystrix網關過濾器允許你将斷路器引入網關路由,保護你的服務免受級聯失敗的影響,并在下遊發生故障時提供預備響應。

為了啟用Hystrix網關過濾器,你需要引入 spring-cloud-starter-netflix-hystrix

Hystrix網關過濾器需要一個name參數,這個name是HystrixCommand的名字

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

給這個過濾器包裝一個名字叫myCommandName的HystrixCommand

Hystrix網關過濾器也接受一個可選的參數fallbackUri,但是目前隻支援forward:字首的URL。也就是說,如果這個fallback被調用,請求将被重定向到比對的這個URL。

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      

當fallback被調用的時候,請求将被重定向到/incaseoffailureusethis

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      

在這個例子中,專門定義了一個端點來處理/fallback請求,它在localhost:9994上。也就是說,當fallback被調用的時候将重定向到http://localhost:9994/fallback

3.5.5.  PrefixPath GatewayFilter Factory

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

所有比對的請求都将加上字首/mypath。例如,如果請求是/hello,那麼經過這個過濾器後,發出去的請求變成/mypath/hello

3.5.6.  RequestRateLimiter GatewayFilter Factory

RequestRateLimiter網關過濾器使用一個RateLimiter實作來決定是否目前請求可以繼續往下走。如果不能,預設将傳回HTTP 429 - Too Many Requests

這個過濾器接受一個可選的參數keyResolver,這個參數是一個特定的rate limiter

keyResolver是實作了KeyResolver接口的一個Bean。

在配置的時候,使用SpEL按名稱引用Bean。#{@myKeyResolver}是一個SpEL表達式,表示引用名字叫myKeyResolver的Bean。

KeyResolver.java 

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

KeyResolver預設的實作是PrincipalNameKeyResolver,它從ServerWebExchange中檢索Principal,并調用Principal.getName()方法。 

預設情況下,如果KeyResolver沒有找到一個key,那麼請求将會被denied(譯:否認,拒絕)。這種行為可以通過spring.cloud.gateway.filter.request-rate-limiter.deny-empty-key (true or false) 和 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code 屬性來進行調整. 

Redis RateLimiter

需要引用 spring-boot-starter-data-redis-reactive

這個邏輯使用令牌桶算法

  • redis-rate-limiter.replenishRate : 允許使用者每秒處理多少個請求。這是令牌桶被填充的速率。
  • redis-rate-limiter.burstCapacity : 使用者在一秒鐘内允許執行的最大請求數。這是令牌桶可以容納的令牌數量。将此值設定為0将阻塞所有請求。

一個穩定的速率是通過将replenishRate 和 burstCapacity設為相同的值來實作的。也可以将burstCapacity設得比replenishRate大,以應對臨時爆發的流量。在這種情況下,需要允許速率限制器在突發事件之間間隔一段時間,因為連續兩次突發事件将導緻丢棄請求(HTTP 429 - Too Many Requests)

application.yml

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

Config.java

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

這裡定義了每個使用者的請求速率限制為10。允許使用20個請求,但是在接下來的一秒中,隻有10個請求可用。

這個例子中隻是簡單地從請求參數中擷取"user",在實際生産環境中不建議這麼做。

我們也可以通過實作RateLimiter接口來自定義,這個時候,在配置中我們就需要引用這個Bean,例如:#{@myRateLimiter} 

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

3.5.7.  Default Filters

如果你想要添加一個過濾器并且把它應用于所有路由的話,你可以用spring.cloud.gateway.default-filters。這個屬性接受一個過濾器清單。

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar
      - PrefixPath=/httpbin      

3.6.  Global Filters(全局過濾器)

GlobalFilter接口的方法簽名和GatewayFilter相同。這些是有條件地應用于所有路由的特殊過濾器。

3.6.1.  GlobalFilter和GatewayFilter的順序

當一個請求過來的時候,将會添加所有的GatewayFilter執行個體和所有特定的GatewayFilter執行個體到過濾器鍊上。過濾器鍊按照org.springframework.core.Ordered接口對該鍊路上的過濾器進行排序。你可以通過實作接口中的getOrder()方法或者使用@Order注解。

Spring Cloud Gateway将過濾器執行邏輯分為“pre”和“post”階段。優先級最高的過濾器将會是“pre”階段中的第一個過濾器,同時它也将是“post”階段中的最後一個過濾器。

ExampleConfiguration.java 

1 @Bean
 2 @Order(-1)
 3 public GlobalFilter a() {
 4     return (exchange, chain) -> {
 5         log.info("first pre filter");
 6         return chain.filter(exchange).then(Mono.fromRunnable(() -> {
 7             log.info("third post filter");
 8         }));
 9     };
10 }
11 
12 @Bean
13 @Order(0)
14 public GlobalFilter b() {
15     return (exchange, chain) -> {
16         log.info("second pre filter");
17         return chain.filter(exchange).then(Mono.fromRunnable(() -> {
18             log.info("second post filter");
19         }));
20     };
21 }
22 
23 @Bean
24 @Order(1)
25 public GlobalFilter c() {
26     return (exchange, chain) -> {
27         log.info("third pre filter");
28         return chain.filter(exchange).then(Mono.fromRunnable(() -> {
29             log.info("first post filter");
30         }));
31     };
32 }
      

3.6.2.  LoadBalancerClient Filter

LoadBalancerClientFilter查找exchange屬性中查找ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR一個URI。如果url符合lb schema(例如:lb://myservice),那麼它将使用Spring Cloud LoadBalancerClient 來解析這個名字到一個實際的主機和端口,并替換URI中相同的屬性。原始url中未被修改的部分被附加到ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR屬性清單中。

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

預設情況下,當一個服務執行個體在LoadBalancer中沒有找到時,将傳回503。你可以通過配置spring.cloud.gateway.loadbalancer.use404=true來讓它傳回404。

3.7.  配置

RouteDefinitionLocator.java 

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

預設情況下,PropertiesRouteDefinitionLocator通過@ConfigurationProperties機制加載屬性

下面兩段配置是等價的

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

下面用Java配置

GatewaySampleApplication.java 

1 // static imports from GatewayFilters and RoutePredicates
 2 @Bean
 3 public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
 4     return builder.routes()
 5             .route(r -> r.host("**.abc.org").and().path("/image/png")
 6                 .filters(f ->
 7                         f.addResponseHeader("X-TestHeader", "foobar"))
 8                 .uri("http://httpbin.org:80")
 9             )
10             .route(r -> r.path("/image/webp")
11                 .filters(f ->
12                         f.addResponseHeader("X-AnotherHeader", "baz"))
13                 .uri("http://httpbin.org:80")
14             )
15             .route(r -> r.order(-1)
16                 .host("**.throttle.org").and().path("/get")
17                 .filters(f -> f.filter(throttle.apply(1,
18                         1,
19                         10,
20                         TimeUnit.SECONDS)))
21                 .uri("http://httpbin.org:80")
22             )
23             .build();
24 }
      

這種風格允許自定義更多的謂詞斷言,預設是邏輯與(and)。你也可以用and() , or() , negate() 

再來一個例子

1 @SpringBootApplication
 2 public class DemogatewayApplication {
 3 	@Bean
 4 	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
 5 		return builder.routes()
 6 			.route("path_route", r -> r.path("/get")
 7 				.uri("http://httpbin.org"))
 8 			.route("host_route", r -> r.host("*.myhost.org")
 9 				.uri("http://httpbin.org"))
10 			.route("hystrix_route", r -> r.host("*.hystrix.org")
11 				.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
12 				.uri("http://httpbin.org"))
13 			.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
14 				.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
15 				.uri("http://httpbin.org"))
16 			.route("limit_route", r -> r
17 				.host("*.limited.org").and().path("/anything/**")
18 				.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
19 				.uri("http://httpbin.org"))
20 			.build();
21 	}
22 }
      

3.8.  CORS配置

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

上面的例子中,所有原始為docs.spring.io的GET請求均被允許跨域請求。

4.  示例

微服務網關 Spring Cloud Gateway

本例中又4個項目,如下圖:

微服務網關 Spring Cloud Gateway

4.1.  cjs-eureka-server

pom.xml

1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <parent>
 6         <groupId>org.springframework.boot</groupId>
 7         <artifactId>spring-boot-starter-parent</artifactId>
 8         <version>2.1.6.RELEASE</version>
 9         <relativePath/> <!-- lookup parent from repository -->
10     </parent>
11     <groupId>com.cjs.example</groupId>
12     <artifactId>cjs-eureka-server</artifactId>
13     <version>0.0.1-SNAPSHOT</version>
14     <name>cjs-eureka-server</name>
15 
16     <properties>
17         <java.version>1.8</java.version>
18         <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
19     </properties>
20 
21     <dependencies>
22         <dependency>
23             <groupId>org.springframework.cloud</groupId>
24             <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
25         </dependency>
26         <dependency>
27             <groupId>ch.qos.logback</groupId>
28             <artifactId>logback-classic</artifactId>
29             <version>1.2.3</version>
30         </dependency>
31     </dependencies>
32 
33     <dependencyManagement>
34         <dependencies>
35             <dependency>
36                 <groupId>org.springframework.cloud</groupId>
37                 <artifactId>spring-cloud-dependencies</artifactId>
38                 <version>${spring-cloud.version}</version>
39                 <type>pom</type>
40                 <scope>import</scope>
41             </dependency>
42         </dependencies>
43     </dependencyManagement>
44 
45     <build>
46         <plugins>
47             <plugin>
48                 <groupId>org.springframework.boot</groupId>
49                 <artifactId>spring-boot-maven-plugin</artifactId>
50             </plugin>
51         </plugins>
52     </build>
53 
54 </project>
      
1 server:
 2   port: 8761
 3 
 4 spring:
 5   application:
 6     name: cjs-eureka-server
 7 
 8 eureka:
 9   client:
10     service-url:
11       defaultZone: http://10.0.29.92:8761/eureka/,http://10.0.29.232:8761/eureka/
12 
13 logging:
14   file: ${spring.application.name}.log
      

Application.java

1 package com.cjs.example;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
 6 
 7 /**
 8  * @author chengjiansheng
 9  * @date 2019-06-26
10  */
11 @EnableEurekaServer
12 @SpringBootApplication
13 public class CjsEurekaServerApplication {
14 
15     public static void main(String[] args) {
16         SpringApplication.run(CjsEurekaServerApplication.class, args);
17     }
18 
19 }       

4.2.  cjs-gateway-server

1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <parent>
 6         <groupId>org.springframework.boot</groupId>
 7         <artifactId>spring-boot-starter-parent</artifactId>
 8         <version>2.1.6.RELEASE</version>
 9         <relativePath/> <!-- lookup parent from repository -->
10     </parent>
11     <groupId>com.cjs.example</groupId>
12     <artifactId>cjs-gateway-server</artifactId>
13     <version>0.0.1-SNAPSHOT</version>
14     <name>cjs-gateway-server</name>
15 
16     <properties>
17         <java.version>1.8</java.version>
18         <spring-cloud.version>Greenwich.SR1</spring-cloud.version>
19     </properties>
20 
21     <dependencies>
22         <dependency>
23             <groupId>org.springframework.cloud</groupId>
24             <artifactId>spring-cloud-starter-gateway</artifactId>
25         </dependency>
26 
27         <dependency>
28             <groupId>org.springframework.boot</groupId>
29             <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
30         </dependency>
31         <dependency>
32             <groupId>ch.qos.logback</groupId>
33             <artifactId>logback-classic</artifactId>
34             <version>1.2.3</version>
35         </dependency>
36     </dependencies>
37 
38     <dependencyManagement>
39         <dependencies>
40             <dependency>
41                 <groupId>org.springframework.cloud</groupId>
42                 <artifactId>spring-cloud-dependencies</artifactId>
43                 <version>${spring-cloud.version}</version>
44                 <type>pom</type>
45                 <scope>import</scope>
46             </dependency>
47         </dependencies>
48     </dependencyManagement>
49 
50     <build>
51         <plugins>
52             <plugin>
53                 <groupId>org.springframework.boot</groupId>
54                 <artifactId>spring-boot-maven-plugin</artifactId>
55             </plugin>
56         </plugins>
57     </build>
58 
59 </project>
      
1 server:
 2   port: 8080
 3   servlet:
 4     context-path: /
 5 spring:
 6   application:
 7     name: cjs-gateway-server
 8   redis:
 9     host: 10.0.29.187
10     password: 123456
11     port: 6379
12   cloud:
13     gateway:
14       routes:
15         - id: header_route
16           uri: http://10.0.29.187:8080/
17           predicates:
18             - Header=X-Request-Id, \d+
19 #        - id: path_route
20 #          uri: http://10.0.29.187:8080/
21 #          predicates:
22 #            - Path=/foo/{segment},/bar/{segment}
23         - id: query_route
24           uri: http://10.0.29.187:8080/
25           predicates:
26             - Query=baz
27 #      default-filters:
28 #        - AddResponseHeader=X-Response-Foo, Bar
29 #        - AddRequestParameter=hello, world
30 
31 logging:
32   file: ${spring.application.name}.log
      
1 package com.cjs.example.gateway;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
 6 import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
 7 import org.springframework.cloud.gateway.route.RouteLocator;
 8 import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
 9 import org.springframework.context.annotation.Bean;
10 import org.springframework.web.bind.annotation.RestController;
11 import reactor.core.publisher.Mono;
12 
13 /**
14  * @author chengjiansheng
15  */
16 @RestController
17 @SpringBootApplication
18 public class CjsGatewayServerApplication {
19 
20     public static void main(String[] args) {
21         SpringApplication.run(CjsGatewayServerApplication.class, args);
22     }
23 
24     @Bean
25     public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
26         return builder.routes()
27                 .route("path_route", r -> r.path("/price/**")
28                         .filters(f -> f.addRequestHeader("hello", "world")
29                                 .addRequestParameter("name", "zhangsan")
30                                 .requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
31                         .uri("http://10.0.29.232:8082/price"))
32                 .route("path_route", r -> r.path("/commodity/**").uri("http://10.0.29.92:8081/commodity"))
33                 .build();
34     }
35 
36     @Bean
37     public RedisRateLimiter redisRateLimiter() {
38         return new RedisRateLimiter(2, 4);
39     }
40 
41     @Bean
42     KeyResolver userKeyResolver() {
43 //        return exchange -> Mono.just(exchange.getRequest().getHeaders().getFirst("userId"));
44         return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
45     }
46 
47 }      

其餘代碼就不一一貼出來了,都在git上

https://github.com/chengjiansheng/cjs-springcloud-example

截兩張圖吧

微服務網關 Spring Cloud Gateway

下面看效果

效果一:正常路由

微服務網關 Spring Cloud Gateway
微服務網關 Spring Cloud Gateway

效果二:限流

1 ab -n 20 -c 10 http://10.0.29.187:8080/price/index/test01?userId=123
      

觀察控制台會看到

1 2019-07-03 18:21:23.946 DEBUG 34433 --- [ioEventLoop-4-1] o.s.c.g.f.ratelimit.RedisRateLimiter     : response: Response{allowed=false, headers={X-RateLimit-Remaining=0, X-RateLimit-Burst-Capacity=4, X-RateLimit-Replenish-Rate=2}, tokensRemaining=-1}
2 2019-07-03 18:21:23.946 DEBUG 34433 --- [ioEventLoop-4-1] o.s.w.s.adapter.HttpWebHandlerAdapter    : [53089629] Completed 429 TOO_MANY_REQUESTS      

5.  文檔

https://spring.io/projects/spring-cloud-gateway

https://spring.io/guides/gs/gateway/

https://cloud.spring.io/spring-cloud-gateway/spring-cloud-gateway.html

https://github.com/spring-cloud/spring-cloud-gateway/tree/master/spring-cloud-gateway-sample

https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/2.1.2.RELEASE/single/spring-cloud-netflix.html

https://stripe.com/blog/rate-limiters

https://en.wikipedia.org/wiki/Token_bucket

https://blog.csdn.net/qingmengwuhen1/article/details/80742654

https://blog.mkfree.com/archives/236