微服務網關 Spring Cloud Gateway

1.  爲何是Spring Cloud Gatewayhtml

一句話,Spring Cloud已經放棄Netflix Zuul了。如今Spring Cloud中引用的仍是Zuul 1.x版本,而這個版本是基於過濾器的,是阻塞IO,不支持長鏈接。Zuul 2.x版本跟1.x的架構大同樣,性能也有所提高。既然Spring Cloud已經再也不集成Zuul 2.x了,那麼是時候瞭解一下Spring Cloud Gateway了。java

能夠看到,最新的Spring Cloud中的Zuul仍是1.3.1版本react

並且,官網中也明確說了再也不維護Zuul了git

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

2.  API網關web

API網關是一個服務器,是系統的惟一入口。從面向對象設計的角度看,它與外觀模式相似。API網關封裝了系統內部架構,爲每一個客戶端提供一個定製的API。它可能還具備其它職責,如身份驗證、監控、負載均衡、緩存、請求分片與管理、靜態響應處理。API網關方式的核心要點是,全部的客戶端和消費端都經過統一的網關接入微服務,在網關層處理全部的非業務功能。一般,網關也是提供REST/HTTP的訪問API。正則表達式

網關應當具有如下功能:redis

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

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

對於 API Gateway,常見的選型有基於 Openresty 的 Kong、基於 Go 的 Tyk 和基於 Java 的 Zuul。這三個選型自己沒有什麼明顯的區別,主要仍是看技術棧是否能知足快速應用和二次開發。spring

以上說的這些功能,這些開源的網關組件都有,或者藉助Lua也能實現,好比:Nginx + Lua

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

其實,我我的理解是這樣的:

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

因此,你看到的網關多是這樣的:

 

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

  

3.  Spring Cloud Gateway

3.1.  特性

  • 基於Spring Framework 五、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.  原理

(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參數,這個nameHystrixCommand的名字

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

給這個過濾器包裝一個名字叫myCommandNameHystrixCommand

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屬性列表中。

application.yml

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.  示例

 

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

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>

application.yml

 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

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-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>

application.yml

 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

Application.java

 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

截兩張圖吧

 

下面看效果

效果一:正常路由

效果二:限流

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

 

http://www.javashuo.com/article/p-saphxhwr-hm.html

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

相關文章
相關標籤/搜索