API网关 Gateway

文章目录

  • Gateway网关
    • 入门使用
    • 负载均衡设计
      • 为什么负载均衡?
      • Gateway中负载均衡实现?
    • 断言(Predicate)增强分析
      • **Predicate 简介**
      • Predicate 内置工厂
      • Predicate 应用案例实践
    • 过滤器(Filter)增强分析
      • 局部过滤器设计及实现
      • 全局过滤器设计及实现
      • 过滤器顺序
      • 跨域问题
    • 限流设计及实现
      • 限流简述
      • 限流快速入门
      • 基于请求属性限流
      • 自定义API维度限流
      • 限流算法


本博客是基于观看b站里黑马程序员Java微服务架构视频做的一个学习记录,存有不足的地方,敬请见谅。

Gateway网关

  • API网关是随着微服务(Microservice)概念兴起的一种架构模式,它是运行于外部请求与内部服务之间的一个流量入口,用于实现对外部请求的协议转换、鉴权、流控、参数校验、监控等通用功能。
  • Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
  • 网关,就是服务访问(流量)的一个入口,底层是通过Netty网络编程框ServerSocket 实现的
  • Gateway 网关是我们服务的守门神,所有微服务的统一入口

网关的核心功能特性:

  • 请求路由
  • 权限控制
  • 限流
    API网关 Gateway_第1张图片

权限控制:网关作为微服务入口,需要校验用户是是否有请求资格,如果没有则进行拦截。

路由和负载均衡:一切请求都必须先经过 gateway,但网关不处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由。当然路由的目标服务有多个时,还需要做负载均衡。

限流:当请求流量过高时,在网关中按照下流的微服务能够接受的速度来放行请求,避免服务压力过大。

在 SpringCloud 中网关的实现包括两种:

  • gateway
  • zuul

Zuul 是基于 Servlet 实现,属于阻塞式编程。而 Spring Cloud Gateway 则是基于 Spring5 中提供的WebFlux,属于响应式编程的实现,具备更好的性能。

网关的作用:

  • 对用户请求做身份认证、权限校验
  • 将用户请求路由到微服务,并实现负载均衡
  • 对用户请求做限流

入门使用

  1. 创建 SpringBoot 工程 gateway,引入网关依赖
  2. 编写启动类
  3. 编写基础配置和路由规则
  4. 启动网关服务进行测试

引入SpringCloudGateway的依赖和nacos的服务发现依赖:


<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-gatewayartifactId>
dependency>

<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>

创建 application.yml 文件,编写路由配置及nacos地址,内容如下:

server:
  port: 10010 # 网关端口
spring:
  application:
    name: gateway # 服务名称
  cloud:
    nacos:
      server-addr: localhost:8848 # nacos地址
    gateway:
      routes: # 网关路由配置
        - id: user-service # 路由id,自定义,只要唯一即可
          # uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
          uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
          predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
            - Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
#          filters: ##网关过滤器,用于对谓词中的内容进行判断分析以及处理
#           	- StripPrefix=1 #转发之前去掉path中第一层路径,例如nacos

我们将符合Path 规则的一切请求,都代理到 uri参数指定的地址。

上面的例子中,我们将 /user/** 开头的请求,代理到 lb://userservice,其中 lb负载均衡(LoadBalance),根据服务名拉取服务列表,实现负载均衡

重启网关,访问 http://localhost:10010/user/1 时,符合 /user/** 规则,请求转发到 uri:http://userservice/user/1
API网关 Gateway_第2张图片

其中:路由(Route) 是 gateway 中最基本的组件之一,表示一个具体的路由信息载体。主要定义了下面的几个信息:

  • id,路由标识符,区别于其他 Route。
  • uri,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
  • predicate,断言(谓词)的作用是进行条件判断,只有断言都返回真,才会执行路由。
  • filter,过滤器用于修改请求和响应信息。
    第三步:创建项目启动类,例如:
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class,args);
    }
}

负载均衡设计

为什么负载均衡?

网关才是服务访问的入口,所有服务都会在网关层面进行底层映射,所以在访问服务时,要基于服务serivce id(服务名)去查找对应的服务,让请求从网关层进行均衡转发,以平衡服务实例的处理能力。

Gateway中负载均衡实现?

第一步:项目中添加服务发现依赖

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
  <groupId>com.alibaba.cloudgroupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>

第二步:修改其配置文件

server:
  port: 9000
spring:
  application:
    name: sca-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true  #开启通过服务注册中心的serviceId创建路由
      routes:
        - id: route01
          ##uri: http://localhost:8081/
          uri: lb://sca-provider # lb为服务前缀(负载均衡单词的缩写),不能随意写
          predicates: ###匹配规则
              - Path=/nacos/provider/echo/**
          filters:
              - StripPrefix=1 #转发之前去掉path中第一层路径,例如nacos

其中,lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略
打开gateway日志,代码如下:

logging:
  level:
    org.springframework.cloud.gateway: debug   

第三步:启动服务,进行访问测试,并反复刷新分析
执行流程分析
根据官方的说明,其Gateway具体工作流程,如图所示:
API网关 Gateway_第3张图片

客户端向Spring Cloud Gateway发出请求。 如果Gateway Handler Mapping 通过谓词predicates(predicates)的集合确定请求与路由(Routers)匹配,则将其发送到Gateway Web Handler。
Gateway Web Handler 基于路由配置调用过滤链中的过滤器(也就是所谓的责任链模式)进一步的处理请求。 Filter由虚线分隔的原因是, Filter可以在发送请求之前和之后执行拓展逻辑。

断言(Predicate)增强分析

Predicate 简介

Predicate(断言)又称谓词,用于条件判断,只有断言结果都为真,才会真正的执行路由。断言其本质就是定义路由转发的条件。

再者通常,我们在配置文件中写的断言规则只是字符串,这些字符串会被 Predicate Factory 读取并处理,转变为路由判断的条件
例如 Path=/user/** 是按照路径匹配,这个规则是由
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory 类来处理的,像这样的断言工厂在 Spring Cloud Gateway 还有十几个
API网关 Gateway_第4张图片

Predicate 内置工厂

SpringCloud Gateway包括一些内置的谓词工厂(所有工厂都直接或间接的实现了RoutePredicateFactory接口),这些断言或谓词工厂负责创建谓词对象,并通过这些谓词对象判断http请求的合法性。常见谓词工厂如下:
基于Datetime类型的断言工厂
此类型的断言根据时间做判断,主要有三个:
1) AfterRoutePredicateFactory:判断请求日期是否晚于指定日期
2) BeforeRoutePredicateFactory:判断请求日期是否早于指定日期
3) BetweenRoutePredicateFactory:判断请求日期是否在指定时间段内

-After=2020-12-31T23:59:59.789+08:00[Asia/Shanghai]

当且仅当请求时的时间After配置的时间时,才转发该请求,若请求时的时间不是After配置的时间时,则会返回404 not found。时间值可通过ZonedDateTime.now()获取。

基于header的断言工厂HeaderRoutePredicateFactory

判断请求Header是否具有给定名称且值与正则表达式匹配。例如:

-Header=X-Request-Id, \d+

基于Method请求方法的断言工厂,MethodRoutePredicateFactory接收一个参数,判断请求类型是否跟指定的类型匹配。例如:

-Method=GET

基于Query请求参数的断言工厂,QueryRoutePredicateFactory

接收两个参数,请求param和正则表达式, 判断请求参数是否具 有给定名称且值与正则表达式匹配。例如:

-Query=pageSize,\d+

Predicate 应用案例实践

内置的路由断言工厂应用案例,例如:

server:
  port: 9000
spring:
  application:
    name: sca-gateway
  cloud:
    nacos:
      server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true #开启通过服务中心的serviceId 创建路由的功能
      routes:
        - id: bd-id
          ##uri: http://localhost:8081/
          uri: lb://sca-provider
          predicates: ###匹配规则
              - Path=/nacos/provider/echo/**
              - Before=2021-01-30T00:00:00.000+08:00
              - Method=GET
          filters:
            -  StripPrefix=1 # 转发之前去掉1层路径

当条件不满足时,则无法进行路由转发,会出现404异常。

过滤器(Filter)增强分析

  • 过滤器(Filter)就是在请求传递过程中,对请求和响应做一个处理Gateway 的Filter从作用范围可分为两种:GatewayFilterGlobalFilter。其中:
    • GatewayFilter:应用到单个路由或者一个分组的路由上
    • GlobalFilter:应用到所有的路由上(例如负载均衡过滤器,请求转发过滤器等)。
      API网关 Gateway_第5张图片

局部过滤器设计及实现

Spring提供了31种不同的路由过滤器工厂。
在SpringCloud Gateway中内置了很多不同类型的网关路由过滤器。具体如下:

基于AddRequestHeader GatewayFilterFactory,为原始请求添加Header。

例如,为原始请求添加名为 X-Request-Foo ,值为 Bar 的请求头:

spring:
  cloud:
    gateway:
      routes:
        - id: add_request_header_route
          uri: https://example.org
          filters:
            - AddRequestHeader=X-Request-Foo, Bar

基于AddRequestParameter GatewayFilterFactory,为原始请求添加请求参数及值

例如,为原始请求添加名为foo,值为bar的参数,即:foo=bar。

spring:
  cloud:
    gateway:
      routes:
        - id: add_request_parameter_route
          uri: https://example.org
          filters:
            - AddRequestParameter=foo, bar

基于PrefixPath GatewayFilterFactory,为原始的请求路径添加一个前缀路径

例如,该配置使访问${GATEWAY_URL}/hello 会转发到uri/mypath/hello。

spring:
  cloud:
    gateway:
      routes:
        - id: prefixpath_route
          uri: https://example.org
          filters:
            - PrefixPath=/mypath

基于RequestSize GatewayFilterFactory,设置允许接收最大请求包的大小

配置示例

spring:
  cloud:
    gateway:
      routes:
        - id: request_size_route
      uri: http://localhost:8080/upload
      predicates:
        - Path=/upload
      filters:
        - name: RequestSize
          args:
            # 单位为字节
            maxSize: 5000000

如果请求包大小超过设置的值,则会返回 413 Payload Too Large以及一个errorMessage
在列出几个可能会使用到的过滤器工厂:
API网关 Gateway_第6张图片

Gateway 也是有全局过滤器的,如果要对所有的路由都生效,则可以将过滤器工厂写到 default-filters 下:

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=sign, xn2001.com is eternal # 添加请求头

全局过滤器设计及实现

全局过滤器(GlobalFilter)作用于所有路由, 无需配置。在系统初始化时加载,并作用在每个路由上。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。一般内置的全局过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们 自己编写过滤器来实现的,那么我们一起通过代码的形式自定义一个过滤器,去完成统一的权限校验。 例如,当客户端第一次请求服务时,服务端对用户进行信息认证(登录), 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证 以后每次请求,客户端都携带认证的token 服务端对token进行解密,判断是否有效。

全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与 GatewayFilter 的作用一样。区别在于 GlobalFilter 的逻辑可以写代码来自定义规则;而 GatewayFilter 通过配置定义,处理逻辑是固定的。

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    // 获取username 参数
        String username=exchange.getRequest()
                .getQueryParams().getFirst("username");
        if (!"admin".equals(username)) {
            System.out.println("认证失败");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
             // 设置拦截
            return exchange.getResponse().setComplete();
        }
        //调用chain.filter继续向下游执行
        return chain.filter(exchange);
    }
    // 设置过滤器优先级,值越低优先级越高
    // 也可以使用 @Order 注解
    @Override
    public int getOrder() {
        return 0;
    }
}

启动Gateway服务,假如在访问的url中不带“user=admin”这个参数,可能会出现异常.

  • 网关过滤器的作用是:对请求和响应数据做一个预处理
  • 网关过滤器的类型有 局部过滤器全局过滤器
  • 局部过滤器针对具体链路的应用的过滤器,需要进行配置
  • 全局过滤器作用于所有请求链路
  • 自定义全局过滤器直接或间接实现GlobalFilter接口

过滤器顺序

请求进入网关会碰到三类过滤器:DefaultFilter当前路由的过滤器GlobalFilter

请求路由后,会将三者合并到一个过滤器链(集合)中,排序后依次执行每个过滤器.
API网关 Gateway_第7张图片
排序的规则是什么呢?

  • 每一个过滤器都必须指定一个 int 类型的 order 值,order 值越小,优先级越高,执行顺序越靠前。
  • GlobalFilter 通过实现 Ordered 接口,或者使用 @Order 注解来指定 order 值,由我们自己指定。
  • 路由过滤器defaultFilterorderSpring 指定,默认是按照声明顺序从1递增。
  • 过滤器order 值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter 的顺序执行。

例子

org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()方法是先加载defaultFilters,然后再加载某个route的filters,然后合并。

org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链

跨域问题

跨域:域名不一致就是跨域,主要包括:

  • 域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
  • 域名相同,端口不同:localhost:8080和localhost8081

跨域问题:浏览器禁止请求的发起者与服务端发生跨域ajax请求,请求被浏览器拦截的问题

解决方案:CORS

在 Gateway 网关中解决跨域问题还是比较方便的。

spring:
  cloud:
    gateway:
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求 allowedOrigins: “*” 允许所有网站
              - "http://localhost:8090"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

限流设计及实现

限流简述

网关是所有外部请求的公共入口,所以可以在网关进行限流,而且限流的方式也很多,我们采用Sentinel组件来实现网关的限流。Sentinel支持对SpringCloud Gateway、Zuul等主流网关进行限流。参考网址如下:
https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel

限流快速入门

第一步:添加依赖
在原有spring-cloud-starter-gateway依赖的基础上再添加如下两个依赖,例如:

<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-alibaba-sentinel-gatewayartifactId>
dependency>

第二步:添加sentinel及路由规则(假如已有则无需设置)

routes:
  - id: route01
    uri: lb://sca-provider
    predicates: ###匹配规则
      - Path=/provider/echo/**
sentinel:
  transport:
    dashboard: localhost:8180 #Sentinel 控制台地址
  eager: true  #取消Sentinel控制台懒加载,即项目启动即连接

第三步:启动网关项目,检测sentinel控制台的网关菜单。
启动时,添加sentinel的jvm参数,通过此菜单可以让网关服务在sentinel控制台显示不一样的菜单,代码如下。

-Dcsp.sentinel.app.type=1

Sentinel 控制台启动以后,界面如图所示:
API网关 Gateway_第8张图片

说明,假如没有发现请求链路,API管理,关闭网关项目,关闭sentinel,然后重启sentinel,重启网关项目.
第四步:在sentinel面板中设置限流策略,如图所示:

API网关 Gateway_第9张图片

第五步:通过url进行访问检测是否实现了限流操作

基于请求属性限流

定义指定routeId的基于属性的限流策略如图所示:
API网关 Gateway_第10张图片

通过postman进行测试分析API网关 Gateway_第11张图片

自定义API维度限流

自定义API分组,是一种更细粒度的限流规则定义,它允许我们利用sentinel提供的API,将请求路径进行分组,然后在组上设置限流规则即可。

第一步:新建API分组,如图所示:

API网关 Gateway_第12张图片

第二步:新建分组流控规则,如图所示API网关 Gateway_第13张图片

限流算法

常见的包括:

  • 计数器算法,又包括窗口计数器算法、滑动窗口计数器算法
  • 漏桶算法(Leaky Bucket)
  • 令牌桶算法(Token Bucket)

限流过滤器-计数器算法

固定窗口计数器算法概念如下:

  • 将时间划分为多个窗口;
  • 在每个窗口内每有一次请求就将计数器加一,当时间到达下一个窗口时,计数器重置。
  • 如果计数器超过了限制数量,则本窗口内所有的请求都被丢弃。

API网关 Gateway_第14张图片

限流过滤器-漏桶算法

漏桶算法说明:

  • 将每个请求视作"水滴"放入"漏桶"进行存储;
  • "漏桶"以固定速率向外"漏"出请求来执行,如果"漏桶"空了则停止"漏水”;
  • 如果"漏桶"满了则多余的"水滴"会被直接丢弃。
    API网关 Gateway_第15张图片

限流过滤器-令牌桶算法

漏桶算法说明:

  • 以固定的速率生成令牌,存入令牌桶中,如果令牌桶满了以后,多余令牌丢弃
  • 请求进入后,必须先尝试从桶中获取令牌,获取到令牌后才可以被处理
  • 如果令牌桶中没有令牌,则请求等待或丢弃
    API网关 Gateway_第16张图片

你可能感兴趣的:(微服务架构,gateway,java,spring,cloud)