SpringCloud系列(四)服务网关

182 阅读4分钟

这是我参与更文挑战的第 14 天,活动详情查看: 更文挑战

Gateway

一、概述

Gateway(网关)主要提供了一种简单而有效的方式来对API进行路由,以及一些强大的过滤功能,如:熔断、断流、重试等。

Spring Cloud Gateway 作为Spring Cloud生态系统中的网关,目标是替代Zuul,Cloud Gateway是基于WebFlux框架实现的,而web Flux框架底层使用了高性能的Reactor模式通信框架Netty,基于异步非阻塞模型上进行开发的。

Spring Cloud Gateway提供了统一的路由方式且基于Filter链的方式提供了网关的基本功能,如:安全、监控、指标和限流。

Gateway三大概念:

  • Route(路由):是构成网关的基本模块,由id、目标url、一系列断言和过滤器组成,如果断言为true匹配该路由。
  • Predicate(断言):可以匹配Http请求中的所有内容,如果请求与断言相匹配则进行路由。
  • Filter(过滤):使用过滤器,可以在请求在路由前或后对请求进行修改。

执行流程:客户端向Gateway发送请求,如何在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。Handler再通过指定的过滤器链将请求发送到实际的服务器执行业务逻辑,如何返回。在前置过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等。后置过滤器可以做响应内容、响应头的修改。

二、搭建环境

依赖,注意:引入gateway后不可再添加web的依赖了

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

配置文件配置路由规则

server:
  port: 9527

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes: # 可以配置多个路由
        - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001   #匹配后提供服务的路由地址
          predicates:
            - Path=/payment/getById/**   #断言,路径相匹配的进行路由


        - id: payment_routh2
          uri: http://localhost:8001
          predicates:
            - Path=/payment/getPort/**

测试

访问http://localhost:9527/payment/getById/**即可访问到8001访问下的方法。

或者使用编码方式配置路由规则

/**
 * 使用配置类方法配置网关路由
 */
@SpringBootConfiguration
public class GatewayConfig {
    /**
     * 配置了一个路由名字为path_route_yylm的规则
     * 访问http://localhost:9527/guonei跳转到百度新闻国内新闻
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("path_route_yylm",r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }
}

三、动态路由

把gateway网关也注册到注册中心,然后通过注册中心中的微服务名称调用具体的服务。

配置

gateway:
  discovery:
    locator:
      enabled: true # 开启动态创建路由,使用注册中心的服务名称进行路由
  routes:
    - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
      uri: lb://CLOUD-PAYMENT-SERVICE #使用动态路由,提供服务的服务名称

启动8001和8002两个微服务,通过lb://CLOUD-PAYMENT-SERVICE即可访问到具体服务,并且可以实现负载均衡。

四、Predicate(断言)的使用

Gateway包括许多内置的Route Predicate工厂,所有Predicate都与Http请求的不同属性匹配。 Predicate就是实现一组匹配规则,让请求找到对应的Route进行匹配

时间级别的

  predicates:
    - Path=/payment/getPort/**
    - After=2020-12-04T00:14:13.512+08:00[Asia/Shanghai]  # 指定时间后生效
    # - Before=2020-12-04T00:10:13.512+08:00[Asia/Shanghai]  # 指定时间前生效

cookie级别

  predicates:
    - Path=/payment/getPort/**
    - After=2020-12-04T00:14:13.512+08:00[Asia/Shanghai]  # 指定时间后生效
    # - Before=2020-12-04T00:14:13.512+08:00[Asia/Shanghai]  # 指定时间前生效
    - Cookie=username,yylm  # 必须携带指定cookie

Cookie Route Predicates需要两个参数,一个是cookie name,一个是正则表达式,路由规则会通过获取对应的cookie name和正则表达式去匹配,匹配上执行,没有匹配上不执行。

在访问时就必须携带Cookie 值为username=yylm

还有Header/Host Route Predicates 和Cookie的差不多,需要携带指定的请求头才能访问成功。

其他级别

  • Method:- Method=GET //指定匹配的访问方法
  • Path:- Path=xxxx //指定匹配的访问路径
  • Query: -Query=username, \d+ //指定参数名要有username,并且值为整数才能路由

五、Gateway的Filter

路由过滤器可以修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用,内置了多种路由过滤器。

内置过滤器,和Predicates的使用方法相似,使用更多的是自定义过滤器。

filters:
    - AddRequestHeader=X-Request-red, blue

自定义全局过滤器

@Component
public class MyLogGatewayFilter implements GlobalFilter, Ordered {
    public static final Logger LOGGER = LoggerFactory.getLogger(MyLogGatewayFilter.class);

    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        LOGGER.info("进到MyLogGatewayFilter的filter方法:"+ new Date());
        //从请求中获取请求参数
        String name = exchange.getRequest().getQueryParams().getFirst("uname");
        //如果没有该参数就给出提示
        if (name == null) {
            LOGGER.info("非法用户");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    public int getOrder() {
        return 0;
    }
}

测试:使用http://localhost:9527/payment/getById/31?uname=lm,携带请求参数uname才可以请求进来。否则被拒绝。