API 网关和微服务框架这两个概念,对于有 IaaS 基础,初涉 PaaS 的同学有可能容易混淆。我们试着从一些实例入手,来看看 API 网关和微服务框架的相同之处以及差异之处。
近期工作需要,编者研究了一下 API 网关与微服务框架。
对于没有做过开发的同学,或者是脱离一线 JAVA/Go/Python 等互联网时代开发时间较长的同学而言,这两个概念本身就很难理解,也很容易混淆。因此,我们在这里试图正本清源,帮助自己 ,也帮助大家弄清楚这两个基本概念。
首先让我们来理解一下 API 网关的概念,举一个例子:
Dennis 的公司开发了一个报销系统,域名为: sse.Dennis.com
报销系统需要关联这些信息:
1、报销人所在的部门、部门主管及秘书,用于报销单初审;
2、报销单所关联的出差流程,用于确定相关的出差补助天数;
3、报销单所关联的产品或市场项目,用于分摊费用;
而这些信息所在的系统和API如下表:
系统功能 | API URL |
---|---|
员工信息查询 | hr.Dennis.com/query.aspx?employeeid=员工ID |
出差流程查询 | travel.Dennis.com/query.aspx?businesstravelid=出差流程ID |
财务编码查询 | finance.Dennis.com/query.aspx?productid=产品ID |
finance.Dennis.com/query.aspx?mktprojectid=市场项目ID |
我们发现,开发报销系统,会涉及到3个域名和4个 API 接口。那么,这些因素中的任意一个发生变化,都会导致需要重新修改报销系统的代码,并重新构建版本,进行测试后发布!
显然,这种变更带来的修改代码并验证的工作量,会让开发同学的发际线疯狂后移……
究其根本,虽然企业可以通过部署 IaaS 云计算等方式,让各个应用系统共享计算/存储/网络/安全等 ICT 资源,但由于各个应用系统本身之间是烟囱式的,彼此之间的数据互联互通存在鸿沟——
IaaS资源统一分配,其他软件各自为政
有没有一种机制,能统一企业内部 API 接口,让“程序媛MM”不需要为这些复杂的API 消耗心血呢?
API 网关就是这种机制。
假设 Dennis 公司引入了 API 网关以后,将 API 网关作为企业内部系统 API 的统一接口。所有的查询操作在apigw.Dennis.com/query.aspx进行,输入参数 method 决定了查询的内容,而下一个参数为查询键值。
API 网关根据 method 的不同,去实际对应的业务系统里查询数据。这样一来,实际上是对各个业务系统的 API 做了一层统一的封装。
这样,当某个系统的接口或域名修改之后,只需在 API 网关上进行对应的变更即可,不需要其他对接的业务应用修改代码,挽救了开发同学们开始后移的发际线。
当然,API 网关的功能还有许多,让我们举一个栗子:
有一天,从报销系统和其他系统去往 finance.Dennis.com 的查询量过大,导致服务器忙不过来,出现了 http 504 错误(前端 nginx 无法和后端 tomcat 连接,一般是 tomcat 挂掉了)。
此时,有个5个亿的投标需要在 finance.Dennis.com 上进行利润测算和申请价格。大家的心情可想而知。
在没有 API 网关的时候,SMS 的应用层,本身是没有办法区分哪些请求来自关键业务,哪些请求来自非关键业务,除非在程序代码中做修改——这又增加了大量的开发验证工作量,并且每次变更都需要重新修改验证。
而在企业 IT 系统引入 API 网关后,我们可以将这种限流工作交给 API 网关来实现,运维同学们只需要在 API 网关上设置好限流策略,就可以保证关键业务的可用性(Availability)。
如上图所示,finance 业务的性能只能扛住 1000QPS。而关键业务请求一般不超过400QPS,非关键业务请求的压力有可能到 2000QPS 以上。为了防止非关键业务请求把 finance 业务打死,运维同学们可以对非关键业务请求做限流,让这些请求的性能限制到 500QPS,给关键业务留下 1000-500=500QPS 的性能,防止着急投标的时候无法完成申请价格和测算利润等关键动作。
限流功能可以对业务系统提供基本的 QoS 保障,但功能过于刚性,只要超出了限流的 QPS 数,就会有一部分用户看到的是 http 500 或 http 502 页面。如果某个业务的关键性没有那么强,有没有办法让系统动态调整业务承载能力,实现容纳更多用户呢?
答案是肯定的。API 网关可以检查对后端的业务请求是否成功。如果后端出现 http 5xx 这样的错误信息,说明后端业务忙不过来了,API 网关会让它休养生息一会儿——这叫做熔断。
如图,假设运维同学在 API 网关上设定的熔断策略为:
当 HTTP 5xx 响应超过5%时,对业务进行熔断,3秒钟后恢复。
在某个时刻,有较大的突发访问请求被 finance 业务的 apached 前端接收,但后端的 tomcat 负担过重,无法及时响应来自 apached 前端的请求。使得 apached向 API 网关返回 HTTP 5xx 错误。API 网关发现,来自 finance 业务的 HTTP 5xx错误率上升到熔断阈值5%,执行熔断策略,对于所有指向 finance 业务的请求暂时返回 HTTP 5xx,从而保护 finance 的 tomcat 后端不被彻底打死,得以休养生息。3秒钟的熔断时间过后,finance 业务又可以重新营业了。
显然,熔断机制对业务的保护效果是显而易见的。但是,熔断本身是一种简单粗暴的保护,在业务熔断期间,所有用户见到的是这个业务不可用(如 HTTP 5xx 错误)。有没有颗粒度更细的保护方式呢?
答案是肯定的。
让我们穿越回童年的记忆:电视机偶尔能收到邻居家小霸王学(you)习(xi)机发射的无线信号,但由于房屋结构的遮挡和衰减,画面往往是黑白的……
这实际上就是PAL彩色制式的一种服务降级机制:在信噪比恶劣的情况下,牺牲颜色来保障画质的还原。
那么,我们如果引入这种服务降级机制,也可以通过牺牲业务质量,在业务峰值期间让更多用户能够使用基本服务,而不是面对着 HTTP 5xx 的错误页面不知所措。
怎么样可以让 API 网关支持服务降级功能呢?
这超出了 API 网关这个产品的能力范畴,在以后我们将解答这个问题。
当然,实际上 API 网关不仅限于提供限流、熔断、性能监控、性能告警等 QoS 相关的功能,还可以提供统一的鉴权功能,保护信息的机密性( Confidentiality )和完整性( Integrity ),而不需要每个业务自己去重新实现一遍这些控制逻辑。
微服务本身是一个非常大的话题,展开可以写一本书。还是让我们用一个栗子来简单直接地让大家对微服务有基本的认知吧:
Dennis 开发了一个交友 APP,叫做“探陌”,一开始,探陌采用传统的 Web-APP-DB 前后端三层架构实现,但随着功能的不断迭代,Dennis 发现了两个问题:
- 探陌的代码不断膨胀,每次重新构建需要(也就是程序员们发呆)的时间越来越长;
- 探陌一开始使用了 JAVA 语言开发,如果期望引入其他语言(如 Python,Golang 等)编写的开源组件几乎不可能;
因此,Dennis 梳理了探陌的功能:
- 编辑个人资料;
- 发布自己的图片及语音;
- 查看并按条件筛选附近的人;
- 查看和搜索附近的人发表的内容;
- 与关注的人文字、语音或视频聊天;
- 保存并查找聊天记录;
并且对这6个功能使用的共同的组件进行了抽象化,将涉及的后端 APP 从一个运行在 tomcat 里面的单体 java 程序拆分为若干组件:
名称 | 功能 | 实现语言 |
---|---|---|
user_search | 查看筛选附近的人 | Golang |
chatting_txt | 发送接收聊天内容 | java |
chatting_mm | 语音视频等多媒体聊天 | java |
searching | 搜索 | java |
recording | 聊天记录保存 | Golang |
user_match | 用户匹配推荐 | Python |
image | 图片显示与拉取 | node.js |
…… |
各个组件之间使用 Rest API 或 gRPC 进行通信。
我们发现,这样一来,不但让 APP 的设计遵循了“低耦合,高内聚“的原则,还可以让不同的组件用最适合的语言编写(如利用 java 开发的 elasticsearch 中间件快速实现搜索功能)
实际上,Dennis 实现的就是将“探陌“这个 APP 进行了微服务化,上表中的各个组件就是所谓的微服务,从前 APP 进程内的各种调用,大部分演化为微服务之间的远程调用。
如前文所述,对于这种情况,使用 API 网关不但能够统一调用入口,减少了某组件修改 API 接口后,其他组件需要同步配合修改的工作量,还能够实现统一的鉴权、性能监控、性能告警和 QoS。
然而,在微服务的时代,API 网关的这些功能还不够。
Dennis 问方老师:为什么这么说呢?
方老师随手给 Dennis 分享了一个链接,Dennis 点开,是李上安的《孤独城市》。
……
看着一望无际心中的炽热
就在川流不息的城市困惑
你像节奏生活之下的空壳
疑惑 挥霍 疑惑 挥霍
你要的明天只是种平坦的生活
何必说被掏空幻想的寄托
抛弃了初心在违背意愿的生活
谁能快活 谁能快乐
……
Dennis 猛然坐起
“抛弃了初心在违背意愿的生活 谁能快活 谁能快乐“
Dennis 明白了。
原来,将 APP 微服务化的初心,是因为原有的单体 APP 太过于重量级,任何修改都需要反复的验证测试,难以适应互联网时代敏捷迭代的需求。
应用微服务化以后,可以对其中一个组件做修改,其他组件无感知。在开发测试环境中做简单的测试后,在特定的条件下,就可以发布到生产环境了。
所谓的“特定的条件下“,实际上指的是,微服务的容器化部署!我们知道,容器是超轻量级的计算资源分配方案,能够将 APP 及所依赖的运行环境打包为镜像,剥离掉厚重的 Guest OS,实现快速部署、快速弹性扩容、各种灰度方式发布迭代(如蓝绿发布,滚动发布,金丝雀发布等)。
因此,微服务框架还需要支持负载均衡(将访问分发到各个微服务实例运行的容器)、服务注册发现(让迭代后的微服务能够自动化向微服务 API 网关更新注册变更后的 API )、服务部署平台(发布机制和租户资源治理等)……这样才能更好地支撑运行在容器上,利用 DevOps 流水线开发的微服务化应用。
早期最流行的微服务框架是 SpringCloud,SpringCloud 的微服务注册中心叫Erueka Server,开发者可以利用 Erueka Client 实现微服务注册。由于并不是所有编程语言都有 Erueka Client 客户端的实现,使用 SpringCloud 事实上限制了编程语言的使用。
而 Istio 是另一类微服务框架。Istio 在运行了微服务的容器上插入一个监听代理(sidecar),利用 Sidecar 实现熔断、限流等功能。由于这种方式无需改动原有程序代码,被叫做非侵入式微服务框架。这也是未来微服务演变的趋势。
最后,让我们做一个小结:
由于基于传统 IaaS 开发部署的企业内部应用之间,中间件及数据层是割裂的,它们之间的 API 调用关系复杂,一个应用的 API 更新会影响其他应用的正常运行,因此,出现了 API 网关对应用 API 进行封装,为各个应用之间调用提供统一的入口;
对于各种应用逐渐复杂的情形,API 网关可以提供 QoS 机制,如限流、熔断、性能监控等功能,保障关键应用的服务质量,还可以提供统一的认证鉴权保障应用的安全性;
当厚重的单体应用被拆分为容器化的微服务后,API网关进化为微服务框架。它除了提供 API 网关的 API 封装、QoS、统一鉴权认证等功能外,还可以实现 API 的自动化注册、负载均衡、性能监控与弹性伸缩、服务发布部署等功能。
由于以 springcloud 的侵入式微服务框架对开发者有特殊要求,甚至需要修改部分已有应用的程序代码,业界出现了以 Istio 为代表的非侵入式微服务框架,它向运行了微服务的容器 pod 上植入 sidecar 实现微服务框架的数据平面功能,这将是未来微服务的发展方向。