SpringCloud第四课(gateway网关详解、gateway统一鉴权、令牌桶算法、基于Filter限流、基于Sentinel限流、网关高可用)
前言
上文文章(Hystrix监控平台、DashBoard、Turbine监控、熔断器的状态、微服务网关Zuul)
本文目录
文章目录
微服务网关GateWay
Zuul网关存在的问题
在实际使用中我们会发现直接使用Zuul会存在诸多问题,包括:
- 性能问题
Zuul1x版本本质上就是一个同步Servlet,采用多线程阻塞模型进行请求转发。简单讲,每来
一个请求,Servlet容器要为该请求分配一个线程专门负责处理这个请求,直到响应返回客户
端这个线程才会被释放返回容器线程池。如果后台服务调用比较耗时,那么这个线程就会被
阻塞,阻塞期间线程资源被占用,不能干其它事情。我们知道Servlet容器线程池的大小是有
限制的,当前端请求量大,而后台慢服务比较多时,很容易耗尽容器线程池内的线程,造成 容器无法接受新的请求。
- 不支持任何长连接,如websocket
Zuul网关的替换方案
- SpringCloud Gateway
微服务网关GateWay简介
Zuul 1.x 是一个基于阻塞 IO 的 API Gateway 以及 Servlet;直到 2018 年 5 月,Zuul 2.x(基于
Netty,也是非阻塞的,支持长连接)才发布,但 Spring Cloud 暂时还没有整合计划。Spring Cloud
Gateway 比 Zuul 1.x 系列的性能和功能整体要好。
Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式,统一访问接口。SpringCloud Gateway 作为 Spring Cloud 生态系中的网关,目标是替代 Netflix ZUUL,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。它是基于Nttey的响应式开发模式。
核心概念
- 路由(route) 路由是网关最基础的部分,路由信息由一个ID、一个目的URL、一组断言工厂和一
组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。 - 断言(predicates) Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是
Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定
义匹配来自Http Request中的任何信息,比如请求头和参数等。 - 过滤器(filter) 一个标准的Spring webFilter,Spring Cloud Gateway中的Filter分为两种类型,
分别是Gateway Filter和Global Filter。过滤器Filter可以对请求和响应进行处理。
搭建gateway项目模块
创建项目导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
注意:
SpringCloud Gateway使用的web框架为webflux,和SpringMVC不兼容。引入的限流组件是hystrix。redis底层不再使用jedis,而是lettuce
意思就是说不能存在web的启动器,不然会报错,如下
配置启动类
package cn.ebuy.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);
}
}
编写配置文件
创建 application.yml 配置文件
server:
port: 9091
spring:
application:
name: ebuy-gateway #服务名称
cloud:
gateway:
routes:
# 第一种 不支持负载均衡 http://localhost:9091/product/816753
- id: ebuy-product
uri: http://127.0.0.1:6501
predicates:
- Path=/product/**
logging:
level:
cn.ebuy: DEBUG
释义:
- id:我们自定义的路由 ID,保持唯一
- uri:目标服务地址
- predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
- filters:过滤规则,暂时没用。
启动测试
服务详情信息
访问测试
上图的意思是指访问路由路径http://localhost:9091/product/会自动转发至http://localhost:6501/product/地址,所以修改Path=/**,可以实现http://localhost:9091会自动转发至http://localhost:6501地址(不再演示)
动态路由
和zuul网关类似,在SpringCloud GateWay中也支持动态路由:即自动的从注册中心中获取服务列表并
访问。
添加注册中心依赖
在工程的pom文件中添加注册中心的客户端依赖(这里以Eureka为例)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置动态路由
修改 application.yml 配置文件,添加eureka注册中心的相关配置,并修改访问映射的URL为服务名称
server:
port: 9091
spring:
application:
name: ebuy-gateway #服务名称
cloud:
gateway:
routes:
# 第一种 不支持负载均衡 http://localhost:9091/product/816753
# - id: ebuy-product
# uri: http://127.0.0.1:6501
# predicates:
# - Path=/product/**
# 第二种 支持负载均衡,但只支持一个命令空间 http://localhost:9091/product/816753
- id: ebuy-product
uri: lb://ebuy-product
predicates:
- Path=/product/**
logging:
level:
cn.ebuy: DEBUG
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6001/eureka/,http://127.0.0.1:6002/eureka/
instance:
instance-id: ${spring.cloud.client.ip-address}:${server.port} #实例id
prefer-ip-address: true #使用ip地址注册
lease-expiration-duration-in-seconds: 10 #eureka client发送心跳给server端后,续约到期时间(默认90秒)
lease-renewal-interval-in-seconds: 5 #发送心跳续约时间间隔
重启服务
重启ebuy-gateway服务,并且开启第二个ebuy-product服务
访问测试
同样该配置只能访问请求连接以/product/开始的路径,我们可以修改Path=/**,这样就可以映射ebuy-product的任何服务了,配置如下:
# 第三种 映射该服务下全部路径 http://localhost:9091/product2/816753
# - id: ebuy-product
# uri: lb://ebuy-product
# predicates:
# - Path=/**
不再演示。
添加RewritePath重写转发路径
修改 application.yml ,添加重写规则。
# 第四种 添加RewritePath重写转发路径 http://localhost:9091/ebuy-product/product2/816753
- id: ebuy-product
uri: lb://ebuy-product
predicates:
- Path=/ebuy-product/**
filters:
- RewritePath=/ebuy-product/(?<segment>.*), /$\{segment}
请求服务
过滤器
Spring Cloud Gateway除了具备请求路由功能之外,也支持对请求的过滤。通过Zuul网关类似,也是通过过滤器的形式来实现的。那么接下来我们一起来研究一下Gateway中的过滤器
过滤器基础
过滤器的生命周期
Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。
- PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择
请求的微服务、记录调试信息等。 - POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
示意图:
过滤器类型
Spring Cloud Gateway的Filter从作用范围可分为另外两种Gateway和GlobalFilter。
- GatewayFilter:应用到单个路由或者一个分组的路由上。
- GlobalFilter:应用到所有的路由上
局部过滤器
局部过滤器(GatewayFilter),是针对单个路由的过滤器。可以对访问的URL过滤,进行切面处理。在Spring Cloud Gateway中通过GatewayFilter的形式内置了很多不同类型的局部过滤器。这里简单将Spring Cloud Gateway内置的所有过滤器工厂整理成了一张表格,虽然不是很详细,但能作为速览使
用。
如下:
—
每个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾,这是Spring Cloud Gateway的一个约定,例如 AddRequestHeader 对应的实现类为AddRequestHeaderGatewayFilterFactory 。对于这些过滤器的使用方式可以参考官方文档
全局过滤器
全局过滤器(GlobalFilter)作用于所有路由,Spring Cloud Gateway 定义了Global Filter接口,用户
可以自定义实现自己的Global Filter。通过全局过滤器可以实现对权限的统一校验,安全性验证等功
能,并且全局过滤器也是程序员使用比较多的过滤器。
Spring Cloud Gateway内部也是通过一系列的内置全局过滤器对整个路由转发进行处理如下:
图示
统一鉴权
内置的过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们自己编写过滤器来实现的,那么我们一起通过代码的形式自定义一个过滤器,去完成统一的权限校验。
鉴权逻辑
开发中的鉴权逻辑:
- 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
- 认证通过之后,将用户信息进行加密形成token,返回给客户端,作为登录凭证
- 以后每次请求,客户端都携带认证的token
- 服务端对token进行解密,判断是否有效
流程图:
如