Spring Cloud Gateway过滤器配置

往期回顾

Nacos的安装与配置

Spring Cloud集成Nacos作为注册中心

LoadBalacer集成Nacos实现负载均衡

常见的负载均衡策略分析

Spring Cloud集成Dubbo实现RPC调用

SpringCloud集成Nacos作为配置中心

Nacos整合OpenFegin实现RPC调用

Nacos整合Gateway入门实例

前面我们已经介绍了Nacos 的安装与配置,Spring Cloud 集成Nacos 作为服务的注册中心和配置中心,集成Nacos 实现服务的负载均衡和一些常见的负载均衡策略、使用Dubbo、OpenFegin进行RPC调用以及整合Spring Cloud Gateway作为服务的网关

Nacos整合Gateway入门实例中,我们只是简单的介绍了如何集成Spring Cloud Gateway ,下面我们来详细说说Spring Cloud Gateway 的一些特性

Spring Cloud Gateway 建立在 Spring Boot 2.x、Spring WebFlux 和 Project Reactor 之上。 因此,当您使用 Spring Cloud Gateway 时,您所知道的许多熟悉的同步库(例如 Spring Data 和 Spring Security)和模式可能并不适用。 如果您不熟悉这些项目,我们建议您在使用 Spring Cloud Gateway 之前先阅读他们的文档以熟悉一些新概念。

Spring Cloud Gateway官网

路由的配置方式

基于配制文件

Spring Cloud Gateway 有两种配置路由和过滤器的方法:快捷方式和完全扩展的参数。下面的大多数示例都使用快捷方式。

快捷方式配置

快捷方式配置由过滤器名称识别,后跟等号 ( =),后跟以逗号 ( ,) 分隔的参数值。

spring:
  cloud:
    gateway:
      routes:
      - id: after_route #路由的ID,保证全局唯一就行了,建议和服务名搭配命名
        uri: https://example.org #uri,断言匹配时转发的uri
        predicates:
        - Cookie=mycookie,mycookievalue #断言,当断言匹配时就会转发至uri

完全扩展的参数

完全扩展的参数看起来更像是带有名称/值对的标准 yaml 配置。通常,会有一把name钥匙和一把args钥匙。键是用于配置谓词或过滤器的args键值对映射。

spring:
  cloud:
    gateway:
      routes:
      - id: after_route #路由的ID,保证全局唯一就行了,建议和服务名搭配命名
        uri: https://example.org #uri,断言匹配时转发的uri
        predicates:
        - name: Cookie
          args:
            name: mycookie
            regexp: mycookievalue

之前我们项目中用到的路由匹配规则就是快捷方式

service-url:
  user-service: http://localhost:8080

spring:
  cloud:
    gateway:
      # 静态路由      
      routes:
        - id: path_route
          uri: ${
    
    service-url.user-service}/user/get/{
    
    id}
          predicates:
            - Path=/user/get/{
    
    id}

这个配置的意思就是当请求的URI/user/get/{id} 相匹配时,就会将请求转发至http://localhost:8080/user/get/{id}

基于Java代码的配置方式

除了可以通过配制文件的方式,我们还可以通过Java代码进行硬编码,但是我个人不推荐这种方式,因为一是我认为在配制文件中配置路由可读性更高,另外就是配制文件可以整合配置中心实现动态路由,而显然,Java硬编码的方式是没法配合配置中心进行动态路由的,灵活性不高

// Spring 官方的例子
@SpringBootApplication
public class DemogatewayApplication {
    
    
	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    
    
		return builder.routes()
			.route("path_route", r -> r.path("/get")
				.uri("http://httpbin.org"))
			.route("host_route", r -> r.host("*.myhost.org")
				.uri("http://httpbin.org"))
			.route("rewrite_route", r -> r.host("*.rewrite.org")
				.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
				.uri("http://httpbin.org"))
			.route("hystrix_route", r -> r.host("*.hystrix.org")
				.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
				.uri("http://httpbin.org"))
			.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
				.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
				.uri("http://httpbin.org"))
			.route("limit_route", r -> r
				.host("*.limited.org").and().path("/anything/**")
				.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
				.uri("http://httpbin.org"))
			.build();
	}
}

网上Copy的代码

package com.atguigu.springcloud.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author LiFang
 * @version 1.0
 * @since 2021/12/30 21:02
 * 使用java配置路由
 */

@Configuration
public class GateWayConfig {
    
    
    /**
     * @param routeLocatorBuilder
     * @return org.springframework.cloud.gateway.route.RouteLocator
     * @description 配置了一个id为route_name的路由规则,
     * 当访问地址为http://localhost:9527/guonei时会自动转发到地址:http://news.baidu.com/guonei
     * @author LiFang
     * @date 2021/12/30 21:27
     */
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
    
    
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("path_route_atguigu",
                r -> r.path("/guonei")
                        .uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }
    
    @Bean
    public RouteLocator customRouteLocator2(RouteLocatorBuilder builder) {
    
    
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("path_route_atguigu2", 
        		r -> r.path("/guoji")
        				.uri("http://news.baidu.com/guoji")).build();
        return routes.build();
    }
}

过滤器

Route Predicate 的使用

Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。 Spring Cloud Gateway包括许多内置的Route Predicate工厂。 所有这些Predicate都与HTTP请求的不同属性匹配。 多个Route Predicate工厂可以进行组合,下面我们来介绍下一些常用的Route Predicate。

注意:Predicate中提到的配置都在application-predicate.yml文件中进行修改,并用该配置启动api-gateway服务。

Spring Cloud Gateway自带了很多断言过滤器,可以让我们在断言中使用,我这里只简单的列举几个常用的,想要详细了解的可以查阅官方文档

After Route Predicate

在指定时间之后的请求会匹配该路由。

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: ${
    
    service-url.user-service}
          predicates:
            - After=2019-09-24T16:30:00+08:00[Asia/Shanghai]

Before Route Predicate

在指定时间之前的请求会匹配该路由。

spring:
  cloud:
    gateway:
      routes:
        - id: before_route
          uri: ${
    
    service-url.user-service}
          predicates:
            - Before=2019-09-24T16:30:00+08:00[Asia/Shanghai]

Between Route Predicate

在指定时间区间内的请求会匹配该路由。

spring:
  cloud:
    gateway:
      routes:
        - id: before_route
          uri: ${
    
    service-url.user-service}
          predicates:
            - Between=2019-09-24T16:30:00+08:00[Asia/Shanghai], 2019-09-25T16:30:00+08:00[Asia/Shanghai]

Cookie Route Predicate

带有指定Cookie的请求会匹配该路由

spring:
  cloud:
    gateway:
      routes:
        - id: cookie_route
          uri: ${
    
    service-url.user-service}
          predicates:
            - Cookie=username,macro

带有cookie为username=macro的请求可以匹配该路由

Header Route Predicate

带有指定请求头的请求会匹配该路由

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: ${
    
    service-url.user-service}
        predicates:
        - Header=X-Request-Id, \d+

带有请求头为X-Request-Id:123的请求可以匹配该路由

Method Route Predicate

发送指定方法的请求会匹配该路由

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: ${
    
    service-url.user-service}
        predicates:
        - Method=GET

GET请求可以匹配该路由

Path Route Predicate

发送指定路径的请求会匹配该路由

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: ${
    
    service-url.user-service}/user/{
    
    id}
          predicates:
            - Path=/user/{
    
    id}

Query Route Predicate

带指定查询参数的请求可以匹配该路由

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: ${
    
    service-url.user-service}/user/getByUsername
        predicates:
        - Query=username

username=macro查询参数的请求可以匹配该路由。

RemoteAddr Route Predicate

从指定远程地址发起的请求可以匹配该路由

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: ${
    
    service-url.user-service}
        predicates:
        - RemoteAddr=192.168.1.1/24

从192.168.1.1发起请求可以匹配该路由

Weight Route Predicate

使用权重来路由相应请求,以下表示有80%的请求会被路由到localhost:8201,20%会被路由到localhost:8202

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: http://localhost:8201
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: http://localhost:8202
        predicates:
        - Weight=group1, 2

Route Filter 的使用

路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。Spring Cloud Gateway 内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生,下面我们介绍下常用路由过滤器的用法。

AddRequestHeader

AddRequestHeader GatewayFilter 采用名称和值参数。 以下示例配置一个 AddRequestHeader GatewayFilter:

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

这个例子将 X-Request-red:blue 标头添加到所有匹配请求的下游请求标头中

AddRequestParameter GatewayFilter

给请求添加参数的过滤器

spring:
  cloud:
    gateway:
      routes:
        - id: add_request_parameter_route
          uri: http://localhost:8201
          filters:
            - AddRequestParameter=username, macro
          predicates:
            - Method=GET

以上配置会对GET请求添加username=macro的请求参数,相当于发起该请求:

curl http://localhost:8201/user/getByUsername?username=macro

StripPrefix GatewayFilter

对指定数量的路径前缀进行去除的过滤器

spring:
  cloud:
    gateway:
      routes:
      - id: strip_prefix_route
        uri: http://localhost:8201
        predicates:
        - Path=/user-service/**
        filters:
        - StripPrefix=2

以上配置会把以/user-service/开头的请求的路径去除两位,通过curl工具使用以下命令进行测试

curl http://localhost:9201/user-service/a/user/1

相当于发起该请求:

curl http://localhost:8201/user/1

PrefixPath GatewayFilter

与StripPrefix过滤器恰好相反,会对原有路径进行增加操作的过滤器。

spring:
  cloud:
    gateway:
      routes:
      - id: prefix_path_route
        uri: http://localhost:8201
        predicates:
        - Method=GET
        filters:
        - PrefixPath=/user

以上配置会对所有GET请求添加/user路径前缀,通过curl工具使用以下命令进行测试。

curl http://localhost:9201/1

相当于发起该请求:

curl http://localhost:8201/user/1

Hystrix GatewayFilter

Hystrix 过滤器允许你将断路器功能添加到网关路由中,使你的服务免受级联故障的影响,并提供服务降级处理。

  • 要开启断路器功能,我们需要在pom.xml中添加Hystrix的相关依赖:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  • 然后添加相关服务降级的处理类:
@RestController
public class FallbackController {
    
    

    @GetMapping("/fallback")
    public Object fallback() {
    
    
        Map<String,Object> result = new HashMap<>();
        result.put("data",null);
        result.put("message","Get request fallback!");
        result.put("code",500);
        return result;
    }
}
  • 在application-filter.yml中添加相关配置,当路由出错时会转发到服务降级处理的控制器上:
spring:
  cloud:
    gateway:
      routes:
        - id: hystrix_route
          uri: http://localhost:8201
          predicates:
            - Method=GET
          filters:
            - name: Hystrix
              args:
                name: fallbackcmd
                fallbackUri: forward:/fallback
  • 关闭user-service,调用该地址进行测试:http://localhost:9201/user/1 ,发现已经返回了服务降级的处理信息。

img

RequestRateLimiter GatewayFilter

RequestRateLimiter 过滤器可以用于限流,使用RateLimiter实现来确定是否允许当前请求继续进行,如果请求太大默认会返回HTTP 429-太多请求状态。

  • 在pom.xml中添加相关依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
  • 添加限流策略的配置类,这里有两种策略一种是根据请求参数中的username进行限流,另一种是根据访问IP进行限流
@Configuration
public class RedisRateLimiterConfig {
    
    
    @Bean
    KeyResolver userKeyResolver() {
    
    
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("username"));
    }

    @Bean
    public KeyResolver ipKeyResolver() {
    
    
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }
}
  • 我们使用Redis来进行限流,所以需要添加Redis和RequestRateLimiter的配置,这里对所有的GET请求都进行了按IP来限流的操作
server:
  port: 9201
spring:
  redis:
    host: localhost
    password: 123456
    port: 6379
  cloud:
    gateway:
      routes:
        - id: requestratelimiter_route
          uri: http://localhost:8201
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1 #每秒允许处理的请求数量
                redis-rate-limiter.burstCapacity: 2 #每秒最大处理的请求数量
                key-resolver: "#{@ipKeyResolver}" #限流策略,对应策略的Bean
          predicates:
            - Method=GET
logging:
  level:
    org.springframework.cloud.gateway: debug

Retry GatewayFilter

对路由请求进行重试的过滤器,可以根据路由请求返回的HTTP状态码来确定是否进行重试。

  • 修改配置文件:
spring:
  cloud:
    gateway:
      routes:
      - id: retry_route
        uri: http://localhost:8201
        predicates:
        - Method=GET
        filters:
        - name: Retry
          args:
            retries: 1 #需要进行重试的次数
            statuses: BAD_GATEWAY #返回哪个状态码需要进行重试,返回状态码为5XX进行重试
            backoff:
              firstBackoff: 10ms
              maxBackoff: 50ms
              factor: 2
              basedOnPreviousValue: false
  • 当调用返回500时会进行重试,访问测试地址:http://localhost:9201/user/111
  • 可以发现user-service控制台报错2次,说明进行了一次重试。
2019-10-27 14:08:53.435 ERROR 2280 --- [nio-8201-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
	at com.macro.cloud.controller.UserController.getUser(UserController.java:34) ~[classes/:na]

添加默认过滤器

要添加过滤器并将其应用于所有路由,您可以使用spring.cloud.gateway.default-filters. 此属性采用过滤器列表。以下清单定义了一组默认过滤器:

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

自定义全局过滤器

当请求与路由匹配时,过滤 Web 处理程序会将 的所有实例GlobalFilter和所有特定于路由的实例添加GatewayFilter到过滤器链中。这个组合的过滤器链是按org.springframework.core.Ordered接口排序的,你可以通过实现getOrder()方法来设置。

由于 Spring Cloud Gateway 区分过滤器逻辑执行的“前”和“后”阶段(请参阅它的工作原理),具有最高优先级的过滤器是“前”阶段的第一个和“后”阶段的最后一个阶段。

参考示例

package cuit.epoch.pymjl.filter;

import lombok.extern.log4j.Log4j2;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @author Pymjl
 * @version 1.0
 * @date 2022/9/2 17:02
 **/
@Component
@Log4j2
public class MyGlobalFilter implements GlobalFilter, Ordered {
    
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        log.info("=====================come in MyGlobalFilter==========================");
        String username = exchange.getRequest().getQueryParams().getFirst("username");
        if (username == null) {
    
    
            log.error("用户名为null,非法用户");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 得到权重,order越小,优先级越高
     *
     * @return int
     */
    @Override
    public int getOrder() {
    
    
        return 0;
    }
}

我们启动项目进行测试

当带上username参数时访问正常

image-20220902171706049

当不带username参数时网关会路由失败

image-20220902171736678

观察后台输出

image-20220902171808402

跨域过滤器配置

您可以配置网关来控制 CORS 行为

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

在前面的示例中,允许来自docs.spring.io所有 GET 请求路径的请求的 CORS 请求。

要为某些网关路由谓词未处理的请求提供相同的 CORS 配置,请将spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping属性设置为true.

通常的配置是:

spring:
  cloud:
    gateway:
      default-filters:
      #  gateway 和下游系统同时支持了跨域,都会向 response header 中增加 Access-Control-Allow-Origin*,所以出现重复的 *。所以需要使用DedupeResponseHeader 来处理一下响应头重复的问题
        - DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_UNIQUE
      globalcors:
        cors-configurations:
          '[/**]':
			# 允许携带认证信息
            allow-credentials: true
            # 允许跨域的源(网站域名/ip),设置*为全部
            allowedOrigins: "*"
            # 允许跨域的method, 默认为GET和OPTIONS,设置*为全部
            allowedMethods: "*"
             # 允许跨域请求里的head字段,设置*为全部
            allowedHeaders: "*"

至此,关于Spring Cloud Gateway的进一步解释到这里就结束啦,后面我们会继续介绍Spring Cloud Gateway的动态路由配置以及负载均衡

项目源码:gitee github

参考文章:

Spring Cloud Gateway:新一代API网关服务

  • DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_UNIQUE
    globalcors:
    cors-configurations:
    ‘[/**]’:
    # 允许携带认证信息
    allow-credentials: true
    # 允许跨域的源(网站域名/ip),设置为全部
    allowedOrigins: "
    "
    # 允许跨域的method, 默认为GET和OPTIONS,设置为全部
    allowedMethods: "
    "
    # 允许跨域请求里的head字段,设置为全部
    allowedHeaders: "
    "

至此,关于Spring Cloud Gateway的进一步解释到这里就结束啦,后面我们会继续介绍Spring Cloud Gateway的动态路由配置以及负载均衡

项目源码:[gitee](https://gitee.com/pymjl_0/cloud-learn)	[github](https://github.com/Pymjl/cloud-learn)

参考文章:

[Spring Cloud Gateway:新一代API网关服务](https://www.macrozheng.com/cloud/gateway.html#retry-gatewayfilter)

[官方文档](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories)

猜你喜欢

转载自blog.csdn.net/apple_52109766/article/details/126695092