当前位置: 首页 > 科技观察

最大的技术重构:数据库连接数从15000到不到100

时间:2023-03-18 00:51:07 科技观察

长什么样子?信用评分。这是他们衡量一家公司有问题的过去以及他们背负着什么样的包袱的方式,我们对技术包袱并不陌生。作为管理自己的服务器和硬件的云提供商,我们面临许多复杂的问题其他初创公司不会面对云计算的新时代。这些困难的情况最终迫使我们在存在的早期做出权衡。正如任何快速发展的公司所知道的那样,早期做出的技术决策往往会在以后赶上你。看着桌子对面的新员工,作者深吸一口气说,“让我给你讲讲我们的数据库有1500个直接连接时的故事……”我给新员工讲的故事是DigitalOcean迄今为止的最大永远不要进行技术重建。全公司为之奋斗了多年,从中学到了很多东西。我希望这个故事能帮助其他正在与技术债务作斗争的开发人员。一切从哪里开始从一开始,DigitalOcean就一直痴迷于简单性。这是其核心价值观之一:力求简单而优雅的解决方案。这不仅适用于我们的产品,也适用于我们的技术决策。这在原始系统设计中表现得最为明显。与GitHub、Shopify和Airbnb一样,DigitalOcean于2011年作为Rails应用程序启动。Rails应用程序(内部称为Cloud)管理UI和公共API中的所有用户交互。帮助Rails的是两个Perl服务:Scheduler和DOBE(DigitalOcean后端)。Scheduler计划并将Droplet分配给管理程序,而DOBE负责创建实际的Droplet虚拟机。Cloud和Scheduler作为独立服务运行时,DOBE运行在机群中的每台服务器上。Cloud、Scheduler和DOBE都不能直接通信。它们通过MySQL数据库进行通信。该数据库有两个用途:存储数据和安排通信。这三个服务都使用数据库表作为消息队列来传递信息。每当用户创建新的Droplet时,Cloud都会将新的事件记录插入队列中。调度程序每秒持续轮询数据库以获取新的Droplet事件,并安排这些事件在可用的管理程序上创建。最后,每个DOBE事件都会等待新的预定Droplet被创建并完成任务。为了让这些服务器检测到任何新的更改,它们都需要轮询数据库以获取表中的新记录。在系统设计方面,无限循环和让每台服务器直接连接到数据库,这可能是最基本的、简单的,而且效果很好——尤其是对于人手不足、面临紧迫的最后期限和快速增长的用户群的技术团队。四年来,数据库消息队列构成了DigitalOcean技术堆栈的支柱。这段时间,我们采用了微服务架构,内部通信用gRPC代替HTTPS,后端服务用Perl代替Golang。然而,条条大路还是通向那个MySQL数据库。重要的是不要仅仅因为某些东西旧了就认为它不正常并且应该更换。Bloomberg和IBM拥有用Fortran和COBOL编写的遗留服务,这些服务产生的收入远远超过整个公司。另一方面,每个系统都有比例限制。我们需要面对它。从2012年到2016年,DigitalOcean的用户流量增长超过10000%。我们已将更多产品添加到我们的目录中,并为我们的基础设施添加了更多服务。这会增加数据库消息队列中传入的事件量。对Droplet的需求增加意味着调度程序加班加点地把它们全部分配给服务器。不幸的是,对于Scheduler,可用服务器的数量不是固定的。为了跟上不断增长的Droplet需求,我们添加了越来越多的服务器来处理流量。每个新的管理程序都意味着与数据库的另一个持久连接。到2016年初,该数据库有超过15,000个直接连接,每个连接每1到5秒查询一次新事件。如果这还不够糟糕,每个管理程序用来获取新Droplet事件的SQL查询也变得越来越复杂。它已经变成了一个包含18个表的150多行的巨人。它既令人印象深刻又不稳定,因此难以维护。不出所料,正是在这个时期出现了裂缝。一个单点故障和成千上万个依赖共享资源的竞争不可避免地会导致一段时间的混乱。表锁和查询积压会导致中断和性能下降。而且由于系统中的紧密耦合,没有一个明确或简单的解决方案。Cloud、Scheduler、DOBE都是瓶颈。只修补一两个组件只会将负载转移到其余的瓶颈上。于是,经过反复考虑,工程师们想出了一个三管齐下的整改方案:减少数据库的直连数。重构调度器的排序算法以提高可用性。一个数据库解除了它的消息队列责任。开始重构为了解决数据库依赖问题,DigitalOcean工程师创建了EventRouter。事件路由器充当区域代理,代表每个数据中心中的每个DOBE实例轮询数据库。不再有数千台服务器各自查询数据库,而是只有少数代理执行查询。每个事件路由器代理将获取特定区域中的所有活动事件,并将每个事件委托给相应的管理程序。事件路由器还将巨大的轮询查询分解为更小、更易于维护的查询。EventRouter上线后,数据库连接数从15000多条减少到不到100条。接下来,工程师们将目光投向了下一个目标:Scheduler。如前所述,Scheduler是一个Perl脚本,它确定管理程序将负责创建哪些Droplets。它通过使用一系列查询对服务器进行排名和排序来实现这一点。每当用户创建一个Droplet时,调度程序都会用最好的机器更新表行。虽然听起来很简单,但Scheduler有一些缺陷。其逻辑复杂,难以处理。它是单线程的,在流量高峰期间性能会受到影响。最后,只有一个Scheduler实例,它必须为整个机队服务。这是不可避免的瓶颈。为了解决这些问题,工程团队创建了SchedulerV2。更新后的调度程序彻底改革了排名系统。它不是在数据库中查询服务器指标,而是从管理程序中聚合它们并将它们存储在自己的数据库中。此外,调度程序团队通过并发和复制使他们的新服务在负载下工作。EventRouter和SchedulerV2都是伟大的成就,解决了许多架构上的失败。尽管如此,还是有一个明显的缺陷。截至2017年初,中心化的MySQL消息队列仍在使用,甚至很忙。它每天处理多达400,000条新记录,每秒更新20次。不幸的是,删除数据库的消息队列并不是一件容易的事。第一步是防止服务直接访问它。数据库需要一个抽象层。它还需要一个API来聚合请求并代表它执行查询。如果任何服务想要创建一个新事件,它需要通过API来完成。因此,鱼叉诞生了。获得质押所需的时间比您想象的要长,但是构建事件队列的接口是比较容易的部分。事实证明,让其他球队购买股份更加困难。与Harpoon集成意味着团队不得不放弃对数据库的访问,重写部分代码库,并最终改变他们一直以来的工作方式。这并不容易。一个团队一个团队,一个服务一个服务,Harpoon工程师成功地将整个代码库迁移到他们的新平台。大约用了一年时间,但到2017年底,Harpoon成为了数据库消息队列的唯一发布者。现在真正的工作开始了。完全控制事件系统意味着Harpoon可以自由地重新设计Droplet工作流程。Harpoon的首要任务是将消息队列职责从数据库抽象到自身。为此,Harpoon创建了自己的内部消息队列,由RabbitMQ和异步工作站组成。当Harpoon将新事件推入队列的一侧时,工作站将它们从另一侧拉出。由于RabbitMQ取代了数据库的队列,工作站可以自由地直接与调度程序和事件路由器通信。因此,Harpoon没有使用SchedulerV2和EventRouter轮询数据库以获取新更改,而是将更新直接推送到数据库。截至2019年撰写本文时,这就是Droplet事件架构所在的位置。在过去的七年里,DigitalOcean已经从最初的GarageBand发展成为如今的知名云提供商。与其他转型科技公司一样,DigitalOcean定期处理遗留代码和技术债务。无论是打破单体、创建多区域服务,还是消除单点故障,DigitalOcean工程师始终致力于打造优雅而简单的解决方案。