介绍

在微服务项目中,各个功能模块拆分成了一个个微服务,每一个微服务都拥有自己的 ip 和端口。随着微服务逐渐增多时,客户端要去访问不同的服务,就要记住每个服务的 ip 和端口号,这在开发时简直就是灾难性的,混乱的。最终导致访问的入口不统一。

为了解决上面的问题,我们需要将客户端访问入口进行统一,于是 Spring Cloud Gateway 就出现了。

Spring Cloud Gateway 文档链接

快速体验

新建一个项目或模块 Spring Cloud Gateway 的依赖如下(我自己在学习时有一个父模块):

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

自己在学习时,我已经建好一个 localhost:8011 的服务!

然后在 application.yml 中添加如下配置。

server:
  port: 8888
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: order_route # 唯一标识路由
          uri: http://localhost:8011 # 微服务的路由,当然也可以其它的服务
          predicates: # 断言规则,用于路由的匹配
            - Path=/order-serv/** # http://localhost:8011/order-serv/order/add
          filters:
            - StripPrefix=1 # 转发之前去掉第一层路径

其中,配置的一些信息如下。

  • routes:配置路由信息
    • id:路由唯一标识
    • uri:服务的路径
    • predicates:路由规则,用于路由的匹配,目前有以下的断言规则,后面会一一举出这些使用
      • Path:路径匹配
      • After:指定时间之后才能访问
      • Before:指定时间之前才能访问
      • Between:指定时间之间才能访问
      • Header:指定的请求头才能访问
      • Method:指定某个请求方法才能访问
      • Query:必须携带某个参数才能访问
    • filters:过滤器
      • StripPrefix:转发之前去掉第几层的路径

尝试访问 http://localhost:8888/order-serv/order/add,可以正常地访问。

image-20221130212556083

与 nacos 的整合

引入 nacos 的依赖,同时要排除 yaml 解析器的依赖,不然运行会出现异常。

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.yaml</groupId>
            <artifactId>snakeyaml</artifactId>
        </exclusion>
    </exclusions>
</dependency>

当引入成功后,网关也是一个微服务了,此时可以通过服务名来访问了。

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: order_route # 唯一标识路由
          uri: lb://order-service # lb的意思是,使用 nacos 中的 loadbalance 负载均衡器

同时 Spring Cloud Gateway 可以启动是否识别 nacos,这样就不用上面的写法了,但是据我了解上面的写法会比较多。

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 是否启动自动识别nacos服务

路由断言工厂

路由断言默认有以下几个:

  • Path:路径匹配
  • After:指定时间之后才能访问
  • Before:指定时间之前才能访问
  • Between:指定时间之间才能访问
  • Header:指定的请求头才能访问
  • Method:指定某个请求方法才能访问
  • Query:必须携带某个参数才能访问

以上的规则可以一起使用,若全都不使用,则该服务下的所有路径都是可以访问到的。

Path

通配符

predicates:
  - Path=/order-serv/**

占位符

predicates:
  - Path=/order-serv/{path}

Before、After、Between

Between 是时间段,两个时间段之间需要用逗号隔开。不在规定的时间规则内访问,会返回 404 错误。

predicates:
  - After=2022-11-30T14:50:49.088636500+08:00[Asia/Shanghai]
  - Before=2022-11-30T14:55:00.088636500+08:00[Asia/Shanghai]
  - Between=2022-11-30T15:00:00.088636500+08:00[Asia/Shanghai], 2022-11-30T15:05:00.088636500+08:00[Asia/Shanghai]

请求头中没有该参数,会返回 404 错误。逗号后面为传递的值,可以是正则表达式,下面的例子是该参数的值必须要是长度 ≥1 的数字。

predicates:
  - Header=X-Request-Id,\d+

Method

请求方法必须为 GET 请求,如果是其他的请求(比如 POST),会返回 404 错误。

predicates:
  - Method=GET

Query

使用方法和 Header 类似,请求中没有携带该参数,会返回 404 错误。

predicates:
  - Query=name, zhangsan|lisi

自定义断言工厂

自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写apply方法的逻辑、在 apply 方法中可以通过 exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获得请求的参数、请求方式、请求头等信息。

  • 必须spring组件bean
  • 类必须加上 RoutePredicateFactory 作为结尾
  • 必须继承 AbstractRoutePredicateFactory
  • 必须声明静态内部类声明属性来接收配置文件中的信息
  • 需要结合 shortcutFieldOrder

示例

@Component
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenRoutePredicateFactory.Config> {

    public TokenRoutePredicateFactory() {
        super(TokenRoutePredicateFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("token");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return (GatewayPredicate) serverWebExchange -> {
            if(config.getToken().equals("bestguo2020")) {
                return true;
            }
            return false;
        };
    }


    @Validated
    public static class Config {

        private String token;

        public String getToken() {
            return token;
        }

        public void setToken(String token) {
            this.token = token;
        }
    }
}

application.yml

spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: order_route # 唯一标识路由
          uri: lb://order-service # lb的意思是,使用 nacos 中的负载均衡器 http://localhost:8011
          predicates: # 断言规则,用于路由的匹配
            - Token=bestguo2020 # 注意类的名字,所以在配置时自定义断言的 key 为 Token