关于java:深入Java微服务之网关系列4-SpringCloud-gateway详解史上最全

27次阅读

共计 18506 个字符,预计需要花费 47 分钟才能阅读完成。

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 面试必备精品电子书。

正文完
 0