由于我们使用的服务系统架构,所以没办法像传统单体应用一样依靠数据库的 join 查询来得到最终结果,那么如何才能访问各个服务呢?
按照微服务设计的指导原则,我们的微服务可能存在下面的问题:
那么,对于前端的UI需求也可能会有以下几种:
API Gateway(API GW / API 网关),顾名思义,是企业 IT 在系统边界上提供给外部访问内部接口服务的统一入口。在微服务概念的流行之前,API网关的实体就已经诞生了,例如银行、证券等领域常见的前置机系统,它也是解决访问认证、报文转换、访问统计等问题的。
百度百科:API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。
API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。
API网关的流行,源于近几年来,移动应用与企业间互联需求的兴起。移动应用、企业互联,使得后台服务支持的对象,从以前单一的Web应用,扩展到多种使用场景,且每种使用场景对后台服务的要求都不尽相同。这不仅增加了后台服务的响应量,还增加了后台服务的复杂性。
当应用架构是由一系列的微服务构建的时候,我们就应当考虑对外提供的服务如何与微服务进行交互。
我们根据使用场景分类大体如下:
Web App APIGateway 和 Mobile App APIGateway:
聚合作用:写简单的业务逻辑,跨多个service获取数据拼装结果。
在Mobile app产品信息页面仍然要展现很多的信息。例如,基本的产品信息,该页也会展示产品其他相关信息:
购物车;历史订单;顾客评论;低库存预警;运送选项;各种产品推荐;可替代的购买选项;
当使用单体应用架构时:
移动客户端通过给应用发送一个REST请求来获取数据,比如:
GET api.company.com/productdetails/productId
负载均衡器路由这个请求到几个相同应用实例其中的一个。单体应用接着查询多个数据库表并返回响应给客户端。
当使用微服务架构的时候:
显示在产品详细信息页面的数据来自于多个微服务。如下是一些潜在的服务,它们也有可以展示在特定产品信息页面的数据:
购物车服务、订单服务、目录服务、评论服务、库存服务、运送服务、推荐服务。
这个需要使用Mobile app API Gateway:它通常向移动客户端暴露出一个粗粒度的API。
API网关可以提供类似这样的接口:
GET api.company.com/productdetails/productId
可以使移动客户端在一次调用中获取所有的产品信息。API网关通过调用多个服务—产品服务、推荐服务、评论服务等来处理请求,并封装结果。
优点:
解耦作用:它封装了应用的内部结构,客户端直接和网关通信,而不必调用特定的服务。
提供给每种客户端特定优化的API。
减少请求环路、简化客户端逻辑。比如说,API Gateway使得客户端用一次请求就可以从多个service处获取数据。
缺点:
必须被开发、部署和管理成一个高度可用的组件,也有成为开发瓶颈的危险。
为了暴露出每个微服务的,开发者必须更新API网关。
Partner OpenAPI:
转发路由作用:对外提供的接口服务,一般直接把请求转发到合适的service,但是此时的API GW需要增加配额、限流、令牌等一系列安全管控功能。对外提供隔离作用:请求路由,安全认证,负载均衡,限流、监控、权限控制等等。
安全性问题
企业在把服务暴露给外部使用时,首先要确保服务使用的安全,防止外部的恶意访问对公司业务的影响,特别是涉及交易方面的服务,更是要全面考虑安全性。为确保安全,需要考虑在通讯链路的建立、通讯数据的加密、数据的完整性、不可抵赖性等方面。
性能问题
作为企业API的入口,所有的请求都会经过API网关进行转发,可想而知,对API网关的访问压力是巨大的,有的网站甚至达到每分钟上千万的访问量。特别是在一些互联网企业,海量的移动终端每时每刻都需要与后端的服务进行交互,如果不能保证网关的高性能,企业在网关层需要投入大量的设备和成本。曾在一家互联网公司发生过,由于网关性能问题,网关的机器数量,需要与后台服务器的数量保持同步增长。这种情况显然是企业服务忍受的。
高可用问题
API网关作为逻辑上的单点,一旦发生问题,将造成企业服务的不可用,对企业来说可能造成的致命的影响。计算短时间的不可用,也会给企业带来直接的经济损失。所以,如何保证API网关的7*24小时的稳定运行,网关的自动伸缩、API的热更新等问题,都是企业级的网关需要考虑的
扩展性问题
前面说到,企业网关提供了一个脚手架,一些非功能性的问题,例如日志、安全、负载均衡策略、鉴权等。这些插件会随着企业业务规模等的变化进行不断的强化与调整。这就需要网关层提供这样一种机制,使得可以灵活地进行这些调整和变化,而不用频繁对网关层进行改动,确保网关层的稳定性。
API高效运维的问题
API在上线、发布过程中,都需要涉及到网关层的配合,例如,需要网关层知道API发布的地址,API的接口形式、报文格式,也需要网关层对后台API进行封装。在API调整后,需要作出相应的修改。所以,API网关设计时,需要明确网关层与服务层的职责切分与协作模式,使得API的管理、发布更加高效。
API全生命周期的管理
API服务的全生命周期,包括服务的开发、测试、上线发布;服务使用的申请、开通;服务分类分级别的管理、服务使用情况的监控、计费等等。
一个企业可能会暴露成百上千个API,日常也会经常进行API的发布、升级、改造、下架等操作。对不同的服务,不同的访问者,需要提供不同的服务访问策略。有的商业API公司,还需要对API的使用进行付费。所以,与API网关配套的,需要一套完善的自助系统,提供给服务的提供者、管理者、使用者,来对服务的发布、使用、和运营。
通常情况下, API 网关要做很多工作,它作为一个系统的后端总入口,承载着所有服务的组合路由转换等工作,除此之外,我们一般也会把安全,限流,缓存,日志,监控,重试,熔断等放到 API 网关来做,那么可以试想在高并发的情况下,这里可能会出现一个性能瓶颈。
另外,如果没有开源项目的支撑前提下,自己来做这样一套东西,是非常大的一个工作量,而且还要做 API 网关本身的高可用等,如果一旦做不好,有可能最先挂掉的不是你的其他服务,而就是这个API网关。
基于OpenResty 的 Nginx
性能和高可用性上
Nginx性能极高,Nginx先天的事件驱动型设计、全异步的网络I/O处理机制、极少的进程间切换以及许多优化设计,都使得Nginx天生善于处理高并发压力下的互联网请求。Nginx的稳定性也在各大网站得到验证。官方提供的常用模块都非常稳定,每个worker进程相对独立,master进程在1个worker进程出错时可以快速“拉起”新的worker子进程提供服务。支持热部署,可以不停机更新配置文件、更新日志文件、更新服务器程序版本。
扩展性上
Nginx的设计极具扩展性,它完全是由多个不同功能、不同层次、不同类型且耦合度极低的模块组成。因此,当对某一个模块修复Bug或进行升级时,可以专注于模块自身,无须在意其他
易用性上
Nginx使用最自由的BSD许可协议,允许用户在自己的项目中直接使用或修改Nginx源码,有大量的插件可以利用。但是,Nginx模块需要用C开发,而且必须符合一系列复杂的规则。虽然通过第三方模块,可以支持Nginx与Perl、Lua等脚本语言集成工作,但对使用者的要求还是很高。
验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
动态路由: 以动态方式根据需要将请求路由至不同后端集群处。
压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
Netflix公司还利用Zuul的功能通过金丝雀版本实现精确路由与压力测试。虽然提供的功能还算丰富,但都比较弱,很难满足高要求的场景。
性能和高可用性
Zuul处理每个请求的方式是针对每个请求是用一个线程来处理。通常情况下,为了提高性能,所有请求会被放到处理队列中,从线程池中选取空闲线程来处理该请求。2016年底,Netflix将它们的网关服务Zuul进行了升级,全新的Zuul 2将HTTP请求的处理方式从同步变成了异步,以提升其处理性能。除了Netflix公司,目前Zuul在企业中用的还比较少,性能和稳定性方面还有待进一步观察。
扩展性上
从Zuul的架构图上可以看出,Zuul更像是一个过滤器框架,其自身的路由、日志、反向代理、ddos预防等功能都是通过过滤器实现的。提供了PRE、ROUTING、POST和ERROR四个扩展点,可以很容易的添加自定义的过滤器。
易用性上
Zuul的搭建非常简便,使用和配置也很简单。Zuul的开源社区比较活跃,一直在更新状态,但版本不算太稳定,在使用的过程中,还有一些坑要踩。例如重定向问题、异常处理问题,还没有解决的很好,需要自己重写一些filter。
Kong的一个非常诱人的地方就是提供了大量的插件来扩展应用,通过设置不同的插件可以为服务提供各种增强的功能。Kong默认插件插件包括:
身份认证:Kong提供了Basic Authentication、Key authentication、OAuth2.0 authentication、HMAC authentication、JWT、LDAP authentication认证实现。
安全:ACL(访问控制)、CORS(跨域资源共享)、动态SSL、IP限制、爬虫检测实现。
流量控制:请求限流(基于请求计数限流)、上游响应限流(根据upstream响应计数限流)、请求大小限制。限流支持本地、Redis和集群限流模式。
分析监控:Galileo(记录请求和响应数据,实现API分析)、Datadog(记录API Metric如请求次数、请求大小、响应状态和延迟,可视化API Metric)、Runscope(记录请求和响应数据,实现API性能测试和监控)。
转换:请求转换、响应转换
Kong本身也是基于Nginx的,所以在性能和稳定性上都没有问题。Kong作为一款商业软件,在Nginx上做了很扩展工作,而且还有很多付费的商业插件。Kong本身也有付费的企业版,其中包括技术支持、使用培训服务以及 API 分析插件。
从对上面三种方案的比较中可以看到,Spring Cloud Zuul非常适合创业初期的团队,快速搭建一个“基本可用”的API网关。Nginx适合有较强研发团队,自主开发企业自己的API网关。Kong适合于没有自身研发团队,但需要拥有企业级API网关能力的公司。
Orange:和Kong类似也是基于OpenResty的一个API网关程序,是由国人开发的。
apiaxle:Nodejs 实现的一个 API 网关。
api-umbrella:Ruby 实现的一个 API 网关。
Tyk:Tyk是一个开放源码的API网关,它是快速、可扩展和现代的。Tyk提供了一个API管理平台,其中包括API网关、API分析、开发人员门户和API管理面板。Try 是一个基于Go实现的网关服务。
比较:
Spring Cloud Zuul非常适合创业初期的团队,快速搭建一个“基本可用”的API网关。
OpenResty+Nginx适合有较强研发团队,自主开发企业自己的API网关。
Kong适合于没有自身研发团队,但需要拥有企业级API网关能力的公司。
Try扩展需要会Go语言
但是上面的这些 API 网关都缺少超时,熔断,重试,聚合查询等功能。这个需要我们业务自己开发。
OpenResty Api Gateway
HTTP 请求先由DNS在拿到第一手流量后负载均衡到基于 OpenResty 的 API Gataway 网关集群,在这个流程我们可以使用像 Kong,Orage,Tyk 这些开源的支持高并发高访问量 API 网关程序在做第一层流量的防护,在这一级我们可以做一些像身份认证,安全,监控,日志,流控等策略。除了这些我们还可以做一些服务的发现和注册(这个要看不同网关的支持程度),接口的版本控制,路由重写等。
OpenResty Api Gateway也可以直接访问内部服务,比如一些对外开放平台api。
Web/Mobile Api Gateway
然后再由这些 OpenResty API 网关把请求再负载到不同的 Web/Mobile Api Gateway,在这里我们做聚合服务这个操作,具体体现也就是图中的黄色区域是需要由各个语言的开发人员来需要写代码实现的。这期间对于有需求的接口,我们可以应用超时,缓存,熔断,重试等策略。
从 Web/Mobile Api Gateway 到后端微服务集群这中间就属于内部的通讯了。
功能需求
API 生命周期管理功能
覆盖 API 的定义、测试、发布的整个生命周期管理,便捷的日常管理、版本管理,支持热升级和快速回滚。
开发和使用支持功能
提供页面调试工具,自动生成 API 文档和 SDK,大大降低人力成本。
安全防护功能
API 请求到达网关需要经过严格的身份认证、权限认证,才能到达后端服务。支持算法签名,支持 SSL 加密。
流量控制功能
可控制单位时间内 API 允许被调用次数。用来保护企业的后端服务,实现业务分级和用户分级。 支持对 API 流控,您可以根据 API 的重要程度来配置不同流控,从而保障重要业务的稳定运行; 支持用户、应用和例外流控,您可以根据用户的重要性来配置不同流控,从而可以保证大用户的权益; 流控粒度:分钟、小时、天。
请求管理功能
可根据配置进行参数类型、参数值(范围、枚举、正则、Json Schema)的校验,减少后端对非法请求、无效请求的资源消耗和处理成本。可以在 API 网关定义参数映射规则,网关通过映射规则将后端服务通过映射翻译成任何形式,以满足不同用户的不同需求,从而避免功能重复开发。
监控告警功能
提供实时、可视化的 API 监控,包括:调用量、调用方式、响应时间、错误率,让您能够清楚的了解 API 的运行状况和用户的行为习惯。支持自定义报警规则,来针对异常情况进行报警,降低故障处理时间。提供可订阅的数据分析报表和智能分析。
高性能设计
传统的基于线程的并发模型(Thread-based concurrency),为每一个请求分配一个线程或进程。这种模型编程简单,可以将处理一个完整请求的代码编写在一个代码路径中。这种模型的弊端是,随着线程(进程)数的上升,操作系统在这些线程(进程)之间的频繁切换,将急剧降低系统的性能。
高可用设计
无状态设计原则
网关层为保证高可以,易于伸缩,快速启动,需要设计成无状态的。用户的状态数据我们通常使用session对象来封装,网关层要设计成无状态的,也就是说,不能由网关来负责session的维护。那由谁来维护session相关的信息呢?我们是采用cookie+session服务器的方式;
优雅下线原则
当需要撤掉一台网关服务的时候,不是直接结束网关进程,而是先关闭监听套接字,但是继续为当前连接的客户提供服务,所有客户端的服务完成后,在把进程关闭。
Slow start特性
当网关监听到有一台新的服务注册上来时,考虑到有些服务启动后,刚开始会有许多初始化的工作,此时服务对请求的响应速度是比较慢的。如果一开始就给这台服务分配太多的压力,有可能导致服务瞬间被压垮。为了避免这种情况,网关层需要考虑支持Slow Start特性。即,经过一段时间,逐渐把压力增加到预设的值。
扩展性设计
我们知道,网关对请求的处理,可以分为三个阶段:接受请求、路由并转发请求、接受服务的返回数据并返回给请求者,除此之外,还有一种情况是处理错误。所以我们也可以在这四个地方添加扩展点:
拦截器的处理顺序,可以分为两大类:一类为网关平台自带的拦截器,例如安全校验、日志记录等;一类为网关层逻辑开发的,例如格式转换等。一般来说,网关先执行网关平台自带的拦截器,再执行为了业务逻辑编写的拦截器。当然,网关也需要提供一种机制,可以较容易地调整拦截器的执行顺序。最简单的一种方法,就是给每个拦截器定义一个优先级,网关按优先级顺序依次调用各拦截器。
对网关层来说,它接收和处理的数据都是Request对象,网关层在接收到请求后,把请求封装为Request对象,为了让后续的filter能够获得这个对象,可以考虑把Request对象保存在线程变量中。
有些拦截器,例如一些调试日志的拦截器,通常情况下都是关闭的,只有在出现问题的时候才需要打开。为了保证网关的高可用,网关层必须具备在线启用或关闭拦截器的能力。一般,网关需要提供restful接口方式,来关闭和启用一个拦截器。类似这样的命令:PUT /apigateway/v1/filters/filterName?enable=value
API管理与动态发布设计
对服务管理来说,分为前端服务管理与后端服务管理。前端服务指的是网关层暴露给客户端使用的服务API,后端服务指的是服务层提供的业务服务API。一个服务暴露给客户端使用,除了网关层和服务层提供服务的代码外,还需要配置前端服务与后端服务的映射关系。
网关层API调用服务层API,有多种方式。例如,可以由按照服务层API的服务契约,生成一段客户端代码,发布给网关层使用。这种方式的弊端是,网关层代码依赖于服务层代码,服务层频繁修改和调整接口时,导致网关层的代码很难维护。
可以通过配置前后端服务映射的方式,解耦网关层对服务层的依赖。当服务层的API(例如服务名、参数名等)发生变化时,只需调整映射关系,无需对网关层的代码进行调整。网关层按照映射,自动装配服务层API所需要的数据格式。这样,网关层团队与服务层团队可以相互不受干扰地开发各自的服务。
上文出自:架构设计(4)–API网关