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

; "复制代码")

<pre>spring:
cloud:

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

; "复制代码")

这个路由匹配“美国丹佛工夫2017-01-20 17:42”之后的任意申请

3.4.2.  Header Route Predicate Factory

; "复制代码")

<pre>spring:
cloud:

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

; "复制代码")

这个路由匹配“申请头蕴含X-Request-Id并且其值匹配正则表达式\d+”的任意申请

3.4.3.  Method Route Predicate Factory

; "复制代码")

<pre>spring:
cloud:

gateway:  routes:  - id: method_route    uri: https://example.org    predicates:    - Method=GET</pre>

; "复制代码")

这个路由匹配任意GET申请

3.4.4.  Path Route Predicate Factory

; "复制代码")

<pre>spring:
cloud:

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

; "复制代码")

这个路由匹配这样门路的申请,比方:/foo/1 或 /foo/bar 或 /bar/baz

3.4.5.  Query Route Predicate Factory

这个Predicate有两个参数:一个必须的参数名和一个可选的正则表达式

; "复制代码")

<pre>spring:
cloud:

gateway:  routes:  - id: query_route    uri: https://example.org    predicates:    - Query=baz</pre>

; "复制代码")

这个路由匹配“查问参数中蕴含baz”的申请

; "复制代码")

<pre>spring:
cloud:

gateway:  routes:  - id: query_route    uri: https://example.org    predicates:    - Query=foo, ba.</pre>

; "复制代码")

这个路由匹配“查问参数中蕴含foo,并且其参数值满足正则表达式ba.”的申请,比方:bar,baz

3.4.6.  RemoteAddr Route Predicate Factory

这个路由承受一个IP(IPv4或IPv6)地址字符串。例如:192.168.0.1/16,其中192.168.0.1,16是子网掩码

; "复制代码")

<pre>spring:
cloud:

gateway:  routes:  - id: remoteaddr_route    uri: https://example.org    predicates:    - RemoteAddr=192.168.1.1/24</pre>

; "复制代码")

这里路由匹配近程地址是这样的申请,例如:192.168.1.10

3.5.  GatewayFilter Factories(网关过滤器)

路由过滤器容许以某种形式批改传入的HTTP申请或传出HTTP响应。路由过滤器的作用域是特定的路由。Spring Cloud Gateway蕴含许多内置的网关过滤器工厂。

3.5.1.  AddRequestHeader GatewayFilter Factory

; "复制代码")

<pre>spring:
cloud:

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

; "复制代码")

对于所有匹配的申请,将会给传给上游的申请增加一个申请头 X-Request-Foo:Bar

3.5.2.  AddRequestParameter GatewayFilter Factory

; "复制代码")

<pre>spring:
cloud:

gateway:  routes:  - id: add_request_parameter_route    uri: https://example.org    filters:    - AddRequestParameter=foo, bar</pre>

; "复制代码")

对于所有匹配的申请,将给传给上游的申请增加一个查问参数 foo=bar

3.5.3.  AddResponseHeader GatewayFilter Factory

; "复制代码")

<pre>spring:
cloud:

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

; "复制代码")

对于所有匹配的申请,增加一个响应头 X-Response-Foo:Bar

3.5.4.  Hystrix GatewayFilter Factory

Hystrix网关过滤器容许你将断路器引入网关路由,爱护你的服务免受级联失败的影响,并在上游产生故障时提供准备响应。

为了启用Hystrix网关过滤器,你须要引入 spring-cloud-starter-netflix-hystrix

Hystrix网关过滤器须要一个name参数,这个name是HystrixCommand的名字

; "复制代码")

<pre>spring:
cloud:

gateway:  routes:  - id: hystrix_route    uri: https://example.org    filters:    - Hystrix=myCommandName</pre>

; "复制代码")

给这个过滤器包装一个名字叫myCommandName的HystrixCommand

Hystrix网关过滤器也承受一个可选的参数fallbackUri,然而目前只反对forward:前缀的URL。也就是说,如果这个fallback被调用,申请将被重定向到匹配的这个URL。

; "复制代码")

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

; "复制代码")

当fallback被调用的时候,申请将被重定向到/incaseoffailureusethis

; "复制代码")

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

; "复制代码")

在这个例子中,专门定义了一个端点来解决/fallback申请,它在localhost:9994上。也就是说,当fallback被调用的时候将重定向到http://localhost:9994/fallback

3.5.5.  PrefixPath GatewayFilter Factory

; "复制代码")

<pre>spring:
cloud:

gateway:  routes:  - id: prefixpath_route    uri: https://example.org    filters:    - PrefixPath=/mypath</pre>

; "复制代码")

所有匹配的申请都将加上前缀/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 

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

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

; "复制代码")

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

; "复制代码")

Config.java

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

这里定义了每个用户的申请速率限度为10。容许应用20个申请,然而在接下来的一秒中,只有10个申请可用。

这个例子中只是简略地从申请参数中获取"user",在理论生产环境中不倡议这么做。

咱们也能够通过实现RateLimiter接口来自定义,这个时候,在配置中咱们就须要援用这个Bean,例如:#{@myRateLimiter} 

; "复制代码")

<pre>spring:
cloud:

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

; "复制代码")

3.5.7.  Default Filters

如果你想要增加一个过滤器并且把它利用于所有路由的话,你能够用spring.cloud.gateway.default-filters。这个属性承受一个过滤器列表。

<pre>spring:
cloud:

gateway:  default-filters:  - AddResponseHeader=X-Response-Default-Foo, Default-Bar  - PrefixPath=/httpbin</pre>

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 

<pre> 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 } </pre>

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

; "复制代码")

<pre>spring:
cloud:

gateway:  routes:  - id: myRoute    uri: lb://service    predicates:    - Path=/service/**</pre>

; "复制代码")

默认状况下,当一个服务实例在LoadBalancer中没有找到时,将返回503。你能够通过配置spring.cloud.gateway.loadbalancer.use404=true来让它返回404。

3.7.  配置

RouteDefinitionLocator.java 

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

默认状况下,PropertiesRouteDefinitionLocator通过@ConfigurationProperties机制加载属性

上面两段配置是等价的

; "复制代码")

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

; "复制代码")

上面用Java配置

GatewaySampleApplication.java 

<pre> 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 } </pre>

这种格调容许自定义更多的谓词断言,默认是逻辑与(and)。你也能够用and() , or() , negate() 

再来一个例子

<pre> 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 } </pre>

3.8.  CORS配置

; "复制代码")

<pre>spring:
cloud:

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

; "复制代码")

下面的例子中,所有原始为docs.spring.io的GET申请均被容许跨域申请。

4.  示例

本例中又4个我的项目,如下图:

4.1.  cjs-eureka-server

pom.xml

<pre> 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/m...d">
4 <modelVersion>4.0.0</modelVersion>
5 <parent>
6 <groupId>org.springframework.boot</groupId>
7 spring-boot-starter-parent
8 <version>2.1.6.RELEASE</version>
9 <relativePath/> <!-- lookup parent from repository -->
10 </parent>
11 <groupId>com.cjs.example</groupId>
12 cjs-eureka-server
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 spring-cloud-starter-netflix-eureka-server
25 </dependency>
26 <dependency>
27 <groupId>ch.qos.logback</groupId>
28 logback-classic
29 <version>1.2.3</version>
30 </dependency>
31 </dependencies>
32
33 <dependencyManagement>
34 <dependencies>
35 <dependency>
36 <groupId>org.springframework.cloud</groupId>
37 spring-cloud-dependencies
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 spring-boot-maven-plugin
50 </plugin>
51 </plugins>
52 </build>
53
54 </project> </pre>

application.yml

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

Application.java

<pre> 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 } </pre>

4.2.  cjs-gateway-server

pom.xml

<pre> 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/m...d">
4 <modelVersion>4.0.0</modelVersion>
5 <parent>
6 <groupId>org.springframework.boot</groupId>
7 spring-boot-starter-parent
8 <version>2.1.6.RELEASE</version>
9 <relativePath/> <!-- lookup parent from repository -->
10 </parent>
11 <groupId>com.cjs.example</groupId>
12 cjs-gateway-server
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 spring-cloud-starter-gateway
25 </dependency>
26
27 <dependency>
28 <groupId>org.springframework.boot</groupId>
29 spring-boot-starter-data-redis-reactive
30 </dependency>
31 <dependency>
32 <groupId>ch.qos.logback</groupId>
33 logback-classic
34 <version>1.2.3</version>
35 </dependency>
36 </dependencies>
37
38 <dependencyManagement>
39 <dependencies>
40 <dependency>
41 <groupId>org.springframework.cloud</groupId>
42 spring-cloud-dependencies
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 spring-boot-maven-plugin
55 </plugin>
56 </plugins>
57 </build>
58
59 </project> </pre>

application.yml

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

Application.java

<pre> 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 }</pre>

其余代码就不一一贴出来了,都在git上

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

截两张图吧

上面看成果

成果一:失常路由

成果二:限流

<pre>1 ab -n 20 -c 10 http://10.0.29.187:8080/price... </pre>

察看控制台会看到

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

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
微信公众号【程序员黄小斜】作者是前蚂蚁金服Java工程师,专一分享Java技术干货和求职成长心得,不限于BAT面试,算法、计算机根底、数据库、分布式、spring全家桶、微服务、高并发、JVM、Docker容器,ELK、大数据等。关注后回复【book】支付精选20本Java面试必备精品电子书。