微服务网关 Gateway

一、微服务网关概述

不同的微服务一般会有不同的网络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以维护。
在这里插入图片描述
如果让客户端直接与各个微服务通讯,可能会有很多问题:

  • 客户端会请求多个不同的服务,需要维护不同的请求地址,增加开发难度
  • 在某些场景下存在跨域请求的问题
  • 加大身份认证的难度,每个微服务需要独立认证

因此,我们需要一个微服务网关,介于客户端与服务器之间的中坚层,所有的外部请求都会先经过微服务网关。客户端只需要与网关交互,只知道一个网关地址即可,这样简化了开发还有以下优点:
1、易于监控
2、易于认证
3、减少了客户端与各个微服务之间的交互次数
在这里插入图片描述

1.1 服务网关的概念

1.1.1 什么是微服务网关

API 网关为微服务架构中的服务提供了统一的访问入口,客户端通过 API 网关访问相关服务。API 网关的定义类似于设计模式中门面模式,它相当于整个为服务架构中的门面,所有客户端的访问都通过它来进行路由及过滤。

1.1.2 作用和应用场景

网关具有的职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。当然,最主要的职责还是与“外界联系”。

1.2 常见的 API 网关实现方式

  • Kong
    基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。
    问题:只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方
    式。
  • Zuul
    Netflix开源,功能丰富,使用JAVA开发,易于二次开发;需要运行在web容器中,如Tomcat。
    问题:缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如
  • Nginx;
    Traefik
    Go语言开发;轻量易用;提供大多数的功能:服务路由,负载均衡等等;提供WebUI
    问题:二进制文件部署,二次开发难度大;UI更多的是监控,缺乏配置、管理能力;
  • Spring Cloud Gateway
    SpringCloud提供的网关服务
  • Nginx+lua实现
    使用Nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用
    问题:自注册的问题和网关本身的扩展性

二、微服务网关 Gateway

2.1 Gateway 简介

2.1.1 简介

Zuul 1.x 是一个基于阻塞 IO 的 API Gateway 以及 Servlet;直到 2018 年 5 月,Zuul 2.x(基于 Netty,也是非阻塞的,支持长连接)才发布,但 SpringCloud 暂时还没有整合计划。SpringCloud Gateway 比 Zuul 1.x 系列的性能和功能整体要好。

SpringCloud Gateway 是 SpringCloud 的一个全新项目,基于 Spring5、SpringBoot2 和 Project Reactor 等技术开发的网关,它旨在提供一种简答有效的统一的 API路由管理方式。
SpringCloud Gateway 作为 SpringCloud 生态系统中的网关,目标是代替 Zuul,在 SpringCloud 2.0 以上版本中,没有对新版本的 Zuul 2.0 以上的最新高性能版本进行集成,仍然还是使用的 Zuul 1.x 非 Reactor 模式的老版本,而为了提升网关的性能,SpringCloud Gateway 是基于 WebFlux 框架实现的,而 WebFlux 框架底层则使用了高性能的 Reactor 模式通信框架 Netty。
SpringCloud Gateway 的目标提供统一的路由方式且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标和限流。

2.1.2 核心概念

在这里插入图片描述
1、路由(route):路由是网关最基础的部分,路由信息由一个 ID、一个目的 URL、一组断言工厂和一组 Filter 组成。如果断言为真,则说明 请求 URL 和配置的路由匹配。
2、断言(predicates):Java8 中的断言函数,SpringCloud Gateway 中的断言函数输入类型是 Spring 5.0 框架中的 ServerWebExchange。SpringCloud Gateway 中的断言函数允许开发者去定义匹配来自 HttpRequest 中的任何信息,比如请求头和参数等。
3、过滤器(Filter):一个标准的 Spring WebFilter,SpringCloud Gateway 中的 Filter 分为两种类型,分别是 Gateway Filter 和 Global Filter。过滤器 Filter 可以对请求和响应进行处理。

web 请求,通过一些匹配条件,定位到真正的服务节点,并在这个转发过程的前后,进行一些精细化控制。Predicate 就是我们的匹配条件,而 Filter 就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标 URI,就可以实现一个具体的路由了。

2.2 SpringCloud Gateway 特性

  • 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0 进行构建
  • 动态路由:能够匹配任何请求属性
  • 可以对路由指定 Predicate(断言)和 Filter(过滤器)
  • 集成 Hystrix 断路器功能
  • 集成 SpringCloud 服务发现功能
  • 易于编写的 Predicate(断言)和 Filter(过滤器)
  • 请求限流功能
  • 支持路径重写

2.3 SpringCloud Gateway 与 Zuul 的区别

在 SpringCloud Finchley 正式版之前,SpringCloud 推荐的网关是 Netflix 提供的 Zuul:

  • Zuul 1.x 是一个基于阻塞 I/O 的 API Gateway
  • Zuul 1.x 基于 Servlet 2.5,使用阻塞框架,它不支持任何长连接(如 WebSocket)。Zuul 的设计模式和 Nginx 比较像,每次 I/O 操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是 Nginx 是用 C++ 实现,Zuul 用 Java 实现,而 JVM 本身会有第一次加载较慢的情况,使得 Zuul 的性能相对较差。
  • Zuul 2.x 的理念更先进,基于 Netty 非阻塞和支持长连接,但是 SpringCloud 目前还没有整合。Zuul 2.x 的性能较 Zuul 1.x 有较大提升。在性能方面,根据官方提供的基准测试,SpringCloud Gateway 的 RPS(每秒请求数)是 Zuul 的 1.6 倍。
  • SpringCloud Gateway 建立在 Spring Framework 5、Project Reactor 和 SpringBoot 2 之上,使用非阻塞 API。
  • SpringCloud Gateway 还支持 WebSocket,并且与 Spring 紧密集成,拥有更好的开发体验。

2.4 什么是 WebFlux

传统的 Web 框架,比如说:Struts2,SpringMVC 等都是基于 Servlet API 与 Servlet 容器基础之上运行的。但是,在 Servlet 3.1 之后有了异步非阻塞的支持。而 WebFlux 是一个典型非阻塞异步的框架,它的核心是基于 Reactor 的相关 API 实现的。相对于传统的 web 框架来说,它可以运行在诸如 Netty,Undertow 及支持 Servlet 3.1 的容器上。非阻塞式+函数式编程(Spring 5 必须让你使用 Java8)。

Spring WebFlux 是 Spring 5.0 引入的新的响应式框架,区别于 SpringMVC,它不需要依赖 Servlet API,它是完全异步非阻塞的,并且基于 Reactor 来实现响应式流规范。

三、Gateway 的工作流程

在这里插入图片描述

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

四、入门案例

4.1 通过配置文件方式实现网关路由

(1)创建工程导入依赖

在项目中添加新的模块 cloud-gateway-gateway9527,并导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-gateway-gateway9527</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

</project>

注意:SpringCloud Gateway 使用的 web 框架为 WebFlux,和 SpringMVC 不兼容。

(2)编写配置文件

创建 application.yml 配置文件

server:
  port: 9527

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

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
    register-with-eureka: true
    fetch-registry: true

  • id:自定义的路由 id,保持唯一
  • uri:目标服务地址
  • predicates:路由条件,Predicate 接收一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
  • filters:过滤规则

上面这段配置的意思是,配置了一个 id 为 payment_route 的路由规则,当访问网关请求地址以 payment 开头时,会自动转发到地址:http://localhost:8001。配置完成先启动 eureak7001服务,再启动 payment8001 服务,最后启动 gateway9527 服务,然后在浏览器访问进行测试,当我们访问地址:http://localhost:9527/payment/get/31时会展示如下页面:
在这里插入图片描述

4.2 通过硬编码方式实现网关路由

通过9527网关访问到外网的百度新闻网址

@Configuration
public class GatewayConfig {
	/**
     * 配置了一个 id 为 path_route 的路由规则, 
     * 当访问地址 http://localhost:9527/guonei 时
     * 会自动转发到地址: https://news.baidu.com/guonei 
     * 
     * @param routeLocatorBuilder 
     * @return 
     */
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("path_route", r->r.path("/guonei").
                uri("https://news.baidu.com/guonei")).build();

        return routes.build();
    }
}

4.3 动态路由

默认情况下 Gateway 会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。

(1)首先启动 eureka7001,payment8001 和 payment8002 三个微服务。
(2)pom.xml 文件中加入 eureka 的客户端依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

(3)修改 application.yml

spring:
  cloud:
    gateway:
      routes:
        - id: payment_route            # 路由的id,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service   # 匹配后提供服务的路由地址
          predicates:
            - Path=/payment/**     # 断言,路径相匹配的进行路由

需要注意的是 Gateway 中 uri 的协议为 lb(LoadBalance,即负载均衡),表示启用 Gateway 的负载均衡功能。
lb://serviceName,其中 serviceName 就是我们需要转发到的服务名称。

来看下完整的 application.yml 配置:

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
        - id: payment_route            # 路由的id,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service   # 匹配后提供服务的路由地址
          predicates:
            - Path=/payment/**     # 断言,路径相匹配的进行路由

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
    register-with-eureka: true
    fetch-registry: true

(4)测试
启动 gateway9527,访问 http://localhost:9527/payment/lb 以及 http://localhost:9527/cloud-payment-service/payment/lb,均可以成功。
在这里插入图片描述
在这里插入图片描述

4.4 重写转发路径

在 SpringCloud Gateway 中,路由转发是直接将匹配的路由 path 拼接到映射路径(URI)之后,那么值微服务开发中往往没有那么遍历。这就可以通过 RewritePath 机制来进行路径重写。

(1)案例改造
修改 application.yml,将匹配路径修改为 /payment-service/**

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_route            # 路由的id,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service   # 匹配后提供服务的路由地址
          predicates:
            - Path=/payment-service/**     # 断言,路径相匹配的进行路由

重新启动网关,在浏览器上访问 http://localhost:9527/payment-service/payment/lb,会抛出 404。这是由于路由转发规则默认转发到 payment8001 或者 payment8002 微服务路径上,而 payment-service 并没有对应的映射微服务。

(2)添加 RewirtePath 重写转发路径
修改 application.yml,添加重写规则。

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        - id: payment_route            # 路由的id,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service   # 匹配后提供服务的路由地址
          predicates:
            - Path=/payment-service/**     # 断言,路径相匹配的进行路由
          filters:
            - RewritePath=/payment-service/(?<segment>.*),/$\{segment}

通过 RewritePath 配置重写转发的 URL,将 /payment-service/(?<segment>.*),重写为{segment},然后转发到 payment8001 或者 payment8002 微服务上。(注意:在 yml 文档中 $ 要写成 $\)
在这里插入图片描述
在这里插入图片描述

4.5 微服务名称转发

在 application.yml 中添加以下内容:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
          lower-case-service-id: true # 微服务名称以小写形式呈现

完整的 application.yml:

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true  # 开启从注册中心动态创建路由的功能,利用微服务名进行路由
          lower-case-service-id: true # 微服务名称以小写形式呈现
      routes:
        - id: payment_route            # 路由的id,没有固定规则但要求唯一,建议配合服务名
          uri: lb://cloud-payment-service   # 匹配后提供服务的路由地址
          predicates:
            - Path=/payment/**     # 断言,路径相匹配的进行路由

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka
    register-with-eureka: true
    fetch-registry: true

经过验证,当利用微服务名进行路由时,配置的路由转发规则将会失效,即 routes 中配置的内容将失效。

五、Predicate 断言

Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。
Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合

Spring Cloud Gateway 创建 Route 对象时, 使用 RoutePredicateFactory 创建 Predicate 对象,Predicate 对象可以赋值给 Route。

所有这些 Predicate 都匹配HTTP请求的不同属性。多种 Predicate Factory 可以组合,并通过逻辑 and。

在这里插入图片描述

#路由断言之后匹配 
spring: 
  cloud: 
    gateway: 
      routes: 
      - id: after_route 
        uri: https://xxxx.com 
		#路由断言之前匹配 
		predicates: - After=xxxxx 
#路由断言之前匹配 
spring: 
  cloud: 
    gateway: 
      routes: 
      - id: before_route 
        uri: https://xxxxxx.com 
        predicates: - Before=xxxxxxx 
#路由断言之间 
spring: 
  cloud: 
    gateway: 
      routes: 
      - id: between_route 
        uri: https://xxxx.com 
        predicates: - Between=xxxx,xxxx 
#路由断言Cookie匹配,此predicate匹配给定名称(chocolate)和正则表达式(ch.p) 
spring: 
  cloud: 
    gateway: 
      routes: 
      - id: cookie_route 
        uri: https://xxxx.com 
        predicates: - Cookie=chocolate, ch.p 
 #路由断言Header匹配,header名称匹配X-Request-Id,且正则表达式匹配\d+
spring: 
  cloud: 
    gateway: 
      routes: 
      - id: header_route 
        uri: https://xxxx.com 
        predicates: - Header=X-Request-Id, \d+ 
 #路由断言匹配Host匹配,匹配下面Host主机列表,**代表可变参数 
 spring: 
   cloud: 
     gateway: 
       routes: 
       - id: host_route 
         uri: https://xxxx.com 
         predicates: - Host=**.somehost.org,**.anotherhost.org 
 #路由断言Method匹配,匹配的是请求的HTTP方法 
 spring: 
   cloud: 
     gateway: 
       routes: 
       - id: method_route 
         uri: https://xxxx.com 
         predicates: - Method=GET 
 #路由断言匹配,{segment}为可变参数 
 spring: 
   cloud: 
     gateway: 
       routes: 
       - id: host_route 
         uri: https://xxxx.com 
         predicates: - Path=/foo/{segment},/bar/{segment} 
 #路由断言Query匹配,将请求的参数param(baz)进行匹配,也可以进行regexp正则表达式匹配 (参数包含 foo,并且foo的值匹配ba.) 
 spring: 
   cloud: 
     gateway: 
       routes: 
       - id: query_route 
         uri: https://xxxx.com 
         predicates: - Query=baz 或 Query=foo,ba. 
 #路由断言RemoteAddr匹配,将匹配192.168.1.1~192.168.1.254之间的ip地址,其中24为子网掩码位 数即255.255.255.0 
 spring: 
   cloud: 
     gateway: 
       routes: 
       - id: remoteaddr_route 
         uri: https://example.org 
         predicates: - RemoteAddr=192.168.1.1/24

六、过滤器 Filter

路由过滤器可用于修改进入的HTTP请求和返回的HTTP响应,路由过滤器只能指定路由进行使用。
Spring Cloud Gateway 内置了多种路由过滤器,它们都由GatewayFilter的工厂类来产生。

6.1 过滤器基础

6.1.1 过滤器的生命周期

SpringCloud Gateway 的 Filter 的生命周期只有两个:“pre”和“post”。

  • PRE:这种过滤器在请求被路由之前调用。我们可以利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

在这里插入图片描述

6.1.2 过滤器类型

SpringCloud Gateway 的 Filter 从作用范围可分为另外两种 GatewayFilter 和 GlobalFilter。

  • GatewayFilter:应用到单个路由或者一个分组的路由上。
  • GlobalFilter:应用到所有的路由上。

6.2 局部过滤器

局部过滤器(GatewayFilter),是针对单个路由的过滤器,可以对访问的 URL 过滤,进行切面处理。在 SpringCloud Gateway 中通过 GatewayFliter 的形式内置了很多不同类型的局部过滤器。这里简单将 SpringCloud Gateway 内置的所有过滤器工厂整理成了一张表格,虽然不是很详细,但能作为速览使用。如下:

过滤器工厂作用参数
AddRequestHeader为原始请求添加 HeaderHeader 的名称及值
AddRequestParameter为原始请求添加请求参数参数名称及值
AddResponseHeader为原始响应添加 HeaderHeader 的名称及值
DedupeResponseHeader提出响应头中重复的值需要去重的Header名 称及去重策略
Hystrix为路由引入Hystrix的断路器保护HystrixCommand的名 称
FallbackHeaders为fallbackUri的请求头中添加具 体的异常信息Header的名称
PrefixPath为原始请求路径添加前缀前缀路径
PreserveHostHeader为请求添加一个 preserveHostHeader=true的属 性,路由过滤器会检查该属性以 决定是否要发送原始的Host
RequestRateLimiter用于对请求限流,限流算法为令 牌桶keyResolver、 rateLimiter、 statusCode、 denyEmptyKey、 emptyKeyStatus
RedirectTo将原始请求重定向到指定的URLhttp状态码及重定向的 url
RemoveHopByHopHeadersFilter为原始请求删除IETF组织规定的 一系列Header默认就会启用,可以通 过配置指定仅删除哪些 Header
RemoveRequestHeader为原始请求删除某个HeaderHeader名称
RemoveResponseHeader为原始响应删除某个HeaderHeader名称
RewritePath重写原始的请求路径原始路径正则表达式以 及重写后路径的正则表 达式
RewriteResponseHeader重写原始响应中的某个HeaderHeader名称,值的正 则表达式,重写后的值
SaveSession在转发请求之前,强制执行 WebSession::save操作
SecureHeaders为原始响应添加一系列起安全作 用的响应头无,支持修改这些安全 响应头的值
SetPath修改原始的请求路径修改后的路径
SetResponseHeader修改原始响应中某个Header的值Header名称,修改后 的值
SetStatus修改原始响应的状态码HTTP 状态码,可以是 数字,也可以是字符串
StripPrefix用于截断原始请求的路径使用数字表示要截断的 路径的数量
Retry针对不同的响应进行重试retries、statuses、 methods、series
RequestSize设置允许接收最大请求包的大 小。如果请求包大小超过设置的 值,则返回 413 Payload Too Large请求包大小,单位为字 节,默认值为5M
ModifyRequestBody在转发请求之前修改原始请求体 内容修改后的请求体内容
ModifyResponseBody修改原始响应体的内容修改后的响应体内容

每一个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾,这是 SpringCloud Gateway 的一个约定,例如 AddRequestHeader 对应的实现类为 AddRequestHeaderGatewayFilterFactory。对于这些过滤器的使用方式可以参考官方文档。

6.3 全局过滤器

全局过滤器(GlobalFilter)作用于所有路由,SpringCloud Gateway 定义了 GlobalFilter 接口,用户可以自定义实现自己的 Global Filter。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能,并且全局过滤器也是程序员使用比较多的过滤器。

SpringCloud Gateway 内部也是通过一系列的内置全局过滤器对整个路由转发进行处理,如下:
在这里插入图片描述

6.4 统一鉴权

内置的过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们自己编写过滤器来实现。

6.4.1 鉴权逻辑

开发中的鉴权逻辑:

  • 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
  • 认证通过,将用户信息进行加密形成 token,返回给客户端,作为登录凭证
  • 以后每次请求,客户端都携带认证的 token
  • 服务端对 token 进行解密,判断是否有效
    在这里插入图片描述
    如上图,对于验证用户是否已经登录鉴权的过程可以在网关层统一校验。校验的标准就是请求中是否携带 token 凭证以及 token 的正确性。

6.4.2 代码实现

自定义一个 GlobalFilter,校验所有请求的请求参数中是否包含”token“,如果不包含请求参数”token“,则不转发路由,否则执行正常的逻辑。

@Component
public class LoginFilter implements GlobalFilter, Ordered {
    /**
     * 执行过滤器中的业务逻辑:
     *      对请求参数中的 access-token 进行判断
     *      如果存在此参数:代表已经认证成功
     *      如果不存在此参数:认证失败
     * ServerWebExchange:相当于请求和响应的上下文
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("执行了自定义的全局过滤器");
        // 获取请求参数 access-token
        String token = exchange.getRequest().getQueryParams().getFirst("access-token");
        if(token == null){
            System.out.println("没有登录");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 指定过滤器的执行顺序,返回值越小,执行优先级越高
     * 也可以使用注解@Order
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

测试:
重新启动 gateway9527,先不带 access-token 进行访问http://localhost:9527/cloud-payment-service/payment/lb,结果如下:
在这里插入图片描述
携带参数 access-token 访问:http://localhost:9527/cloud-payment-service/payment/lb?access-token=123
在这里插入图片描述

七、网关限流算法

7.1 常见的限流算法

7.1.1 计数器

计数器限流算法是最简单的一种限流实现方式,其本质是通过维护一个单位时间内的计数器,每次请求计数器加1,当单位时间内计数器累加到大于设定的阈值,则之后的请求都被拒绝,知道单位时间已经过去,再将计数器重置为零。
在这里插入图片描述

7.1.2 漏桶算法

漏桶算法可以很好地限制容量池的大小,从而防止流量暴增。漏桶可以看做是一个带有常量服务时间的单服务器队列,如果漏桶(包括缓存)溢出,那么数据包会被丢弃。在网络中,漏桶算法可以控制端口的流量输出速率,平滑网络上的突发流量,实现流量整形,从而为网络提供一个稳定的流量。
在这里插入图片描述
为了更好地控制流量,漏桶算法需要通过两个变量进行控制,一个是桶的大小,支持流量突发增多时可以存多少的睡(burst),另一个是水桶漏洞的大小(rate)。

7.1.3 令牌桶算法

令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择等待可用的令牌,或者直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置 qps 为 100,那么限流器初始化完成一秒后,桶中就已经有 100 个令牌了,这时服务还没有完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的 100 个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值