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中的Zuul还是1.3.1版本

而且,官网中也明确说了不再维护Zuul了

(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。这三个选型本身没有什么明显的区别,主要还是看技术栈是否能满足快速应用和二次开发。

以上说的这些功能,这些开源的网关组件都有,或者借助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 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.  原理

(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 )
 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 )
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 )
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             )
16                 .host("**.throttle.org").and().path("/get")
17                 ,
18                         ,
19                         ,
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         );
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    : [] 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

微服务网关 Spring Cloud Gateway的更多相关文章

  1. Spring Cloud 微服务三: API网关Spring cloud gateway

    前言:前面介绍了一款API网关组件zuul,不过发现spring cloud自己开发了一个新网关gateway,貌似要取代zuul,spring官网上也已经没有zuul的组件了(虽然在仓库中可以更新到 ...

  2. 从0开始构建你的api网关--Spring Cloud Gateway网关实战及原理解析

    API 网关 API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题 ...

  3. 微服务之Spring cloud

    微服务 Spring cloud Spring Cloud provides tools for developers to quickly build some of the common patt ...

  4. 微服务与Spring Cloud资料

    Microservices Using Spring Boot and Spring Cloud 微服务注册中心 Eureka 架构深入解读 50+ 顶级开源 Kubernetes 工具列表 Apol ...

  5. 第十章 消息驱动的微服务: Spring Cloud Stream

    Spring Cloud Stream 是一个用来为微服务应用构建消息驱动能力的框架. 它可以基于Spring Boot 来创建独立的. 可用于生产的 Spring 应用程序. 它通过使用 Sprin ...

  6. 微服务示例-Spring Cloud

    1~开发准备 JDK:1.8 Spring Boot:1.5.9.RELEASE Spring Coud:Edgware.RELEASE IDE:IntelliJ IDEA 2017 Maven:3. ...

  7. 当下最流行的微服务与spring cloud,你搞清楚了吗?

    微服务架构:Spring-Cloud 什么是微服务? 微服务就是把原本臃肿的一个项目的所有模块拆分开来并做到互相没有关联,甚至可以不使用同一个数据库. 比 如:项目里面有User模块和Power模块, ...

  8. 微服务框架Spring Cloud介绍 Part1: 使用事件和消息队列实现分布式事务

    http://skaka.me/blog/2016/04/21/springcloud1/ 不同于单一架构应用(Monolith), 分布式环境下, 进行事务操作将变得困难, 因为分布式环境通常会有多 ...

  9. 网关服务Spring Cloud Gateway(二)

    上一篇文章服务网关 Spring Cloud GateWay 初级篇,介绍了 Spring Cloud Gateway 的相关术语.技术原理,以及如何快速使用 Spring Cloud Gateway ...

随机推荐

  1. 初识openstack

    <1>虚拟化技术的功能和特点 多个虚拟机运行在一台物理服务器上,虚拟机之间共享物理资源,虚拟机可以分别安装不同的操作系统,应用程序相互隔离. 虚拟化技术的优势:虚拟机的操作系统和应用程序不 ...

  2. [转载]DBA的特质第二部分:性格

    本文转自http://www.searchdatabase.com.cn/showcontent_84382.htm 支持原创.尊重原创,分享知识! 在本系列文章中,笔者将谈一谈数据库管理员(DBA) ...

  3. maven 安装alipay-sdk包到本地及远程仓库

    安装到本地:mvn install:install-file -DgroupId=com.alipay -DartifactId=sdk-Java -Dversion=*** -Dpackaging= ...

  4. PHP7的安装

    PHP7和HHVM比较PHP7的在真实场景的性能确实已经和HHVM相当, 在一些场景甚至超过了HHVM.HHVM的运维复杂, 是多线程模型, 这就代表着如果一个线程导致crash了, 那么整个服务就挂 ...

  5. centos的网路配置文件的位置

    在这个路径下:/etc/sysconfig/network-scripts/ 每个网卡有相应的配置文件

  6. 华为&quot;128为大整数相加&quot;机试题

    最近正直春招,偶尔接触到了华为的这道大整数相加的测试题,在网上找了一个算法,然后自己尝试进行了优化,最后也对memmove()函数效率有了进一步把握. #include <time.h># ...

  7. Show or Hide Menu List via ng-show

    <div ng-app ng-controller='MenuController'> <ul ng-show='menuState_show'> <li>Stun ...

  8. css 兼容小三角

    <!DOCTYPE><html ><head><meta http-equiv="Content-Type" content=" ...

  9. URL不能过长,否则summit方法提交失败

    MVC5.0+EF6.0,和浏览器的版本有关系.IE最多1024KB. URL不能过长,否则summit方法提交失败.

  10. 20162311 实验三 敏捷开发与XP实践 实验报告

    20162311 实验三 敏捷开发与XP实践 实验报告 实验内容 一.研究学习IDEA中的Code菜单 使用Code ->Reformate Code功能将以下代码格式化 public clas ...