SpringCloud微服务GateWay网关使用与配置

一、概念

1、什么是GateWay网关

在微服务架构中,Gateway(网关)是一个重要的组件,负责处理外部请求并将它们路由到适当的微服务。以下是Gateway在微服务中的一些主要功能:

SpringCloud微服务GateWay网关使用与配置_第1张图片

  1. 路由: Gateway负责将来自客户端的请求路由到正确的微服务。这可以基于请求的路径、主机头、HTTP方法等条件进行路由。
  2. 负载均衡: Gateway可以执行负载均衡,将请求分发到多个相同或不同的微服务实例,以确保各个实例都能够处理相应的负载,提高系统的性能和可用性。
  3. 安全性: Gateway通常用于处理安全性方面的任务,例如身份验证、授权和加密。它可以拦截请求并验证用户的身份,确保只有经过身份验证的用户才能访问受保护的微服务。
  4. 监控和日志: Gateway可以收集有关请求和响应的信息,用于监控和日志记录。这些信息可以用于分析性能问题、跟踪请求流程,以及生成有关系统行为的报告。
  5. 协议转换: Gateway可以执行协议转换,将外部请求从一个协议转换为另一个协议。例如,将HTTP请求转换为WebSocket请求,或者将请求从HTTP/1.1转换为HTTP/2。
  6. 缓存: Gateway可以实现请求和响应的缓存,以降低对微服务的负载,提高响应速度。这对于处理频繁请求相同资源的情况很有用。
  7. 限流: 通过在Gateway上实现限流策略,可以控制对微服务的请求流量,防止过多的请求导致系统过载。
  8. 断路器模式: Gateway可以实现断路器模式,用于在微服务发生故障或不可用时防止请求继续传递,从而提高系统的稳定性。
  9. API管理: Gateway可以用于集中管理和监控微服务的API。这包括API版本控制、文档生成、请求转换等。

2、gateway网关的工作流程

SpringCloud微服务GateWay网关使用与配置_第2张图片

  1. 客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
  2. 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(Pre)或之后(Post)执行业务逻辑。
  3. 在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等;
  4. 在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。

3、gateway三大核心概念

  1. Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
  2. Predicate(断言):参考的是Java8的java.util.function.Predicate,开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。
  3. Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。

二、搭建网关微服务

1、导入依赖

    
        
        
            org.springframework.cloud
            spring-cloud-starter-gateway
        
        
        
            org.springframework.cloud
            spring-cloud-starter-consul-discovery
        
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
            org.projectlombok
            lombok
        
    

2、配置文件

server:
  port: 9000
spring:
  cloud:
    consul:
      discovery:
        service-name: ${spring.application.name}
        prefer-ip-address: true
      host: 127.0.0.1
      port: 8500
    gateway:
      routes:
        - id: order
          uri: http://localhost:80
          predicates:
            - Path=/feign/pay/gateway/**

这里将网关微服务添加进了consul配置中心,并且配置了一条到http://localhost:80,请求路径为/feign/pay/gateway/**的路由路径,这里的请求服务器我们是写死的,不便于代码的维护和更改。

3、启动类

@SpringBootApplication
public class Gateway {
    public static void main(String[] args) {
        SpringApplication.run(Main9527.class,args);
    }
}

4、修改路由为通用配置

server:
  port: 9000
spring:
  cloud:
    consul:
      discovery:
        service-name: ${spring.application.name}
        prefer-ip-address: true
      host: 127.0.0.1
      port: 8500
    gateway:
      routes:
        - id: order
          uri: lb://cloud-consumer-openfeign-order
          predicates:
            - Path=/feign/pay/gateway/**

此时我们使用的不再是固定的主机,而是使用微服务名来查找对应的服务,并且使用lb(loadBalancer)实现了网关的负载均衡作用

三、predicate断言

1、断言概念

Spring Cloud Gateway 将路由匹配为 Spring WebFluxHandlerMapping基础架构的一部分。Spring Cloud Gateway 包含许多内置的路由谓词工厂。所有这些谓词都匹配 HTTP 请求的不同属性。您可以将多个路由谓词工厂与逻辑and语句结合起来。(谓词工厂也就是我们所说的断言)

在springcloud-gateway官网可以看到,谓词有十几种

SpringCloud微服务GateWay网关使用与配置_第3张图片

我们运行搭建好的gateway网关微服务,查看控制台可以看到如下信息,正好与官网的分类一一对应

SpringCloud微服务GateWay网关使用与配置_第4张图片

2、参数详情

用法示例: 如下示例当中会发现我实际上相当于设置了两个predicates(断言),path也算是一个,After又是一个,在实际开发当中,根据自己的实际场景可以随便使用断言。

  1. Path: 配置请求的路径
  2. Before Route Predicater:Before就是设置的时间之前可以访问,过了时间之后不可以访问
  3. Between Route Predicate:两个时间的区间是可以访问的,过了时间之后不可以访问
  4. Cookie Route Predicate:Cookie路由谓词工厂有两个参数,cookie和namea regexp(这是一个 Java 正则表达式)。此谓词匹配具有给定名称且其值与正则表达式匹配的 cookie。
  5. Header Route Predicater:Header路由谓词工厂有两个参数,the和headera regexp(这是一个 Java 正则表达式)。此谓词与具有给定名称且值与正则表达式匹配的标头匹配。
  6. Host Route Predicate:路由谓词工厂采用Host一个参数:主机名列表patterns。该模式是一种 Ant 风格的模式,.以分隔符为分隔符。
  7. Method Route Predicate:设置了之后只有GET请求会路由
  8. Path Route Predicate:关于path的上面示例当中我们就已经用到了。
  9. Query Route Predicate:支持传入两个参数,一个是属性名,一个为属性值,属性值可以是正则表达式。
  10. RemoteAddr Route Predicate:路由谓词工厂采用的RemoteAddr列表(最小大小为 1)sources,它们是 CIDR 表示法(IPv4 或 IPv6)字符串,例如192.168.0.1/16(其中192.168.0.1是 IP 地址和16子网掩码)。
  11. Weight Route Predicate:Weight路由谓词工厂有两个参数:和group(weight一个 int)。权重是按组计算的。
  12. XForwarded Remote Addr Route Predicate:这可以与反向代理一起使用,例如负载平衡器或 Web 应用程序防火墙,其中仅当请求来自这些反向代理使用的受信任的 IP 地址列表时才允许请求。

3、配置实例

server:
  port: 9000
spring:
  application:
    name: cloud-gateway
  cloud:
    consul:
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
      host: localhost
      port: 8500
    gateway:
      #配置路由路径映射
      routes:
         - id: pay
           uri: lb://cloud-pay
           predicates:
             - Path= /pay/gateway/*/**
             #- My=zxc
             #- After= 2024-04-03T18:44:12.217681800+08:00[Asia/Shanghai] #设置在时间后访问
             #- Method=GET #基于请求方式的断言
             #- RemoteAddr=192.168.100.1/24 #限制ip访问
             #- Query=username,\d+ 基于请求参数的断言
             #- Host=192.168.**.** #基于主机的断言,可以使用通配符
             #- Header=X-request-Id,\d+ #基于请求头的断言
             #- Cookie=username,zxc #基于请求Cookie的断言
             #- Before= 2024-04-03T18:46:12.217681800+08:00[Asia/Shanghai] #设置在时间前访问
             #- Between= 2024-04-03T18:46:12.217681800+08:00[Asia/Shanghai], 2024-04-03T18:49:12.217681800+08:00[Asia/Shanghai] #设置在时间段内前访问

4、自定义断言

需求:自定义用户等级userA\userB\userC,通过断言适配其是否拥有访问权限

1、代码

@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory
{
    public MyRoutePredicateFactory()
    {
        super(MyRoutePredicateFactory.Config.class);
    }

    //配置文件短促写法
    @Override
    public List shortcutFieldOrder() {
        return Arrays.asList("userType");
    }

    @Validated
    public static class Config{
        @Setter
        @Getter
        @NotEmpty
        private String userType; //钻、金、银等用户等级
    }

    @Override
    public Predicate apply(MyRoutePredicateFactory.Config config)
    {
        return new Predicate()
        {
            @Override
            public boolean test(ServerWebExchange serverWebExchange)
            {
                //检查request的参数里面,userType是否为指定的值,符合配置就通过
                String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");

                if (userType == null) return false;

                //如果说参数存在,就和config的数据进行比较
                if(userType.equals(config.getUserType())) {
                    return true;
                }

                return false;
            }
        };
    }
}
2、配置
server:
  port: 9000
spring:
  cloud:
    consul:
      discovery:
        service-name: ${spring.application.name}
        prefer-ip-address: true
      host: 127.0.0.1
      port: 8500
    gateway:
      routes:
        - id: pay_gateway
          uri: lb://cloud-payment-service
          predicates:
            - Path=/pay/gateway/info/**
#            - name: My #未从写shortcutFieldOrder方法则只能使用全配置
#              args:
#                userType: auser
             - My=auser #重写shortcutFieldOrder方法则能使用短配置

3、测试

请求对应服务,分别尝试访问时不添加参数和添加参数userType=userA

四、过滤器

过滤器可以在执行方法前和执行方法后进行过滤,所谓的过滤就是可以在请求上加一些操作,例如匹配到路由后可以在请求上添加个请求头,或者参数等等。

Gateway过滤器分为了两种:路由过滤器 和 全局过滤器:

1、路由过滤器:路由过滤器针对于某一个路由进行使用,其中官网给我们提供了30多种类型的路由过滤器。
2、全局过滤器:全局的往往是我们经常会用到的,他和路由过滤器区别就是,他是针对于所有路由进行设置的过滤规则,实际开发当中很少会针对于某一个路由使用Filter,大部分都会采用全局过滤器。

1、常用的路由过滤器

  1. The AddRequestHeader GatewayFilter Factory:相当于是给匹配到路由的request,添加Header
  2. The RemoveRequestHeader GatewayFilter Factory:删除request请求头信息
  3. The SetRequestHeader GatewayFilter Factory:设置request请求头信息
  4. The RequestRateLimiter GatewayFilter Factory:通过这个Filter就可以利用redis来完成限流
  5. The AddRequestParameter GatewayFilter Factory:添加request参数
  6. The RemoveRequestParameter GatewayFilter Factory:删除请求头参数
  7. The AddResponseHeader GatewayFilter Factory:添加response相应头信息
  8. The ResponseHeader GatewayFilter Factory:设置response相应头信息
  9. The RemoveResponseHeader GatewayFilter Factory:删除response相应头信息
  10. The PrefixPath GatewayFilter Factory:自动添加路径前缀
  11. The SetPath GatewayFilter Factory:修改访问路径
  12. The RedirectTo GatewayFilter Factory:重定向到某个页面

在配置时使用Default Filters相当于全局配置

2、filter配置实例

server:
  port: 9000
spring:
  application:
    name: cloud-gateway
  cloud:
    consul:
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}
      host: localhost
      port: 8500
    gateway:
      #配置路由路径映射
      routes:
         #gateway过滤器配置
         - id: cloud-pay02
           uri: lb://cloud-pay
           predicates:
             - Path= /pay/gateway/filter/**
             #添加请求路径头部
             #- Path= /abc/pay/gateway/filter/**
             #修改请求路径
             #- Path= /abc/abc/{segment}
             #删除请求路径头部
             #- Path= /gateway/filter/**
           filters:
             #出现错误重定向(302临时重定向)
             #- RedirectTo=302,http://www.baidu.com/

             #修改请求路径
             #- SetPath=/pay/gateway/{segment}

             #添加请求路径前部
             #- PrefixPath=/pay

             #添加请求头信息
             - AddRequestHeader=X-Request-zxc1,zxc1
             - AddRequestHeader=X-Request-zxc2,zxc2
             #删除请求头信息
             - RemoveRequestHeader=sec-fetch-site,none
             #修改请求头信息
             - SetRequestHeader=x-request-zxc1,qh


             #添加和删除请求参数
             - AddRequestParameter=id,123
             - RemoveRequestParameter=name

             #添加删除修改响应头信息
             #- AddResponseHeader=name,zxc
             - SetResponseHeader=Date,2019
             - RemoveResponseHeader=Transfer-Encoding

3、自定义全局过滤器

需求:使用网关实现接口日志,统计接口调用时间


@Component
@Slf4j
public class MyGlobalFiter implements GlobalFilter, Ordered {
    public static final String BEGIN_VISIT_TIME = "begin_visit_time"; //开始调用时间

    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //先记录访问接口时间
        exchange.getAttributes().put(BEGIN_VISIT_TIME,System.currentTimeMillis());
        //返回统计的结果给后台
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
            if(beginVisitTime != null){
                log.info("访问接口主机:" + exchange.getRequest().getURI().getHost());
                log.info("访问接口端口:" + exchange.getRequest().getURI().getPort());
                log.info("访问接口路径:" + exchange.getRequest().getURI().getPath());
                log.info("访问接口参数:" + exchange.getRequest().getURI().getRawQuery());
                log.info("访问接口时长:" + (System.currentTimeMillis() - beginVisitTime) + "毫秒");
                log.info("===分割线======================================================================");
            }
        }));
    }

    /**
     * 数字越小优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

全局过滤器不需要配置

4、自定义条件过滤器

需求:请求头携带相关参数才能访问

@Component
@Slf4j
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory {

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

    @Override
    public GatewayFilter apply(MyGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();
                log.info("进入了自定义网关过滤器MyGatewayFilterFactory,status: " + config.getStatus());

                if(request.getQueryParams().containsKey("zxc")){
                    return chain.filter(exchange);
                }else{
                    exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }

    @Override
    public List shortcutFieldOrder() {
        return Arrays.asList("status");
    }

    //内部类
    //设定一个状态值,满足才可以访问
    public static class Config{
        @Getter
        @Setter
        private String status;
    }
}

配置文件:

server:
  port: 9000
spring:
  cloud:
    consul:
      discovery:
        service-name: ${spring.application.name}
        prefer-ip-address: true
      host: 127.0.0.1
      port: 8500
    gateway:
      routes:
        - id: order
          uri: lb://cloud-consumer-openfeign-order
          predicates:
            - Path=/feign/pay/gateway/**

          filters:
            - My=zxc

测试:

请求对应的接口,分别尝试携带key为zxc的参数,值随意,和不携带参数的请求是否可以正常访问

你可能感兴趣的:(spring,cloud,微服务,gateway,分布式)