面试题微服务网关

2021/05/21

微服务网关

在微服务架构中,随着微服务的拆分,这些微服务不可能同时提供对外服务,这样就需要一个网关系统,承接外网的流量。有了API 网关,各个 API 服务提供团队可以专注自己的业务逻辑处理,而 API 网关则更专注于安全、流量、路由等问题。
我们先来看一下微服务网关主要提供哪些功能。

统一流量接入:提供统一的流量入口,这样就可以由统一的入口管理流量,设置各种策略,比如统一的 Token 认证等。
业务聚合:在具体的业务中,经常需要聚合多个服务的结果集,返回给客户端,这个时候可以由 API 网关聚合数据然后返回给客户端。
协议转换:一般入口层,多使用 HTTP 协议的 RESTful 接口,而后端服务的协议可能有多种,比如 gRPC、Thrift 等。
中间件策略:设置统一的中间件策略层,可以在这一层做一些限流熔断、南北向流量的服务治理功能。
安全认证:一些 Token 认证功能,比如数据是否被篡改的认证,可以在 API 网关中做,这也是经常用到的功能。
证书管理:随着对外网安全性能要求的增高,现在基本上都要对外提供 HTTPS 的服务,以保证数据不会被劫持、篡改等问题,在这一层做证书对内网的拆卸非常合适。由一个统一的入口管理接口,降低了证书更换时的复杂度。

常用微服务网关介绍
Kong:几乎是目前最流行的微服务网关,内置了多种网关所需要的功能,后面我们将详细介绍。
Spring Cloud Zuul:作为 Spring Cloud 的一部分,提供了微服务所需要的网关的大部分功能,最大的优势是对 Java 系统友好。严格来说Zuul 更像是一个微服务网关的框架,而不是独立的产品。
Spring Cloud Gateway:Spring Cloud Gateway 是 Spring 官方自己开发的一款 API 网关。性能比Zuul更加优越。

双重网关(系统网关和业务网关)

随着业务进一步发展,单一的网关系统已经不能满足组织架构和业务的发展了,所以在实际生产中,多采用双重网关结构。

这种结构由多个系统网关和多个业务聚合网关组成,这样配合是因为系统网关多由常见的 Nginx 系网关构成,使用 Nginx 系的网关对运维友好,方便运维做一些监控、日志、安全策略;但往往对研发人员不太友好,不方便扩展。所以后来增加了业务网关,用于服务的聚合,这层网关因为需要做一些业务的聚合策略,平时改动会比较多。

如下图所示,两个网关相互配合,既方便了运维维护,也方便了研发人员扩展,达到某种平衡。

另外,随着单一业务的发展,我们也会像下图一样拆分出多个独立的网关,这些网关会根据业务维度拆分,比如分为用户系统网关和内容系统网关。这样拆分的目的,是防止由单一网关的故障带来全站的不可用,从而达到降低故障影响面的效果。

mianshitiweifuwuwangguan_1.png

微服务网关 Kong

开源网关 Kong是一个轻量级的 API 网关,基于 OpenResty + Lua 开发,提供了丰富的功能以及高度灵活的扩展性,可以通过插件的方式扩展 Kong 的功能。

mianshitiweifuwuwangguan_2.png

Kong 作为微服务网关,最大的优势就是抽象出了微服务网关的一套模型,而不需要像 OpenResty 那样手动配置。
下面我们看一下这套模型中的各个名词的含义。
service:服务对应的后端的微服务和 API。一般情况下把一组 API 集合成为一个服务,也就是我们后端的微服务。
router:路由指的是流量到达 API 网关后,如何找到后端对应的服务。针对不同的流量实现多种路由策略。
Admin API:Kong 中的 RESTful 的内部 API 接口。 API 接口可以在集群的任意节点运行,最终同步到所有集群的节点。用于管理 Kong 的各种行为,比如添加 service、router 等。
Plugins:Kong 的插件系统。利用插件系统,我们添加新功能就不需要改动 Kong 的核心代码。插件系统自带足够多的功能,比如认证、限流、日志、Metrics 等。
Load Balancing:Kong提供了两种负载均衡方式,DNS 和内置的负载均衡器。利用 DNS 模式,可以和 Consul 结合,实现服务发现。内置的负载均衡,则需要自己手动维护后端节点。

Konga

Kong 官方并没有为开源版本提供图形界面,只在其企业版提供了此功能。但是我们有一些开源的图形界面可以选择,其中最知名的就是 Konga。
Konga 提供了多种功能,包括 service、router、Load Balance 的管理,并支持多用户系统,方便给不同用户划分不同的权限。
mianshitiweifuwuwangguan_3.png

Kong 架构演进

Kong 经历了几次架构演进,终于演进到了目前比较流行的微服务架构方式。整体来说该架构主要经历了如下演进:DB Mode(0.x-1.0)→ DB-less Mode(1.1-1.5)→ Hybird Mode(2.0+)
下面我们分别介绍以下几种模式,以及它们的适用场景。
DB Mode:最早的 Kong 版本就是使用这种方式,每个 Kong 都连接了后端 DB,这个 DB 可选为 PostgreSQL 或者 Cassandra。但是这个模式之前有个很严重的 Bug,就是在流量大的时候更新路由配置,会导致 Kong 产生剧烈的抖动。

DB-less Mode:无数据库模式,也就是通过声明式的配置方式来运行 Kong。这种方式从某种意义来说是一种倒退,因为它让 Kong 无法通过 API 控制,也就是回到了原始的 Nginx 配置模式,相当于 Service Mesh 失去了控制面。但也因为回归了原始的方式,不会出现 DB Mode 更新配置时导致的一些抖动问题。

Hybird Mode:其实也就是引入了 Service Mesh 中的控制面和数据面的模式。相对于上面两种模式有巨大的架构优势,Hybird Mode 通过控制面收敛了数据库的操作行为,Kong 的数据节点不需要再直接连接到 DB 层。这种模式有以下两种优势。仅控制面节点需要连接到 DB,而不是所有数据节点连接到 DB,对 DB 的压力大大减少,也就降低了数据面出现抖动的概率。易于管理,不用像之前的 API 调用 Kong 的数据节点时再操作 Admin API 变更,只需与控制面交互即可。

Zuul

Zuul是Netflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:
动态路由:动态将请求路由到不同后端集群
压力测试:逐渐增加指向集群的流量,以了解性能
负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求
静态响应处理:边缘位置进行响应,避免转发到内部集群身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。Spring Cloud对Zuul进行了整合和增强。

动态路由

动态路由是动态的将客户端的请求路由到后端不同的服务上,如果没有网关去做统一的路由,那么客户端就需要关注后端 N 个服务。

mianshitiweifuwuwangguan_4.png

左边的图没有使用网关,客户端调用服务时就需要访问服务各自的接口,如客户端调用 A 服务的接口就需要请求 a.com,而对需要访问服务的客户端来说访问流程越简单越好,现在需要关注多个 API 提供方,无疑提高了访问的复杂度。

右边的图使用了网关,使用网关后,客户端只需要关注网关的地址,也就是 gateway.com。不再需要关注多个 API 提供方,由网关统一路由到后端的具体服务中,这其实跟我之前讲的集中式负载均衡的概念类似,这样的好处是对客户端来说访问服务的流程简单了,关注的点少了。

另外一个好处就是可以在后端做 API 聚合操作,比如客户端要展示一个商品详情,里面有商品基本信息、库存信息等,如果没有聚合,就需要调用基本信息的接口,然后再调用库存信息的接口,如果做了聚合,客户端只需要调用一个接口,这个接口中包含了所有需要的信息,减少了前后端交互的次数,提升了用户的体验。

请求监控

请求监控可以对整个系统的请求进行监控,详细地记录请求响应日志,可以实时统计当前系统的访问量及监控状态。

mianshitiweifuwuwangguan_5.png

如果没有使用网关的话,记录请求信息需要在各个服务中去做。当网关出现在我们的架构中后,所有客户端的请求都会经过网关来做路由分发,入口统一了,很多事情也就好处理了,我们只需要在网关中统一进行请求信息的记录,就可以基于这些记录做实时的数据分析,比如并发调用量,根据数据分析决定是否要动态限流,分析是否有爬虫请求等多维数据结果。给业务方提供正确实时的决策信息,是非常有价值的。

认证鉴权

认证鉴权可以对每一个访问请求做认证,拒绝非法请求,保护后端的服务。微服务架构下,如果没有使用网关,那么客户端需要直接跟多个服务进行交互,当请求到达对应的服务时,就必须验证当前的请求有没有登录,有没有权限访问。访问 A 服务需要验证一次,访问 B 服务也需要验证一次,每个服务都要做重复的工作。

mianshitiweifuwuwangguan_6.png

当我们使用网关后,就可以在网关中做统一的验证逻辑了,唯一要做的工作就是在网关验证完成后,需要将用户信息传递给后端服务,后端服务默认相信当前的请求已经在网关中通过验证,它不会再去做验证的逻辑,但是当前请求对应的用户信息要告诉后端服务,可以将用户信息通过 HTTP 请求头传递给路由的后端服务。

压力测试

压力测试是一项很重要的工作,像一些电商公司需要模拟更多真实的用户并发量来保证大促时系统的稳定,通过 Zuul 可以动态地将测试请求转发到后端服务的集群中,还可以识别测试流量和真实流量,用来做一些特殊处理。

mianshitiweifuwuwangguan_7.png

对于测试请求,可以在请求头中添加标识,让网关能够识别这是一个测试请求,当识别到测试请求后,根据对应的规则进行路由,这里可以用配置中心存储规则,测试请求路由到测试服务,测试服务会有单独的测试数据库,这样测试的请求就不会影响到正式的服务和数据库了。

灰度发布

灰度发布可以保障整体系统的稳定性,在初始灰度的时候就可以及时发现、调整问题,以降低影响范围。

mianshitiweifuwuwangguan_8.png

需要发布新版本的时候,不会立即将老的服务停止,去发布新的服务。而是先发布新版本的服务,比如之前的版本是 1.0,那么现在发布的版本就是 1.1,发布后,需要通过测试请求对 1.1 版本的服务进行测试,如果没发现什么问题,就可以将正常的请求转发过来了。如果测试中发现问题,可以直接停掉 1.1 版本的服务,就算不停掉也没关系,不会影响到正常用户的使用。

过滤器

过滤器是 Zuul 中最核心的内容,过滤器可以对请求或响应结果进行处理,Zuul 还支持动态加载、编译、运行这些过滤器,过滤器的使用方式是采取责任链的方式进行处理,过滤器之间通过 RequestContext 来传递上下文,通过过滤器可以扩展很多高级功能。Zuul 中的过滤器总共有 4 种类型,每种类型都有对应的使用场景。

pre 过滤器:可以在请求被路由之前调用。适用于身份认证的场景,认证通过后再继续执行下面的流程。
route 过滤器:在路由请求时被调用。适用于灰度发布的场景,在将要路由的时候可以做一些自定义的逻辑。
post 过滤器:在 route 和 error 过滤器之后被调用。这种过滤器将请求路由到达具体的服务之后执行。适用于添加响应头,记录响应日志等应用场景。
error 过滤器:处理请求发生错误时被调用。在执行过程中发送错误时会进入 error 过滤器,可以用来统一记录错误信息。

请求生命周期

当一个请求进来时,会先进入 pre 过滤器,在 pre 过滤器执行完后,接着就到了 routing 过滤器中,开始路由到具体的服务中,路由完成后,接着就到了 post 过滤器中,然后将请求结果返回给客户端。如果在这个过程中出现异常,则会进入 error 过滤器中,这就是请求在整个 Zuul 中的生命周期。

mianshitiweifuwuwangguan_9.png

性能问题

Zuul1x 版本本质上就是一个同步Servlet,采用多线程阻塞模型进行请求转发。简单讲,每来一个请求,Servlet容器要为该请求分配一个线程专门负责处理这个请求,直到响应返回客户端这个线程才会被释放返回容器线程池。如果后台服务调用比较耗时,那么这个线程就会被阻塞,阻塞期间线程资源被占用,不能干其它事情。我们知道Servlet容器线程池的大小是有限制的,当前端请求量大,而后台慢服务比较多时,很容易耗尽容器线程池内的线程,造成容器无法接受新的请求。
不支持任何长连接,如 websocket

Spring Cloud Gateway

Spring Cloud Gateway 是 Spring 官方自己开发的一款 API 网关。在具体展开介绍 Spring Cloud Gateway 之前,我们有必要对它和 Netflix Zuul 做一个对比。通过上一课时的分析,我们知道 Zuul 的实现原理是对 Servlet 的一层封装,通信模式上采用的是阻塞式 I/O。而在技术体系上,Spring Cloud Gateway 基于最新Spring 5 和 Spring Boot 2,以及用于响应式编程的 Project Reactor 框架,提供的是响应式、非阻塞式 I/O 模型。所以较之 Netflix Zuul,性能上Spring Cloud Gateway 显然要更胜一筹。

另一方面,从功能上,Spring Cloud Gateway 也比 Zuul 更为丰富。除了通用的服务路由机制之外,Spring Cloud Gateway 还支持请求限流等面向服务容错方面的功能,同样也能与 Hystrix 等框架进行良好的集成。

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) 过滤器的生命周期
Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。

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

( 2) 过滤器类型
Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter 与 GlobalFilter。

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

统一鉴权

内置的过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们自己编写过滤器来实现的,那么我们一起通过代码的形式自定义一个过滤器,去完成统一的权限校验。
开发中的鉴权逻辑:
当客户端第一次请求服务时,服务端对用户进行信息认证(登录)认证通过,将用户信息进行加密形成 token,返回给客户端,作为登录凭证以后每次请求,客户端都携带认证的 token服务端对 token进行解密,判断是否有效。

基于Filter的限流

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

原理:

Gateway的客户端向 Spring Cloud Gateway 发送请求,请求首先被 HttpWebHandlerAdapter 进行提取组装成网关上下文,然后网关的上下文会传递
到 DispatcherHandler 。 DispatcherHandler 是所有请求的分发处理器, DispatcherHandler 主要负责分发请求对应的处理器。比如请求分发到对应的 RoutePredicateHandlerMapping (路由断言处理映射器)。路由断言处理映射器主要作用用于路由查找,以及找到路由后返回对应的FilterWebHandler 。 FilterWebHandler 主要负责组装Filter链并调用Filter执行一系列的Filter处理,然后再把请求转到后端对应的代理服务处理,处理完毕之后将Response返回到Gateway客户端。

作者:dalianpai

来源:https://www.cnblogs.com/dalianpai/p/14388776.html

本文为原创文章,转载请标明出处。
本文链接:http://blog.fangzhipeng.com/javainterview/2021/05/21/3365bd2.html
本文出自方志朋的博客


(转载本站文章请注明作者和出处 方志朋-forezp

宝剑锋从磨砺出,梅花香自苦寒来,用心分享,一起成长,做有温度的攻城狮!
   

Post Directory