Spring Cloud 网关之Gateway

一、概述

  • 官方文档
  • SpringCloud Gateway 是 Spring Cloud 的一个全新项目,基于 Spring 5.0+Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
  • 通过网关,可以实现反向代理、鉴权、流量控制、熔断、日志监控等。

image.png

  • 特性
    • 动态路由:能够匹配任何请求属性;
    • 可以对路由指定 Predicate(断言)和 Filter(过滤器);
    • 集成Hystrix的断路器功能;
    • 集成 Spring Cloud 服务发现功能;
    • 易于编写的 Predicate(断言)和 Filter(过滤器);
    • 请求限流功能;
    • 支持路径重写。
    • Spring Cloud Gateway 建立在 Spring Framework 5、 Project Reactor 和 Spring Boot 2 之上, 使用非阻塞 API。
    • Spring Cloud Gateway 还支持 WebSocket, 并且与Spring紧密集成拥有更好的开发体验

二、核心概念

1. 路由(Route)

  • 是构建网关的基本模块,由ID、目标URL、一系列的断言、过滤器组成,如果断言为true则匹配该路由。
  • 类比就是计算机网络的路由,根据不同的目标地址,转发到不同的地方。

2. 断言(Predicate)

  • 附属于路由里面。简单来说,就是匹配规则。可以是根据路径匹配、时间匹配、Cookie匹配、Header匹配等。如果匹配上了,那就由该路由负责转发。

3. 过滤(Filter)

  • 就是个拦截器。可以在请求前或请求后对请求进行修改或者限制。
  • 其实就是Spring框架里面的GatewayFilter的实例。

4. 工作流程

image.png
image.png

  1. 客户端向Spring Cloud Gateway发出请求。
  2. Gateway Handler Mapping找到与请求匹配的路由,发送给相应的Gateway Web Handler
  3. Gateway Web Handler通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑(Proxied Service),然后返回再经历指定的过滤器链。
  4. 然后“原路返回”。

其中,Filter再“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等。“post”类型的过滤器中可以做响应内容、响应头的修改、日志输出、流量监控等有着非常重要的作用。

三、实验、

1. 导包

  • spring-cloud-starter-gateway是最重要的包,网关的主要包。
  • spring-cloud-starter-netflix-eureka-client,需要在注册中心中注册网关服务,并且可以通过注册中心通过服务发现,实现动态路由。
<dependencies>
    <!--gateway-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!--eureka-client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--一般基础配置类-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
复制代码

2. 修改配置文件

  • 常规操作,注册进eureka中。
server:
  port: 9527

spring:
  application:
    name: cloud-gateway

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://localhost:7001/eureka
复制代码
  • 除以上内容外,还需要加上自定义的一些网关配置。routes下面可以有好多个路由,由list组成。每个路由里面包含id、匹配后的路由地址、断言、过滤器等。
server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true      #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8002          #匹配后提供服务的路由地址
         #uri: lb://CLOUD-PAYMENT-SERVICE      # 匹配后提供服务的路由地址
          predicates:
            - Path=/payment/**         # 断言,路径相匹配的进行路由
        #   - After=2021-05-23T12:25:28.017+08:00[GMT+08:00]
        #   - Before=2021-05-23T12:28:00.017+08:00[GMT+08:00]
        #   - Between=2021-05-23T12:31:00.000+08:00[GMT+08:00],2021-05-23T12:31:30.000+08:00[GMT+08:00]
        #   - Cookie=username,du   # curl http://localhost:9527/payment/get/1 --cookie "username=du"
        #   - Header=X-Request-Id,\d+   # curl http://localhost:9527/payment/get/1 -H "X-Request-Id:5"
        #   - Query=username,\d+

        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001          #匹配后提供服务的路由地址
          predicates:
            - Path=/discovery/**         # 断言,路径相匹配的进行路由

        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8002          #匹配后提供服务的路由地址
          predicates:
            - Path=/timeOut/**         # 断言,路径相匹配的进行路由
eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      #defaultZone: http://localhost:7001/eureka
      defaultZone: http://127.0.0.1:7001/eureka,http://127.0.0.1:7002/eureka  # 集群版
复制代码

3. 主启动类

  • 常规操作
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
  public static void main(String[] args) {
    SpringApplication.run(GateWayMain9527.class, args);
  }
}
复制代码

4. 测试

  • 开启网关及网关转发到的一些服务器,即可完成。
  • 浏览器发出请求http://localhost:8527/payment/get/1,网关会转发给http://localhost:8002/payment/get/1

5. 网关路由配置说明

  • 除了上面通过yaml文件配置外,还可以通过往容器中注入Bean的方式配置。
  • 比如,目前需要实现从本地转发到百度新闻的路由。
@Configuration
@SuppressWarnings("all")  // 明明可以autowired,告诉我找不到,我直接抑制你warning
public class GateWayConfig {
  @Bean
  public RouteLocator routeLocator1(RouteLocatorBuilder builder) {
    RouteLocatorBuilder.Builder routes = builder.routes();
    return routes.route("new_guonei", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
  }

  @Bean
  public RouteLocator routeLocator2(RouteLocatorBuilder builder) {
    RouteLocatorBuilder.Builder routes = builder.routes();
    return routes.route("new_guoji", r -> r.path("/guoji").uri("http://news.baidu.com/guoji")).build();
  }
}
复制代码
  • 简单来说,就是给网关加入一个路由器,在路由器里面加入匹配的路径、目标路由地址。实际上和配置文件没太大区别。

6. 动态路由说明

  • 上面的配置文件及bean的uri都是给出直接的ip地址,然后直接转发。实际上还可以给出服务名,让网关自己通过负载均衡策略,从匹配的地址中找出一个适合的地址,然后转发。
uri: lb://CLOUD-PAYMENT-SERVICE      # 匹配后提供服务的路由地址
复制代码
  • 前面给出ip地址,以http://开头,这里使用负载均衡以lb://开头

四、Predicate

  • 断言除了上面通过路径匹配之外,还可以添加很多规则。
  • Predicate对象通过RoutePredicateFactory创建,然后赋值给路由

1. After Route Predicate

  • 表示当前路由在指定时间之后,才生效
predicates: 
    - After=2021-05-23T12:25:28.017+08:00[GMT+08:00]
复制代码

这种格式的日期格式,可以通过java代码生成

public class Now {
  public static void main(String[] args) {
    ZonedDateTime now = ZonedDateTime.now();
    System.out.println(now);
  }
}
复制代码

2. Before Route Predicate

  • 表示当前路由在指定时间之前有效,否则就无效。
predicates: 
    - Before=2021-05-23T12:25:28.017+08:00[GMT+08:00]
复制代码

3. Between Route Predicate

  • 表示在某个时间段之内有效
predicates:
    - Between=2021-05-23T12:31:00.000+08:00[GMT+08:00],2021-05-23T12:31:30.000+08:00[GMT+08:00]
复制代码

4. Cookie Route Predicate

  • 表示请求需要带着指定Cookie并且值匹配指定的正则表达式
predicates:
    - Cookie=username,du   # curl http://localhost:9527/payment/get/1 --cookie "username=du"
复制代码
  • 可以通过cmd进行测试
curl http://localhost:9527/payment/get/1 --cookie "username=du"
复制代码

5. Header Route Predicate

  • 表示请求头必须带上指定的Head和值
predicates:
    - Header=X-Request-Id,\d+   # curl http://localhost:9527/payment/get/1 -H "X-Request-Id:5"
复制代码

6. Host Route Predicate

  • 匹配指定的主机地址
predicates:
    - Host=**.atguigu.com
复制代码

7. Method Route Predicate

  • 匹配指定的方法,GET、POST、PUT、DELETE等
predicates:
    - Method=GET
复制代码

8. Path Route Predicate

  • 匹配指定的请求路径
predicates:
    - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
复制代码

9. Query Route Predicate

  • 匹配相应的请求参数及值
predicates:
    - Query=username,\d+ # curl http://localhost:9527?username=5
复制代码

五、Filter

  • 路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
  • 自带了很多过滤器,基于生命周期可以分为pre和post,种类可以分为GatewayFilter、GlobalFilter。详见官网

image.png
image.png

  • 在配置文件中,可以在路由下添加过滤器
filters: 
    - AddRequestParameter=X-Request-Id,1024 #过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Id值为1024
复制代码
  • 也可以实现GlobalFilter,Ordered,自定义过滤器。检查请求参数是否为空,为空则拦截。
@Component
@Slf4j
public class MyFilter implements GlobalFilter, Ordered {
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    log.info("执行自定义过滤器");
    String username = exchange.getRequest().getQueryParams().getFirst("username");
    // 由于没有包含用户名,直接过滤掉
    if (username == null) {
      log.info("用户名为null,无法登陆");
      exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
      return exchange.getResponse().setComplete();
    }
    return chain.filter(exchange);
  }

  @Override
  public int getOrder() {
    return 0;
  }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享