Spring Cloud 核心组件 Gateway

  • 作者:DecaMinCow

Gateway 能解决什么问题

Spring Cloud Gateway 天生就是异步非阻塞的,基于 Reactor 模型

一个请求—>网关根据一定的条件匹配—匹配成功之后可以将请求转发到指定的服务地址;而在这个过程中,我们可以进行一些比较具体的控制(通过路由、断言、过滤进行参数校验、权限校验、流量监控、日志输出、协议转换等操作)

Gateway路由规则详解

时间点后匹配

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: https://example.org
          predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]

时间点前匹配

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

时间区间匹配

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-
21T17:42:47.789-07:00[America/Denver]

指定Cookie正则匹配指定值

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

指定Header正则匹配指定值

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

请求Host匹配指定值

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

请求Method匹配指定请求⽅式

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

请求路径正则匹配

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

请求包含某参数

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green

请求包含某参数并且参数值匹配正则表达式

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=red, gree.

远程地址匹配

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

Gateway动态路由规则

动态路由设置时,uri以 lb: //开头(lb代表从注册中心获取服务),后面是需要转发到的服务名称

server:
  port: 9002

eureka:
  client:
    serviceUrl: # eureka server的路径
      defaultZone: http://EurekaServerA:8761/eureka/,http://EurekaServerB:8762/eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server 可以同步注册表
    # 每隔多久拉取一次服务列表
    registry-fetch-interval-seconds: 30
  instance:
    #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip)
    prefer-ip-address: true #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@

spring:
  application:
  name: cloud-gateway
  cloud:
    gateway:
      routes: # 路由可以有多个
        - id: service-autodeliver-router # 我们自定义的路由 ID,保持唯一
          uri: lb://autodeliver # 微服务(部署多实例) 动态路由:uri配置的应该是一个服务名称,而不应该是一个具体的服务实例的地址
          predicates: # 断言:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默 认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
          - Path=/autodeliver/**
        - id: service-resume-router # 我们自定义的路由 ID,保持唯一
          uri: lb://resume
          predicates:
          - Path=/resume/**
#          filters:
#            - StripPrefix=1

自定义全局过滤器

/**
 * 定义全局过滤器,会对所有路由⽣效
 */
@Slf4j
@Component  // 让容器扫描到,等同于注册了
public class BlackListFilter implements GlobalFilter, Ordered {
    // 模拟⿊名单(实际可以去数据库或者redis中查询)
    private static List<String> blackList = new ArrayList<>();
    static {
        blackList.add("0:0:0:0:0:0:0:1");  // 模拟本机地址
    }
    /**
     * 过滤器核⼼⽅法
     * @param exchange 封装了request和response对象的上下⽂
     * @param chain ⽹关过滤器链(包含全局过滤器和单路由过滤器)
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain
            chain) {
        // 思路:获取客户端ip,判断是否在⿊名单中,在的话就拒绝访问,不在的话就放⾏
        // 从上下⽂中取出request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        // 从request对象中获取客户端ip
        String clientIp = request.getRemoteAddress().getHostString();
        // 拿着clientIp去⿊名单中查询,存在的话就决绝访问
        if(blackList.contains(clientIp)) {
            // 决绝访问,返回
            response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码
            log.debug("=====>IP:" + clientIp + " 在⿊名单中,将被拒绝访问!");
            String data = "Request be denied!";
            DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }
        // 合法请求,放⾏,执⾏后续的过滤器
        return chain.filter(exchange);
    }
    /**
     * 返回值表示当前过滤器的顺序(优先级),数值越⼩,优先级越⾼
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}