微服务服务网关----Gateway

Posted 叶 秋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微服务服务网关----Gateway相关的知识,希望对你有一定的参考价值。

服务网关----Gateway

1、前言

上篇文章讲解了如何实现服务容错,这篇就介绍下,服务网关—Gateway。在本篇文章中,你将了解到什么是服务网关?什么是Gateway?如何实现Gateway?Gateway的核心架构有哪些?如何实现网关限流等问题。

2、网关简介

首先,在传统的架构中,没有了网关,那么作为客户端,是怎么去调用的呢?如图所示:

Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)SpringBoot项目实现商品服务器端是调用

一、微服务网关GateWay

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

1、 Gateway简介

(1) 简介

Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式,统一访问接口。

SpringCloud Gateway 作为 Spring Cloud 生态系中的网关,目标是替代 Netflix ZUUL,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。

它是基于Nttey的响应式开发模式。


上表为Spring Cloud Gateway与Zuul的性能对比,从结果可知,Spring Cloud Gateway的RPS是Zuul的1.6倍

(2) 核心概念

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

二、入门案例

1、入门案例

(1)创建工程导入依赖

在项目中添加新的模块 gateway_server ,并导入依赖



 <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>
  • 注意SpringCloud Gateway使用的web框架为webflux,和SpringMVC不兼容。引入的限流组件是hystrix。redis底层不再使用jedis,而是lettuce。

(2) 配置启动类


package cn.itbleubox.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GatewayServerApplication 
    public static void main(String[] args) 
        SpringApplication.run(GatewayServerApplication.class, args);
    


(3) 编写配置文件

创建 application.yml 配置文件

server:
  port: 8080 #服务端口
spring:
  application:
    name: api-gateway-server #指定服务名
#配置SpringCloud Gateway的路由
  cloud:
    gateway:
      routes:
        #配置路由: 路由id路由到微服务的uri,断言(判断条件)
        - id: product-service
          uri: http://127.0.0.1:9001
          predicates:
            - Path=/product/**
  • id:我们自定义的路由 ID,保持唯一
  • uri:目标服务地址
  • predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
  • filters:过滤规则,暂时没用。

(4)运行测试

运行报错

SpringCloneGateWay内部是通过netty + webflux 实现的
webflux 实现和SpringMVC存在冲突
我们将父工程的web依赖移动到需要的工程当中,

移动到



重新运行

运行成功

其他的也重新运行一下

访问:http://localhost:8080/product/1

三、路由规则

Spring Cloud Gateway 的功能很强大,前面我们只是使用了 predicates 进行了简单的条件匹配,其实Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。

在 Spring Cloud Gateway 中 Spring 利用Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。

1、动态路由(面向服务的路由)

和zuul网关类似,在SpringCloud GateWay中也支持动态路由:即自动的从注册中心中获取服务列表并访问。

(1)添加注册中心依赖

在工程的pom文件中添加注册中心的客户端依赖(这里以Eureka为例)

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

(2)配置动态路由

server:
  port: 8080 #服务端口
spring:
  application:
    name: api-gateway-server #指定服务名
#配置SpringCloud Gateway的路由
  cloud:
    gateway:
      routes:
        #配置路由: 路由id路由到微服务的uri,断言(判断条件)
        - id: product-service
          #uri: http://127.0.0.1:9001
          uri: lb://service-product   # lb://代表的是根据微服务名称从注册中心拉取服务请求
          predicates:
            - Path=/product/**
# eureka注册中心
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址注册

uri : uri以 lb: //开头(lb代表从注册中心获取服务),后面接的就是你需要转发到的服务名称

  • 运行测试

访问:http://localhost:8080/product/1

2、 过滤器

Spring Cloud Gateway除了具备请求路由功能之外,也支持对请求的过滤。通过Zuul网关类似,也是通过过滤器的形式来实现的。那么接下来我们一起来研究一下Gateway中的过滤器

(1)案例改造

1)重写转发路径

修改 application.yml ,将匹配路径改为 /product-service/**

       predicates:
            #- Path=/product/**
          - Path=/product-service/**  #将当前的请求转发到对应的  http://127.0.0.1:9001/product/1
          filters: #配置路由过滤器  http://localhost:8080/product-service/product/1 --> http://127.0.0.1:9001/product/1
          - RewritePath=/product-service/(?<segment>.*), /$\\segment  #路径重写的过滤器

2)微服务名称转发

      discovery:
        locator:
          enabled:  true #开启根据服务名称自动转发
          lower-case-service-id: true  #微服务名称以小写形式呈现

运行测试访问

访问:http://localhost:8080/service-product/product/1


访问:http://localhost:8080/service-order/order/1

(2)过滤器概述

Spring Cloud Gateway除了具备请求路由功能之外,也支持对请求的过滤。通过Zuul网关类似,也是通过过滤器的形式来实现的。那么接下来我们一起来研究一下Gateway中的过滤器

1) 过滤器基础

01)过滤器的生命周期

Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。

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

02)过滤器类型

Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter 与 GlobalFilter。

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

2) 局部过滤器

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

如下

每个过滤器工厂都对应一个实现类,
并且这些类的名称必须以 GatewayFilterFactory 结尾,
这是Spring Cloud Gateway的一个约定,

例如 AddRequestHeader 对应的实现类为

AddRequestHeaderGatewayFilterFactory 。
对于这些过滤器的使用方式可以参考官方文档

3) 全局过滤器

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

Spring Cloud Gateway内部也是通过一系列的内置全局过滤器对整个路由转发进行处理如下:

01)全局过滤器代码实现


package cn.itbleubox.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/*
自定义全局过滤器
    实现 globalfilter  , ordered 接口
 */
@Component      //将当前过滤器交给Spring管理
public class LoginFilter implements GlobalFilter, Ordered 
    /*
    执行过滤器当中的业务逻辑
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) 
        System.out.println("执行了自定义的全局过滤器");
        return chain.filter(exchange); //继续向下执行
    
    /*
    指定过滤器的执行顺序,返回值越小,执行优先级别越高
     */
    @Override
    public int getOrder() 
        return 0;
    

02)运行测试


访问:http://localhost:8080/service-order/order/1

控制台

4)统一鉴权

内置的过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们自己编写过滤器来实现的,那么我们一起通过代码的形式自定义一个过滤器,去完成统一的权限校验。

01)鉴权逻辑

开发中的鉴权逻辑:

  • 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
  • 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证
  • 以后每次请求,客户端都携带认证的token
  • 服务端对token进行解密,判断是否有效。

如上图,对于验证用户是否已经登录鉴权的过程可以在网关层统一检验。
检验的标准就是请求中是否携带token凭证以及token的正确性。

02)代码实现

继续完善上述的LoginFilter,去校验所有请求的请求参数中是否包含“token”,
如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。

package cn.itbleubox.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/*
自定义全局过滤器
    实现 globalfilter  , ordered 接口
 */
@Component      //将当前过滤器交给Spring管理
public class LoginFilter implements GlobalFilter, Ordered 
    /*
    执行过滤器当中的业务逻辑
        对请求参数中的access-token进行判断
            如果存此参数:代表以及认证成功
            如果不存在次参数:认证失败
           ServerWebExchange : 相当于请求和响应的上下文(Zuul当中的RequestContext)
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) 
        System.out.println("执行了自定义的全局过滤器");
        //1.获取请求参数access-token
        String token = exchange.getRequest().getQueryParams().getFirst("access-token");
        //2、判断是否存在
        if(token == null)
            //3、如果不存在:认证失败
            System.out.println("没有登录");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();//请求结束,--将请求拦截
        
        //4、如果存在,继续执行
        return chain.filter(exchange); //继续向下执行
    
    /*
    指定过滤器的执行顺序,返回值越小,执行优先级别越高
     */
    @Override
    public int getOrder() 
        return 0;
    

  • 自定义全局过滤器需要实现GlobalFilter和Ordered接口。
  • 在filter方法中完成过滤器的逻辑判断处理
  • 在getOrder方法指定此过滤器的优先级,返回值越大级别越低
  • ServerWebExchange 就相当于当前请求和响应的上下文,存放着重要的请求-响应属性、请求实例和响应实例等等。
    一个请求中的request,response都可以通过ServerWebExchange 获取
  • 调用 chain.filter 继续向下游执行

重新启动运行测试

访问:http://localhost:8080/service-order/order/1


控制台

再次访问添加token
http://localhost:8080/service-order/order/1?access-token=123


3、网关限流

(1) 常见的限流算法

1) 计数器

计数器限流算法是最简单的一种限流实现方式。

其本质是通过维护一个单位时间内的计数器,每次请求计数器加1,当单位时间内计数器累加到大于设定的阈值,则之后的请求都被拒绝,直到单位时间已经过去,再将计数器重置为零

2) 漏桶算法

漏桶算法可以很好地限制容量池的大小,从而防止流量暴增。漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。 在网络中,漏桶算法可以控制端口的流量输出速率,平滑网络上的突发流量,实现流量整形,从而为网络提供一个稳定的流量。

3) 令牌桶算法

令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。

在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。

算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。

放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。

所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。


(2)基于Filter的限流

SpringCloudGateway官方就提供了基于令牌桶的限流支持。基于其内置的过滤器工厂
RequestRateLimiterGatewayFilterFactory 实现。
在过滤器工厂中是通过Redis和lua脚本结合的方式进行流量控制。

1)环境搭建

  • 安装并启动redis
    启动window本地的redis

    打开客户端使用monitor监控redis当中的数据

  • 导入redis的依赖
    首先在工程的pom文件中引入gateway的起步依赖和redis的reactive依赖,代码如下:

		 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

2)修改application.yml配置文件

在application.yml配置文件中加入限流的配置,代码如下:

server:
  port: 8080 #端口
spring:
  application:
    name: api-gateway-server #服务名称
  redis:
    host: localhost
    pool: 6379
    database: 0
  cloud: #配置SpringCloudGateway的路由
    gateway:
      routes:
        - id: product-service
          uri: lb://service-product
          predicates:
            - Path=/product-service/**
          filters:
            - name: RequestRateLimiter
              args:
                # 使用SpEL从容器中获取对象
                key-resolver: '#@pathKeyResolver'
                # 令牌桶每秒填充平均速率
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的上限
                redis-rate-limiter.burstCapacity: 3
            - RewritePath=/product-service/(?<segment>.*), /$\\segment
  # RequestRateLimiter : 使用限流过滤器,SpringCloud gateway提供的
  # 参数 replenishRate  :  向令牌桶中填充的速率
  # burstCapacity  : 令牌桶的容量
#eureka注册中心
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址注册

application.yml 中添加了redis的信息,并配置了RequestRateLimiter的限流过滤器:

  • burstCapacity,令牌桶总容量。
  • replenishRate,令牌桶每秒填充平均速率。
  • key-resolver,用于限流的键的解析器的 Bean 对象的名字。
    它使用 SpEL 表达式根据#
    @beanName从 Spring 容器中获取 Bean 对象。

3)配置KeyResolver

01)基于请求IP的 127.0.0.1

为了达到不同的限流效果和规则,可以通过实现 KeyResolver 接口,定义不同请求类型的限流键。

package cn.itbleubox.gateway;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway09.微服务网关组件之Gateway

Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)SpringBoot项目实现商品服务器端是调用

#私藏项目实操分享#SpringCloud技术专题「Gateway网关系列」微服务网关服务的Gateway组件的原理介绍分析

搭建SpringCloud微服务框架:SpringCloud-Gateway 服务网关处理

搭建SpringCloud微服务框架:SpringCloud-Gateway 服务网关处理

Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)SpringBoot项目实现商品服务器端是调用