微服务网关
閱讀本文約花費: 14 (分鐘)
本文阐述微服务的API网关的一些主要功能,并例举了几种常用的网关,最后结合spring cloud微服务框架对网关做一些简要的论述。 |
一、前言
随着微服务的兴起,基于其业务耦合性低、负载能力强、服务边界清晰等优点,大家纷纷使用微服务架构来实现新系统或进行老系统的改造。微服务在带来诸多好处的同时,也有一些问题需要解决,比如:如何做到有效拆分、减少服务间调用,如何统一管理所有服务的接口,如何进行自动化部署等。本文阐述微服务的API网关的一些主要功能,并例举了几种常用的网关,最后结合spring cloud微服务框架对网关做一些简要的论述。
二、API网关简介
API网关,顾名思义,是统一管理API的一个网络关口、通道,是整个微服务平台所有请求的唯一入口,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。下图为微服务架构的简单示意图,网关起到的作用一目了然。
三、API网关的作用
为微服务云平台提供统一的入口是API网关最主要的用途,除此之外,网关还可承担认证授权、访问控制、路由、负载均衡、缓存、日志、限流限额、转换、映射、过滤、熔断、注册、服务编排、API管理、监控、统计分析等等非业务性的功能。
所以实现或者选择一个好的API网关,是建设容器云和微服务体系中一个至关重要的事项。这也决定了API网关的部署,要尽可能的减少接触面,确保安全。下面介绍几个API网关的核心功能。
1)负载均衡
微服务架构一般都有一个注册中心,后端服务启动时候会将自己的服务地址注册到注册中心,并和注册中心保持心跳,网关用过监听注册中心来进行服务的发现,并根据一定的负载均衡算法(随机、轮询、权重、hash等)将客户端的请求尽量均衡地转发到后端的各个服务中。
2)服务熔断、重试及路由切换
和微服务的熔断道理一样,为了防止服务的雪崩效应,当检测到服务不可用时,需要进行快速失败。当某服务在短时间内多次发生调用失败,服务消费方的断路器会被断开。开路的断路器就像电路跳闸一样,阻止消费方向故障服务发送请求,直接返回失败或者执行消费方的降级逻辑。断路器通常在一定时间后关闭,在这期间可以为底层服务提供足够的空间来恢复。
当前服务调用失败时,可进行一定次数的重试,重试失败的情况下,网关还可以对指定个数实例的路由切换,保证后端服务的可用性。
3)限流
限流的主要目的是防止类似DDos的恶意攻击导致服务器短时间内收到大量请求而造成的服务瘫痪。因此需要在接口层面做流量的控制,API网关统计一个时间窗口内针对某服务的请求数量,如果超过一定的阈值,则应拒绝继续转发请求到后端服务。时间窗口是滑动窗口,下一个时间窗口到来时,计数器清零。可以使用Redis的单线程模型和高性能的并发性来保证高并发下计数器计数准确。
4)认证鉴权
单体应用中,一般用户登录后,服务端会将用户信息存入session中,然后返回给用户(客户端)一个cookie作为登录后调用其他业务接口的凭证。而在微服务架构下,服务被拆分成多个实例,单体应用中的模式就很难试用,于是需要把鉴权的业务从各服务中抽离出来,单独建立一个权限认证服务,利用API网关入口作为切面拦截。网关拦截用户请求,获取请求中附带的用户身份信息,调用认证授权中心的服务,对请求者做身份认证,即确认当前访问者确实是其所声称的身份,检查该用户是否有访问该后台服务的权限。
目前主流的认证鉴权方案有2种。
第一种是引入Redis做分布式会话,即用户登录成功后,将用户身份、权限信息存入Redis,以一个唯一ID作为Key,并设置信息在Redis里的失效时间。这个唯一ID的Key将返回给客户端,客户端可以放入Cookie,sessionStorage等处做本地存储。下次访问的时候,将这个唯一ID放入请求参数中一起发送(一般放入Header)。服务端通过检查Redis里有无这个ID来判断用户是否登录,获取用户身份和权限信息。客户端如果长时间没有操作,则存储在Redis里会话信息过期自动删除。客户端每访问一次服务端,需刷新一次会话信息的过期时间,避免固定过期时间带来的低用户体验。
第二种是JWT,即Java Web Token。用户登录成功后,服务端向客户端返回的唯一ID不再是无意义的字符串,而是包含了用户身份、权限、失效时间等信息的加密字符串。并且这个字符串包含数字签名,服务端可对这个字符串做数字签名验签,确保该字符串未经篡改和伪造。相比分布式会话方案,JWT虽省去了Redis存储,但是每次访问都要做数字签名验证,增加了CPU的资源损耗。
5)灰度发布
灰度发布是服务发布时比较好的一种升级方式,它可以根据客户端的实际情况(版本、IP端等)进行请求分流,将一小部分测试者的请求切到新版本服务上,万一有问题也能及时定位修复,且不影响线上老版本的使用。
四、常见的几种api网关
目前比较常见的几种开源的API网关有以下几种:Kong、Treafik、Ambassador、Tyk、Zuul ,下面对这几种网关做一下简单的介绍和对比。
Kong是一个在 Nginx 中运行的Lua应用程序,并且可以通过lua-nginx模块实现,Kong不是用这个模块编译Nginx,而是与 OpenResty 一起发布,OpenResty已经包含了lua-nginx-module,OpenResty不是Nginx的分支,而是一组扩展其功能的模块。
它的核心是实现数据库抽象,路由和插件管理,插件可以存在于单独的代码库中,并且可以在几行代码中注入到请求生命周期的任何位置。
Traefik是一个现代HTTP反向代理和负载均衡器,可以轻松部署微服务,Traeffik 可以与您现有的组件(Docker、Swarm,Kubernetes,Marathon,Consul,Etcd,…)集成,并自动动态配置。
Ambassador是一个开源的微服务 API 网关,建立在 Envoy 代理之上,为用户的多个团队快速发布,监控和更新提供支持,支持处理 Kubernetes ingress controller 和负载均衡等功能,可以与 Istio 无缝集成。
Tyk是一个开源的、轻量级的、快速可伸缩的 API 网关,支持配额和速度限制,支持认证和数据分析,支持多用户多组织,提供全RESTful API。基于go编写。
Zuul是一种提供动态路由、监视、弹性、安全性等功能的边缘服务。Zuul 是Netflix出品的一个基于JVM路由和服务端的负载均衡器。
由上述对比表格中可以看出:从开源社区活跃度来看,无疑是Kong和Traefik较好;从成熟度来看,较好的是Kong、Tyk、Traefik;从性能角度来看,Kong要比其他几个领先一些;从架构优势的扩展性来看,Kong、Tyk有丰富的插件,Ambassador也有插件但不多,而Zuul是完全需要自研,但Zuul由于与Spring Cloud深度集成,使用度也很高,近年来Istio服务网格的流行,Ambassador因为能够和Istio无缝集成也是相当大的优势。
五、spring cloud
目前能和spring cloud体系紧密集成的有两种API网关:zuul(zuul1)和spring cloud gateway。zuul网关再上面有提到,spring cloud gateway是spring官方推出的用于替代zuul的新组建。这里对于如何集成zuul或spring cloud gateway不做讲述,只对这两种网关做一个简单的阐述与比较。
先来看下zuul,目前zuul有两个版本:zuul1和zuul2,目前spring cloud只集成了zuul1,zuul2是Netflix在2018年5月推出,它最大的特点就是支持异步调用?(zuul1仅支持同步) ,可惜springcloud暂时没有计划集成zuul2,而且还推出spring cloud gateway来替代zuul1。
来看下zuul1的编程模型(同步阻塞)
本质上就是一个同步 Servlet,每来一个请求,zuul会专门分配一个线程去处理,然后转发到后端服务,后端再启线程处理请求,后端处理时网关的线程会阻塞,当请求数量比较大时,很容易造成线程池被沾满而无法接受新的请求,Netflix 为此还专门研发了Hystrix熔断组件来解决慢服务耗尽资源问题。
zuul2的编程模型(异步非阻塞)
zuul2是基于Netty实现的异步非阻塞编程模型,一般异步模式的本质都是使用队列 Queue(或称总线 Bus)。网关中会有一个队列专门处理用户请求,一个队列专门负责后端服务调用,中间有个事件环线程 (Event Loop Thread)同时监听两个队列,它的主要作用是将请求转发给后端,并将后端服务的处理结果返回给客户端,用队列的形式减轻了前端请求数量的压力。
zuul1和zuul2对比:
总结:建议是在生产环境中使用 zuul1
Zuul1 同步编程模型简单,门槛低,开发运维方便,容易调试定位问题。Zuul2 门槛高,调试不方便。
Zuul1 监控埋点容易,比如和调用链监控工具 CAT 集成,如果你用 Zuul2 的话,CAT 不好埋点是个问题。
Zuul1 已经开源超过 6 年,稳定成熟,坑已经被踩平。Zuul2 刚开源很新,实际落地案例不多,难说有 bug 需要踩坑。
大部分公司达不到 Netflix 那个量级,Netflix 是要应对每日千亿级流量,它们才挖空心思搞异步,一般公司亿级可能都不到,Zuul1 绰绰有余。
Zuul1 可以集成 Hystrix 熔断组件,可以部分解决后台服务慢阻塞网关线程的问题。
Zuul1 可以使用 Servlet 3.0 规范支持的 AsyncServlet 进行优化,可以实现前端异步,支持更多的连接数,达到和 Zuul2 一样的效果,但是不用引入太多异步复杂性。
Spring Cloud Gateway构建于 Spring 5+,基于 Spring Boot 2.x 响应式的、非阻塞式的 API。同时,它支持 websockets,和 Spring 框架紧密集成,开发体验相对来说十分不错。由于zuul2没有被spring cloud所集成,所以拿zuul1与spring cloud gateway做一些简单的比较。
spring官方给出的性能测试数据:
从表中可以看出,spring cloud gateway在性能上还是胜zuul一筹的,而且上手也简单。
一些API网关(Zuul 1)是阻塞的,另外一些(Zuul 2、Linkerd、Envoy)是非阻塞的。阻塞架构对开发和跟踪请求友好,但是阻塞可能产生扩展性问题。非阻塞架构对于团队开发和跟踪更复杂,但是有更好的可扩展和弹性。如何选择还是需要根据实际情况再做抉择。