服务网关-GateWay-微服务核心组件【分布式微服务笔记05】

服务网关-GateWay

引出GateWay

当我们后端的服务部署在不同的ip和端口上,存在一些问题:

  1. 前端项目需要维护不同的后端服务ip/访问接口,非常麻烦
  2. 如果调用的是后端的集群服务,存在负载均衡问题
  3. 没有断言以及过滤机制
  4. 因此我们需要网关服务

引入GateWay后项目

  1. 网关服务对外提供统一的调用接口,根据不用的请求url,转发到对应的后端服务【需要配置】
  2. 实现负载均衡
  3. 限流
  4. 熔断降级
  5. 鉴权

GateWay网络拓扑图

GateWay基本介绍

  1. Gateway 是在Spring 生态系统之上构建的API 网关服务,基于Spring ,Spring Boot 和Project Reactor 等技术。
  2. Gateway 旨在提供一种简单而有效的方式来对API 进行路由,以及提供一些强大的过滤器功能,例如∶熔断、限流、重试等。

GateWay核心功能

  1. 鉴权
  2. 流量控制
  3. 熔断
  4. 日志监控
  5. 反向代理

Gateway 和Zuul 区别

  1. SpringCloud Gateway 作为Spring Cloud 生态系统中的网关,目标是替代Zuul
  2. SpringCloud Gateway 是基于Spring WebFlux 框架实现
  3. Spring WebFlux 框架底层则使用了高性能的Reactor 模式通信框架Netty提升了网关性能

Gateway 特性

Spring Cloud Gateway 基于Spring Framework(支持Spring WebFlux),Project Reactor 和 Spring Boot 进行构建,具有如下特性:

  1. 动态路由
  2. 可以对路由指定Predicate(断言)和Filter(过滤器)
  3. 集成Hystrix的断路器功能
  4. 集成Spring Cloud 服务发现功能
  5. 请求限流功能
  6. 支持路径重写

Gateway 基本原理

Route(路由)

路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true 则匹配该路由

Predicate(断言)
  1. 对HTTP 请求中的所有内容(例如请求头或请求参数)进行匹配,如果请求与断言相匹配则进行路由,如果Http 请求的路径不匹配, 则不进行路由转发.
Filter(过滤)
  1. 使用过滤器,可以在请求被路由前或者之后对请求进行处理【在对Http 请求断言匹配成功后, 可以通过网关的过滤机制, 对Http 请求处理】

GateWay工作机制

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

GateWay代码实现

需要将服务消费方升级成服务网关

  1. 引入gateway-starter网关场景启动器

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    
  2. Gateway路由配置两种【application.yml和配置类】

    1. 配置类

      //配置类-配置路由
      @Configuration
      public class GateWayRoutesConfig {
      
          /*
              Function<PredicateSpec, Route.AsyncBuilder> fn
              :函数式接口,接收的类型是PredicateSpec,返回的类型是 Route.AsyncBuilder
           r -> r.path("/member/get/**")
                      .uri("http://localhost:10001") 是Lambda表达式
           */
          @Bean
          public RouteLocator myRouteLocator04(RouteLocatorBuilder routeLocatorBuilder){
              RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
              /*
              route 源码:
              public Builder route(String id, Function<PredicateSpec, Route.AsyncBuilder> fn) {
                  Route.AsyncBuilder routeBuilder = (Route.AsyncBuilder)fn.apply((new RouteSpec(this)).id(id));
                  this.add(routeBuilder);
                  return this;
              }
               */
              return routes.route("member_route04",r -> r.path("/member/get/**")
                      .uri("http://localhost:10001"))
                      .build();
          }
      }
      
    2. application.yml【推荐】

      spring:
        application:
          name: e-commerce-gateway-20000 #配置应用的名称
        cloud:
          gateway:
            discovery:
              locator:
                enabled: true #启用 DiscoveryClient 服务发现
            #配置路由,可以配置多个路由 放在List<RouteDefinition> routes 中
            routes:
              - id: member_route01 #路由的ID
      #          uri: http://localhost:10001 #路由目标接口的ip 和 端口
                uri: lb://member-service-provider #lb:// 是协议名【Load Balance负载均衡的协议名】 member-service-provider 是服务名小写
                predicates: #断言,可以多种形式
                  - Path=/member/get/**
              - id: member_route02 #路由的ID
      #          uri: http://localhost:10001 #路由目标接口的ip 和 端口
                uri: lb://member-service-provider #lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
                predicates: #断言,可以多种形式
                  - Path=/member/save
      

更改GateWay负载均衡算法

只需要添加配置类即可【默认的轮询算法足够应对大多业务】

//配置自己的负载均衡算法
@Configuration
public class RibbonRule {

//    配置注入负载均衡算法
    @Bean
    public IRule myRibbonRule(){
        return new RandomRule();//new 的就是负载均衡算法 ,自己选择,这里是随机算法
    }
}

Predicate-断言

Predicate 就是一组匹配规则,当请求匹配成功,就执行对应的Route;当匹配失败,放弃处理/转发

Route Predicate factory - 路由断言工厂

  1. Spring Cloud Gateway包括许多内置的Route Predicate工厂, 所有这些Predicate都与HTTP请求的不同属性匹配【可以组合使用】,来进行不同的断言.【比如说Cookie,消息头,时间,路径......】
  2. Spring Cloud Gateway 创建Route 对象时,使用RoutePredicateFactory 创建Predicate对象,Predicate 对象可以赋值给Route。
  3. 所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合。【谓词就是匹配的标准】
After Router Predicate-根据时间之后匹配

例如 只有2024-7-26 11:11:11 之后的请求才进行匹配/转发, 不满足该条件的,不处理

spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
        #lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
          uri: lb://member-service-provider 
          predicates: #断言,可以多种形式
            - Path=/member/get/**
            - After=2024-07-26T11:11:11.000+08:00[Asia/Shanghai] #需要请求满足这个时间之后
Before Route Predicate-根据时间之前匹配

例如 只有2024-7-26 11:11:11 之前的请求才进行匹配/转发, 不满足该条件的,不处理

spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
        #lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
          uri: lb://member-service-provider 
          predicates: #断言,可以多种形式
            - Path=/member/get/**
            - Before=2024-07-26T11:11:11.000+08:00[Asia/Shanghai] #需要请求满足这个时间之前
Between Route Predicate-根据时间之间匹配

例如 只有2024-7-26 11:11:11 和 2024-7-29 11:11:11 之间的请求才进行匹配/转发, 不满足该条件的,不处理

spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
        #lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
          uri: lb://member-service-provider 
          predicates: #断言,可以多种形式
            - Path=/member/get/**
            - Between=2024-07-26T11:11:11.000+08:00[Asia/Shanghai],2024-07-29T11:11:11.000+08:00[Asia/Shanghai] #需要请求满足这个时间之间            

请求带有Cookie,并且键值对有一定要求【值 也可以是一个正则表达式】

spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
        #lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
          uri: lb://member-service-provider 
          predicates: #断言,可以多种形式
            - Path=/member/get/**
            - Cookie=name,zy #键:name    值: zy 【值 也可以是一个正则表达式】
Header Route Predicate-根据请求头匹配

请求头Header 有Authorization, 并且值admin才匹配/断言成功

spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
			#lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
          uri: lb://member-service-provider 
          predicates: #断言,可以多种形式
            - Path=/member/get/**
            - Header=Authorization,admin #Authorization是请求头中的一个属性,【值 也可以是一个正则表达式】
Host Route Predicate-根据主机名匹配

请求Host 满足一定要求 才匹配/断言成功

spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
			#lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
          uri: lb://member-service-provider 
          predicates: #断言,可以多种形式
            - Path=/member/get/**
            - Host=**.zy.**,**.zy88.** #【可以有多个,用逗号间隔】
Method Route Predicate-根据请求方式匹配

请求方式满足一定要求【post/get/delete......】 才匹配/断言成功

spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
			#lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
          uri: lb://member-service-provider 
          predicates: #断言,可以多种形式
            - Path=/member/get/**
            - Method=POST,GET #【可以有多个,用逗号间隔】
Path Route Predicate-根据路径匹配

路径满足一定要求 才匹配/断言成功

spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
			#lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
          uri: lb://member-service-provider 
          predicates: #断言,可以多种形式
            - Path=/member/get/**,/member/save #【可以有多个路径】
Query Route Predicate-根据请求参数匹配
spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
		#lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
          uri: lb://member-service-provider 
          predicates: #断言,可以多种形式
            - Path=/member/get/** #【可以有多个路径】
            - Query=email, [\w-]+@([a-zA-Z]+\.)+[a-zA-Z]+ #【可以是正则表达式】
RemoteAddr Route Predicate-根据远程地址匹配

调用端远程地址是否满足 来判断

spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
 		#lb: 是协议名【Load Balance负载均衡的协议名】 member-service-provider :服务名小写
          uri: lb://member-service-provider
          predicates: #断言,可以多种形式
            - Path=/member/get/** #【可以有多个路径】
            - RemoteAddr=127.0.0.1  #调用端的远程地址

Filter-过滤器

  1. 路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应
  2. Spring Cloud Gateway 内置了多种路由过滤器,他们都由GatewayFilter的工厂类来产生
GatewayFilter

GatewayFilter是GatewayFilter的工厂类来产生内置Filter,有31种

spring:
  application:
    name: e-commerce-gateway-20000 #配置应用的名称
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #启用 DiscoveryClient 服务发现
      routes:
        - id: member_route01 #路由的ID
          uri: lb://member-service-provider 
          predicates: #断言,可以多种形式
            - Path=/member/get/** #【可以有多个路径】
          filters:
            - AddRequestParameter=role, admin #过滤器可以有多个
            - AddRequestParameter=address, beijing 
自定义过滤器GlobalFilter

开发直接使用GatewayFilter 较少,一般使用自定义过滤器

@Component
public class CustomGateWayFilter implements GlobalFilter, Ordered {

    //filter 方法写的就是过滤业务
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        //获取对应的参数值
        //getFirst 请求的是键username获取到的值的集合的第一个
        String username = exchange.getRequest().getQueryParams().getFirst("username");
        String pwd = exchange.getRequest().getQueryParams().getFirst("pwd");
        if (!("zy".equals(username) && "123456".equals(pwd))){
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);//设置状态码
            return exchange.getResponse().setComplete();
        }
        //放行
        return chain.filter(exchange);
    }

    //order 表示过滤器执行的顺序,数字越小,优先级越高
    @Override
    public int getOrder() {
        return 0; //指定优先级
    }
}