Netflix 宣布了通用 API 网关 Zuul 的架构转型。Zuul 原本采用同步阻塞架构,转型后叫作Zuul 2,采用异步非阻塞架构。Zuul 2 和Zuul 1 在架构方面的主要区别在于,Zuul 2 运行在异步非阻塞的框架上,比如Netty。Zuul 1 依赖多线程来支持吞吐量的增长,而Zuul 2 使用的Netty 框架依赖事件循环和回调函数。
InfoQ 对负责这次转型的 Netflix 项目经理 Mikey Cohen 进行了采访。
InfoQ:你把 Zuul 2 的转型描述成了一个旅程,而且它看起来像是一个长途旅程,你能说一下这个旅程的动机和概况,以及为什么需要这么长时间吗?
Mikey Cohen:我们希望构建一个可以为持久连接设备横向扩展的系统,这就是转向 Zuul 2 的主要动机。8300 多万用户,每个用户持有多个设备,在系统横向扩展方面,我们面临着一个巨大挑战。基于持久连接,我们可以实现推送,还可以支持从用户设备到云控制系统之间的双向通信。我们还可以使用推送通知取代从用户设备发出的轮询请求。轮询请求在用户设备访问微服务方面扮演了重要角色。使用推送可以降低通信成本,还能提升用户体验。除此之外,基于持久连接和推送机制可以开发出很多用户体验更好、更易于调试的功能特性。最后,我们会支持更多协议,比如 websocket 和 http/2。
实际上,这确实是一个旅程。如果我们在构建 Zuul 2 时脱离实际,那么它就不会是一个值得纪念的旅程。这次转型的复杂性是巨大的。运行在 Zuul 上的云网关是 Netflix 云服务的主要入口。我们支持超过 1000 种设备,这些设备在功能配置、使用限制方面也是千差万别。Zuul 需要支持这些设备的各种古怪行为,并把它们规范化。例如,有些设备关心消息头的顺序,有些对消息头有大小限制,有些不支持块编码,而且它们的一些特性在 Tomcat 和 Netty 里的处理方式不一样。这个项目的一个巨大挑战是找出这些差异,并做到让它们兼容。大部分这类问题只有在真实环境里才会暴露出来。要想在不影响生产环境用户的情况下检测出这类问题,需要承担巨大的风险,同时对减小风险影响范围的工程技术来说也是一个巨大考验。一个失效就会让我们 8300 多万用户中的一部分人或所有人无法消费 Netflix 的内容。
更深入一点看我们的架构,网关的所有特性和平台基础架构需要运行在异步环境里。Netflix 的所有平台基础架构,包括 Zuul 过滤器,都是基于同一个假设而构建的,那就是它们会被运行在阻塞式的环境里。跟请求相关的线程变量在我们的支持包和平台代码里无处不在。使用阻塞式 I/O 是这个平台的第二大特点。这些设计概念在异步非阻塞的架构里无法正常工作。识别出这些陷阱,并构建出基于异步非阻塞架构的可行方案是很耗费时间的,有时候会变得很困难。
作为 Netflix 网关的构成角色之一,尝试理解我们所看到的一切也是这个旅程的一部分。简单地说,这个角色主要负责路由、观察和规范化那些进入 Netflix 的请求。我们使用迂回的方式构建 Zuul 2,我们先移除 Zuul 1 的大部分业务逻辑,再把新的逻辑填充进原先的系统。除了让 Zuul 2 在网关里的角色更加明确之外,也简化了阻塞式代码的迁移工作,因为我们移除了大量代码。
创建 Zuul 过滤器和过滤解析异步化也一直是这次转型的另一个重大挑战。正如博文里所讲的那样,在 Zuul 2 之前我们就做了这方面的工作,所以我们可以在 Zuul 1 和 Zuul 2 上运行之前创建的 Zuul 过滤器(异步代码可以在同步环境里运行,反之则不行)。这样我们就可以基于这些过滤器继续开发网关的其它功能特性。不过在迭代过程中还是会对 Zuul 过滤器接口进行修改,并把这些过滤器串联起来。我们使用 RxJava 来串联 Zuul 过滤器。通过对 100 多个 Zuul 过滤器进行串联,我们发现这真是一个重注细节、耗费时间的任务。
最后,我们使用几个迭代来构建可以让 Zuul 2 运行起来的框架。我们从使用早期版本的 Netty 转到使用 RxNetty 项目。在构建 Zuul 2 的过程中,我们发现有些地方仍需要 Netty 的核心功能,而 RxNetty 却把它们移除了,所以在这些地方我们仍然使用 Netty。一路走来,伴随着学习和纠错,我们构建了一个越来越健壮的产品。
InfoQ:你在博客里提到,除了后端延迟、错误重试,多线程系统能够应对大部分场景。是否还有其它原因促使你转向新的架构?
Cohen:我们一度相信在 CPU 使用效率和弹性方面会得到大幅改进。在部分集群(不是全部)里,我们确实看到了效率方面的改进(10-25%),不过这似乎不值一提。未能准确估算网关 CPU 工作量,也未能准确判断那些 CPU 比异步 NIO 密集的场景,这些大概就是造成我们错误预期的原因。我相信,使用 Zuul 2 开源版本(在它发布之后)的人将要看到的性能方面的大幅度提升,这在很大程度上是因为我们在 Netflix Zuul 2 网关上所做的大部分工作都是跟 Netflix 整体架构相关的。很多人认为 10-25% 这样的性能提升已经是一个很大的收获。不过如果把所耗费的时间、资源,还有运营方面面临的挑战以及异步 NIO 系统引入的调式复杂性都考虑在内,那么在性能方面获得的提升跟所付出的工作量无法划上等号。这么说来,其它方面的成效,比如连接管理和推送通知,以及弹性的提升,对我们来说就更重要了。
我们期待在弹性方面有更大的提升。就像我在博客里提到的那样,我坚信它会变成现实,但天下没有免费的午餐。我们积极地采取各种措施来获得更多的弹性提升,比如减少实例化对象,记录异常信息,改变节流机制,重用连接,改进负载均衡算法等。
InfoQ:Zuul 过滤器是 Zuul 的关键组成部分。这次转型的切入点就是这些过滤器,对吗?你能否描述一下过滤器的重构过程,同时为那些使用类似方式重构大型系统的开发者们提供一些建议吗?
Cohen:把网关的业务逻辑部分异步化是有远见的想法,从长远来看,它会为我们节省很多时间。我们没必要为了保持一致性而维护两套过滤器(Zuul 1 一套,Zuul 2 一套)。在构建 Zuul 2 之前,我们准备了超过 6 个月的时间,这着实是一个巨大的前期投入。我想很多团队会认为这是本末倒置,但我们有理由相信这样做对我们有很大好处。首先,过滤器的开发可以在同一个代码库上进行,这样可以保证业务逻辑的一致性。既然我们知道业务逻辑要保持一致,那么就可以把不一致的部分移除掉。最后,我们可以借助这样的一个非常有用的工具,从系统和运营的角度来比较 Zuul 1 和 Zuul 2。
InfoQ:你有没有一些实战经验可以分享给开发者和架构师们,帮助他们完成从同步阻塞到异步非阻塞的架构转型?另外,RxNetty 是一个什么样的框架,它在这次转型过程中起到多大的作用?
Cohen:在转向异步系统的过程中,我们打了很多场仗。它们相互交织在一起,有着相同的主题和模式。在一开始,我们发现了资源泄露的问题:ByteBuf、信号量、文件描述符等。这类罕见的极端情况在我们的系统里却大量出现。通常需要几天时间的调试才能找到问题的根源。产生问题的根源多种多样,不过大部分都跟错误事件丢失有关。在打仗过程中,我们构建了一些工具,并尝试把它们贡献出来。我们向 Netty 贡献了一个可插拔的资源泄露探测器,有了这个插件,就可以使用监控工具检测资源泄露。我们还总结了一些模式,并把它们分享出来,遵循这些模式可以帮助我们更快地解决问题。
我们打的这些仗不是相互孤立的。我们构建的测试框架止步于此,因为很多问题只有在生产环境里才会出现。生产环境有大规模的用户和真实的系统,会暴露出大量问题。变更会带来巨大风险,一旦网关崩溃,用户就无法使用系统。转型越快意味着经历得越多,不过对用户的影响也越大。相反,以保守的速度转型意味着经历是循序渐进的,对用户的影响也较小。所以,对 Zuul 2 来说,在高风险的快速转型和保守的转型之间,有着微妙的平衡关系。
InfoQ:你说自己有意避开测试基准。既然异步和非阻塞都跟性能相关,那么你能告诉我们一些整体性能改进的细节吗?
Cohen:关于 Zuul 1 和 Zuul 2(阻塞和非阻塞)之间的性能比较问题,我想我们在连接扩展性能上取得了大幅度的改进,不过吞吐量的提升受到 CPU 密集型任务的限制。这些任务主要包括收集度量指标、记录日志、分析数据、加密和压缩。不过在把 Zuul 2 开源以后,我相信在移除了这些任务的实现版本上可以看到吞吐量的重大提升。
InfoQ:你能具体说一下 Zuul 2 的开源计划以及 Zuul 的路线图吗?
Cohen:我们一直在致力于 Zuul 2 的开源工作,并计划在今年年底发布。我们还计划增加一些新的功能,比如对 websocket 和 http/2 的支持。从路线图来看,我们即将对外开放部分过滤器、路由工具和设计理念。把 Netflix 特定的逻辑梳理出去是这个工作的主要内容。既然我们开发了 websocket 和推送功能,我们也想把相关的经验和基础架构也开源出来。
Zuul 2 文档还在准备当中。Zuul wiki 上有 Zuul 的相关信息,它还告诉我们如何开始使用 Zuul。
查看英文原文: Netflix Zuul Gets a Makeover to a Asynchronous and Non-Blocking Architecture
感谢夏雪对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ , @丁晓昀),微信(微信号: InfoQChina )关注我们。
活动推荐:
2023年9月3-5日,「QCon全球软件开发大会·北京站」 将在北京•富力万丽酒店举办。此次大会以「启航·AIGC软件工程变革」为主题,策划了大前端融合提效、大模型应用落地、面向 AI 的存储、AIGC 浪潮下的研发效能提升、LLMOps、异构算力、微服务架构治理、业务安全技术、构建未来软件的编程语言、FinOps 等近30个精彩专题。咨询购票可联系票务经理 18514549229(微信同手机号)。
评论