从 API Gateway 的职能,看 Web 架构的演进

本文根据自身过往的工作经验,总结了从初创公司到大型企业的 Web 服务架构演进,这些经验来自于在不同公司、不同项目的复杂程度中的实践。这些架构来源于工作中的经验实践,可能并不完整,也可能存在疏漏或者不正确的地方。欢迎讨论和交流,以便我们共同学习和进步。

单体架构,好用的瑞士军刀

一个新的项目或者说一个初创公司中,最常见的 Web 架构则是单体架构(Monolithic Architecture)。现在在公司的不少的小团队中仍旧能够看到它的身影。顾名思义,在这个架构中,所有的功能和组件都打包成一个单一的,紧密集成的程序,并运行在一个进程空间中。严格上这里没有 API Gateway 这一层服务,所有的请求都会通过 NGINX 或者 Apache 的反向代理来进行转发到同一个 Server 上面。

单体架构有以下特点:

  1. 组件间紧密集成:在单体应用中,各个功能和组件都是紧密集成的。它们可能通过函数调用,库引用等机制进行交互。
  2. 部署简单:由于是一个单体的应用或服务,所以部署通常比较简单。只需要启动一个应用或者服务即可,在早期虚拟化技术还未出现的时刻,简单的部署和启动降低了工程的复杂度。
  3. 扩展性问题:随着应用规模和复杂性的增长,容易出现巨石应用,应用变得难以维护和扩展。特别是当需要水平扩展时,我们只能把整体应用部署到多个服务器中,而不是部署遇到瓶颈的部分。
  4. 故障隔离:由于所有的功能都在一个应用中,所以一个组件的故障可能会导致整个应用崩溃,影响整个应用的整体性。
  5. 技术栈限制:在单体应用中,通常使用一种技术栈或者框架。如果需要引入新的技术栈或者框架,往往需要进行整体重构。
  6. 持续集成/持续部署:随着应用的复杂度增加和体积增大,构建,测试和部署可能会变得更加缓慢。

单体架构在早期的互联网公司中一直都非常流行并且长时间占据主流地位,因而早期我们经常能听到把一个服务部署到 48C,48G 的服务器上面。不过随着容器化技术的兴起,这一类巨石应用逐步消失在历史的长河中,我们现在更多的是听到把一个服务部署到 2C,2G 的服务器上面,然后通过水平扩展以支撑更多的流量和服务。

微服务雏形,拆分巨石应用成为多个小型服务

针对巨石应用遇到的种种问题,我们对巨石应用按照功能进行了拆分,这通常是根据业务逻辑或数据的独立性来判断的。例如,用户管理、订单处理、库存管理等都可以被视为独立的服务。接下来,我们需要设置一个服务网关,如 NGINX,来处理所有的请求和响应。服务网关的主要职责是根据请求的路径(path)将请求转发到相应的服务。例如,所有以/user 开头的请求都被转发到用户管理服务,所有以/order 开头的请求都被转发到订单处理服务。

这一种方式对原来的单体应用进行了拆分,根据不同的功能做了对应的隔离,降低了应用的复杂度和维护成本,增加了应用的可扩展性和健壮程度,一定程度上解决了之前单体应用带来的问题。

但是这种 Web 架构也有它自身存在的一些问题。

  1. 客户端到微服务直接通信,Server 和客户端之间的强耦合。后续 Server 的迭代升级困难,需要考虑老旧应用的兼容性问题。
  2. 一个应用的部分功能往往涉及到多个服务,需要进行多次请求,然后在客户端层面做数据聚合的工作,客户端的工作量增大,而且也会因为请求数量的增多导致延迟升高。通常来说我们应用如果想要做到快速迭代的话,都是要做到前轻后重的,前端主要处理 UI 展示逻辑,就快迭代速度。如果前端的逻辑非常多,会影响到业务的交付速度。
  3. 不利于协议的统一,因为客户端与服务器直接通信的缘故,服务器需要做多端的适配,一个功能针对客户端和网页端需要提供不同的 API,API 对接的复杂度上升。
  4. 客户端的兼容耦合到了内部服务,使得服务端需要处理复杂的兼容逻辑。
  5. 统一的逻辑无法收敛,例如鉴权认证,限流熔断等,分散在各个 Server 中非常难以处理。

BFF,救星的出现

为了解决上述问题,我们遵循互联网工程的首要原则,即如果无法解决问题,就在其上添加一层。因此,我们在中间添加了一个隔离层:

通过新增一层服务直接面向客户,把 Server 和客户端之间进行了隔离。我们在这一层服务里面做数据的聚合和处理,提供使用前端业务场景的 API 和数据。底层的微服务之间则使用同意的协议进行数据出口,只需要提供粗粒度的 API。面对前端和客户端,Node.js 具有天然的优势,因而 Node.js 的生态也伴随着这种业务架构流行起来。这一层服务则是我们今天所熟知的 BFF (Backend for frontend)层。

加入了 BFF 之后有许多优势:

  1. 交互轻量:每个服务只需要考虑内网接口,同时提供粗粒度的 API。协议上面进行了精简。
  2. 解耦和快速迭代:数据裁剪和聚合的工作放在了 BFF 层,针对终端用户进行 API 定制化处理。底层微服务不需要考虑终端的场景,可以快速迭代和动态升级,只需要保证原有接口和系统的兼容性。
  3. 沟通效率的提升,协作模式演进为 Web Server + Core Server。

BFF 的拆分,增加应用的健壮性。

使用一个单一的 BFF 层作为唯一的出口存在着一个无法避免的问题,就是容易导致单点失败。为了解决这个问题,我们对 BFF 按照业务场景进行拆分,拆分成不同的 BFF,由不同的团队负责,例如 Order BFF,User BFF,Item BFF。

很多横向的功能逻辑,例如安全认证,日志监控,限流熔断等,可以进行聚合层一个新的网关层,这里我们在引入一个新的 API Gateway 网关层,把路由,认证,限流,安全等通用的横向功能全部上沉其中。

API Gateway 扮演着至关重要的角色,它实现了单个 BFF 的解耦和分离,使得每个业务线团队能够独立开发和交付自己的微服务。通用的横向逻辑从 BFF 转移到了网关上,使得 BFF 的开发人员能够更专注于业务逻辑的交付,实现了关注点分离(Separation of Concerns)。

实际的业务流量也演变成了:

客户端/浏览器 -> NLB -> API Gateway -> BFF -> Microservice

根据康威定律,组织结构会影响软件架构,软件架构反映了组织结构。在这个阶段,工程团队通常会被拆分为:网关团队(Gateway Team)、BFF Web 服务器团队(BFF Web Server Team)和微服务核心团队(Core Server Team)。

总结

这个世界没有银弹,每种架构的出现和存在都有其特定的优点和缺点。盲目地选择最复杂的架构并不总是最佳选择。根据业务的复杂度和场景,以及团队的人力资源和工程能力,选择最适合的架构才是最优解。随着业务的增长,我们应逐步演进技术架构,选择最适合当前情况的架构才是最佳选择。

相关推荐
弥琉撒到我2 小时前
微服务swagger解析部署使用全流程
java·微服务·架构·swagger
奔跑吧邓邓子6 小时前
大数据利器Hadoop:从基础到实战,一篇文章掌握大数据处理精髓!
大数据·hadoop·分布式
2401_857622667 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
2402_857589367 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
哎呦没9 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch9 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
杨哥带你写代码10 小时前
足球青训俱乐部管理:Spring Boot技术驱动
java·spring boot·后端
AskHarries11 小时前
读《show your work》的一点感悟
后端
weixin_4539650011 小时前
[单master节点k8s部署]30.ceph分布式存储(一)
分布式·ceph·kubernetes
weixin_4539650011 小时前
[单master节点k8s部署]32.ceph分布式存储(三)
分布式·ceph·kubernetes