Spring Cloud Gateway学习笔记

Gateway是一个API网关服务,提供了反向代理、鉴权、流量控制。熔断、日志监控等功能。

官方文档

一、服务创建

  1. 创建项目;
  2. 引入依赖:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
  1. 网关实现
    主要有配置文件实现与Java配置类实现两种方式

二.、断言规则配置

2.1、 配置类

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;

/**
 * gateway配置类
 *
 * @Since 2021-01-26 21:43
 */
@Configuration
public class GatewayConfig {
    @Bean
    public RouteLocator getRouteLocator(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("route", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }
}

即访问"/guonei"将跳转到"http://news.baidu.com/guonei"。

注:HTTPS的网站不行

2.2、 配置文件

spring:
  cloud:
    gateway:
      enabled: true  # 是否开启网关,默认true
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由
      routes:
        - id: payment_route1  # 路由id,唯一
          # 路由转发后实际访问地址,lb为负载均衡(默认轮训),payment-service为微服务名,也可以为http://127.0.0.1:8000或者http://test.com等
          uri: lb://payment-service
          predicates:  # 断言,设置匹配URL的规则
            # 可以允许访问的接口路径,不配置即默认可以访问所有接口,多个以“,”英文逗号分隔,- Path在每个 - id(即每组路由规则)中只能有一个,可以写为/payment/**一次性匹配多个
            - Path=/payment/getById/**,/payment/add
            - After=2021-01-27T20:47:01.941+08:00[Asia/Shanghai]  # 路由在该时间之后生效
            - Before=2022-01-27T20:47:01.941+08:00[Asia/Shanghai]  # 路由在该时间之前生效
            # 路由在这段时间之内生效
            - Between=2021-01-27T20:47:01.941+08:00[Asia/Shanghai],2022-01-27T21:47:01.941+08:00[Asia/Shanghai]
            - Cookie=username,zhangsan  # 必须带有cookie并且“username”的值与配置的“zhangsan”正则匹配成功
            - Header=X-Request-Id,\d+  # 请求头必须带有"X-Request-Id"属性并且值与配置的“\d+”正则匹配成功
            - Host=**.somehost.com  # 必须带有"Host"并且值与配置的“**.somehost.com”正则匹配成功
            - Method=GET,POST  # 设置请求方式
            - Query=username,\d+  # 设置参数,即带有参数username并且值与配置的“\d+”正则匹配成功
            - Query=id,\d+  # 多个参数可以有多个Query,猜测(待验证):所有入Query这种一次配置一个键值对的属性,都可以有多个
            # 限制可以访问网关的IP,如果网关在代理层之后要注意IP的真实性
            - RemoteAddr=127.0.0.1/24,192.168.0.1/16,172.0.0.1/8,10.25.48.32
            # 权重,group1为组名,3为权重值,int类型,要是有权重,uri不能是服务名,本路由权重值/同组所有路由权重值之和=分配到本路由的几率
            - Weight=group1,3

时间获取:

ZonedDateTime time = ZonedDateTime.now();

测试请求样例:

curl -X GET "http://127.0.0.1:9527/payment/getById/5?username=123&id=1" --cookie "username=zhangsan" -H "X-Request-Id:123456" -H "Host:www.somehost.com"

注:注意yml格式

三. 过滤器

可以对经过路由的请求和响应做处理。

3.1. Gateway Fiter

3.1.1 操作消息头

spring:
  cloud:
    gateway:
      enabled: true  # 是否开启网关,默认true
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由
      routes:
        - id: payment_route1  # 路由id,唯一
          # 路由转发后实际访问地址,lb为负载均衡(默认轮训),payment-service为微服务名,也可以为http://127.0.0.1:8000或者http://test.com
          uri: lb://payment-service
          filters:  # 过滤器
            - AddRequestHeader=X-Request-red, blue  # 在请求头中加入X-Request-red = blue
            - SetRequestHeader=X-Request-Red, Blue  # 将请求消息头中的X-Request-Red的值设置(替换)为Blue
            - SetRequestHeader=foo, bar-{segment}  # 如果- Host: {segment}.myhost.org,可以动态获取
            - RemoveRequestHeader=X-Request-Foo  # 移除请求消息头中的X-Request-Foo
            # 在请求头中加入X-Request-red-id = blue + URL中的入参id,注意:要使用这个,获取URL中的入参,必须把URL写在 - Path中
            - AddRequestHeader=X-Request-red-id, blue-{id}
            - AddResponseHeader=X-Response-color, Blue  # 在响应消息头中添加
            - SetResponseHeader=X-Response-Red, Blue  # 将响应头中的X-Response-Red的值设置(替换)为Blue
            - SetResponseHeader=foo, bar-{segment}  # 如果- Host: {segment}.myhost.org,可以动态获取
            - RemoveResponseHeader=X-Response-Foo  # 移除响应消息头中的X-Response-Foo
            - AddResponseHeader=foo, bar-{segment}  # 在响应消息头中添加,值可以动态获取URL中的参数
            # 删除响应消息头中“重复”的参数,多个以“空格”分隔
            - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
            # RETAIN_FIRST (default), RETAIN_LAST, and RETAIN_UNIQUE
            - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_LAST
            # 从请求消息头中取出Blue的值,赋值给X-Request-Red,如果Blue不存在,则没有影响,如果X-Request-Red不存在,则创建
            - MapRequestHeader=Blue, X-Request-Red
            # 改写响应消息头中Location的值,四个参数
            # 第一个参数:NEVER_STRIP:版本信息不会被剥离,即使原始请求路径不包含版本
            #           AS_IN_REQUEST:仅当原始请求路径不包含任何版本时,才会剥离版本【默认】
            #           ALWAYS_STRIP:即使原始请求路径包含版本,也会剥离版本
            # 第三个参数:如果提供,会替换 Response Header Location 值中的 host:port 部分;如果不提供,则会使用 Request 的 Host 作为默认值
            # 第四个参数:协议会与该值匹配,如果不匹配,过滤器不回做任何操作,默认值 http|https|ftp|ftps
            - RewriteLocationResponseHeader=AS_IN_REQUEST, Location, ,
            - RewriteResponseHeader=X-Response-Red, , password=[^&]+, password=***  # 重写响应消息头,支持正则
            # 没有值,为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
            - PreserveHostHeader
            - name: SetRequestHostHeader  # 在某些情况下,可能需要覆盖主机标头。在这种情况下可以用指定的值替换现有的主机头
              args:
                host: example.org

3.1.2 操作消息体

  • 操作单个参数
spring:
  cloud:
    gateway:
      enabled: true  # 是否开启网关,默认true
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由
      routes:
        - id: payment_route1  # 路由id,唯一
          # 路由转发后实际访问地址,lb为负载均衡(默认轮训),payment-service为微服务名,也可以为http://127.0.0.1:8000或者http://test.com
          uri: lb://payment-service
          filters:  # 过滤器
            - AddRequestParameter=color, blue  # 在请求中加入参数,color为字段名,blue为
            - AddRequestParameter=foo, bar-{segment}  # 在请求中加入参数,值可以动态获取URL中的参数拼接
            - RemoveRequestParameter=red  # 移除请求参数red
  • 操作整个消息体

修改请求体

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
                    (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
        .build();
}

static class Hello {
    String message;

    public Hello() { }

    public Hello(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

修改响应体

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
            .filters(f -> f.prefixPath("/httpbin")
                .modifyResponseBody(String.class, String.class,
                    (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri))
        .build();
}

3.1.3 断路器

spring:
  cloud:
    gateway:
      enabled: true  # 是否开启网关,默认true
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由
      routes:
        - id: payment_route1  # 路由id,唯一
          # 路由转发后实际访问地址,lb为负载均衡(默认轮训),payment-service为微服务名,也可以为http://127.0.0.1:8000或者http://test.com
          uri: lb://payment-service
          filters:  # 过滤器
            - name: CircuitBreaker  # 断路器
              args:
                name: myCircuitBreaker  # 自定义断路器
                fallbackUri: forward:/fallback  # 转发到回退方法,该方法也不一定要在网关服务上,可以通过路由调用其他服务上的接口
                statusCodes:  # 设置使用断路器的状态码,可以使用带有状态代码值的整数或HttpStatus枚举的字符串表示
                  - 500
                  - "NOT_FOUND"

3.1.4 操作URL

spring:
  cloud:
    gateway:
      enabled: true  # 是否开启网关,默认true
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由
      routes:
        - id: payment_route1  # 路由id,唯一
          # 路由转发后实际访问地址,lb为负载均衡(默认轮训),payment-service为微服务名,也可以为http://127.0.0.1:8000或者http://test.com
          uri: lb://payment-service
          filters:  # 过滤器
            # 改写路径,当请求/aaa 接口时,会改写去请求/test 接口,URL可以是正则,请注意,由于 YAML 规范,$应替换$\为
            - RewritePath=/aaa, /test
            - PrefixPath=/gateway  # 在接口URL前面拼接上/gateway后,在转发
            - RedirectTo=302, https://acme.org  # 重定向,第一个参数为300系列的状态码
            - SetPath=/{segment}  # 如果- Path=/red/{segment},对于/red/blue的请求路径,这会在发出下游请求之前将路径设置为/blue。
            - StripPrefix=2  # 从头部开始去掉2个/以及后面的值,如/name/blue/red会变为/red

3.1.5 限流

spring:
  redis:
    host: 192.168.10.128
    port: 6379
  cloud:
    gateway:
      enabled: true  # 是否开启网关,默认true
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由
      routes:
        - id: payment_route1  # 路由id,唯一
          # 路由转发后实际访问地址,lb为负载均衡(默认轮训),payment-service为微服务名,也可以为http://127.0.0.1:8000或者http://test.com
          uri: lb://payment-service
          filters:  # 过滤器
            - name: RequestRateLimiter  # 限流
              args:
                redis-rate-limiter.replenishRate: 10  # 平均每秒的请求数(在requestedTokens为1的时候)
                # 突发情况下允许用户在一秒内执行的最大请求数(在requestedTokens为1的时候)。这是令牌桶可以容纳的令牌数量。将此值设置为零会阻止所有请求。
                redis-rate-limiter.burstCapacity: 20
                # 请求花费多少令牌。这是每个请求从存储桶中获取的令牌数量,默认为 1。
                redis-rate-limiter.requestedTokens: 1
                key-resolver: "#{@apiKeyResolver}"
            - name: RequestSize  # 设置请求的大小,超过则被拒绝,默认请求大小设置为 5 MB
              args:
                maxSize: 5000000  # 没有单位这则是默认B,可以是1MB等
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * 网关限流配置类
 *
 * @author guoli
 * @data 2021-09-12 17:28
 */
@Configuration
public class RequestRateLimiterConfig {
    @Bean
    @Primary
    KeyResolver apiKeyResolver() {
        // 按URL限流,即以每秒内请求数按URL分组统计,超出限流的url请求都将返回429状态
        return exchange -> Mono.just(exchange.getRequest().getPath().toString());
    }

    @Bean
    KeyResolver userKeyResolver() {
        // 按用户限流
        return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getQueryParams().getFirst("user")));
    }

    @Bean
    KeyResolver ipKeyResolver() {
        // 按IP来限流
        return exchange -> Mono.just(Objects.requireNonNull(exchange.getRequest().getRemoteAddress()).getHostName());
    }
}

3.1.6 重试(请求失败后重新发送请求)

spring:
  cloud:
    gateway:
      enabled: true  # 是否开启网关,默认true
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由
      routes:
        - id: payment_route1  # 路由id,唯一
          # 路由转发后实际访问地址,lb为负载均衡(默认轮训),payment-service为微服务名,也可以为http://127.0.0.1:8000或者http://test.com
          uri: lb://payment-service
          filters:  # 过滤器
            - name: Retry  # 重试
              args:
                retries: 3  # 应该尝试的重试次数,默认3
                statuses: BAD_GATEWAY  # 应该重试的 HTTP 状态码,用org.springframework.http.HttpStatus表示
                series: CLIENT_ERROR  # 应该重试的 HTTP 状态码,用org.springframework.http.HttpStatus.Series表示,默认SERVER_ERROR
                methods: GET,POST  # 应该重试的 HTTP 方法,默认获取方法
                exceptions: TimeoutException  # 应该重试的异常,默认IOException和TimeoutException
                backoff:  # 为重试配置的指数退避,默认禁用,重试的间隔时间为firstBackoff乘以factor的n次方,其中n为迭代次数
                  firstBackoff: 10ms
                  maxBackoff: 50ms  # 最大后退限制
                  factor: 2
                  basedOnPreviousValue: false  # 如果为true,则使用(PreviousBackoff * factor)计算退避。

3.1.7 其他

spring:
  cloud:
    gateway:
      enabled: true  # 是否开启网关,默认true
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由
      routes:
        - id: payment_route1  # 路由id,唯一
          # 路由转发后实际访问地址,lb为负载均衡(默认轮训),payment-service为微服务名,也可以为http://127.0.0.1:8000或者http://test.com
          uri: lb://payment-service
          filters:  # 过滤器
            - SaveSession  # 保存 Session,在向下游服务转发请求之前强制执行 WebSession::save操作
            - SetStatus=401  # 设置返回状态码,无论什么情况,都会返回401
            - SetStatus=BAD_REQUEST  # 可能是枚举的整数值404或字符串表示形式
            - TokenRelay=  # token转发,需要添加 org.springframework.boot:spring-boot-starter-oauth2-client 依赖项

3.1.8 默认过滤器

应用于所有路由

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

2. Global Fiter

3.2.1 自定义全局过滤器

可用于日志、鉴权等。

import lombok.extern.slf4j.Slf4j;
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.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 路由日志过滤器
 *
 * @Author guoli
 * @Since 2021-01-27 22:51
 */
@Component
@Slf4j
public class LogGatewayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 校验参数username不能为空
        String username = exchange.getRequest().getQueryParams().getFirst("username");
        if (StringUtils.isEmpty(username)) {
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 加载过滤器的优先级,值越小优先级越高
     *
     * @return int
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

3.2.2 网关指标过滤器(The Gateway Metrics Filter)

四 其他

4.1 DiscoveryClient

  1. 加注解

启动类加注解 @EnableDiscoveryClient 或其他发现服务的注解如 @EnableEurekaClient 。

  1. 配置
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由
          lower-case-service-id: true  # 开启服务ID小写
  1. 作用

1、uri可以配置微服务名
2、向网关发送的请求可以带上目标服务的微服务名指定请求服务,gateway在下发请求之前会自动去掉微服务名。(注:必须开启微服务名小写

4.2 打印网络日志

Java程序启动参数新增 -Dreactor.netty.http.server.accessLogEnabled=true

在这里插入图片描述
在这里插入图片描述

4.3 远程操作已部署好的网关

4.3.1 开放端点

# 远程查询端点
management:
  endpoint:
    gateway:
      enabled: true
  endpoints:
    web:
      exposure:
        include: gateway

4.3.2通过GET请求查询

4.3.2.1 查询详细的路由信息

查询全部
curl -X GET http://gatewayIP或域名:端口/actuator/gateway/routes

查询单个
curl -X GET http://gatewayIP或域名:端口/actuator/gateway/routes/{id}

默认启用,禁用配置

spring:
  cloud:
    gateway:
      actuator:
        verbose:
          enabled: false
4.3.2.2 查询全局过滤器

查询所有全局过滤器对象以及它们在过滤器链路中的执行顺序(order)(注:如果是通过 @Order 实现则查询不出,返回null,只有实现 ordered 接口才能查到

向gateway服务发送请求:
curl -X GET http://IgatewayP或域名:端口/actuator/gateway/globalfilters

4.3.2.3 刷新路由缓存

向gateway服务发送请求:
curl -X POST http://IgatewayP或域名:端口/actuator/gateway/refresh
请求成功返回 200 没有响应正文

4.3.2.4 创建与删除特定路由

创建:
向 /gateway/routes/{id_route_to_create} 发送POST请求,入参:

{
  "id": "first_route",
  "predicates": [{
    "name": "Path",
    "args": {"_genkey_0":"/first"}
  }],
  "filters": [],
  "uri": "https://www.uri-destination.org",
  "order": 0
}

删除
向 /gateway/routes/{id_route_to_delete} 发送DELETE请求

注:创建与删除都报404

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值