博客
2016 年 5 月 19 日
精简我们的堆栈
作者:阿瑟·诺盖拉·内韦斯和戴维·拉德克利夫
你好,Ruby 社区:
今天,我们要宣布一个好消息:我们无需再使用 Redis 来运行 RubyGems.org,并且我们正在移除 Nginx SPOF(单点故障)。我们已安装了一套全新的下载统计系统,并且关闭了我们的 Redis 实例。
Redis 不好吗?
如果使用得当,它一点也不差!许多公司都在使用 Redis,并且它还有许多有效的用例。Redis 的主要用例之一是将它用作后台任务的队列。我们使用 PostgreSQL 和 DelayedJob 来处理后台任务,所以我们不需要 Redis 来做这个。我们的问题来自我们使用它以及存储数据的方式。我们以永久非过期方式存储所有数据,并且存储和内存都增长了不少。我们在使用 Redis 时遇到的主要问题是将数据加载到运行实例所需的内存。这是资源密集且耗时的。如果有一个实例因任何原因宕机,它需要重新加载所有的内容到内存中,因此要使其再次可用需要很多分钟的时间。我们的部分 Redis 故障导致我们花了大约 45 分钟才恢复它的运行。在 Redis 中没有存储大量数据时(比如后台任务),这通常不是一个问题。
我们之前的统计架构
我们主要使用 Redis 来进行宝石下载计数。这个架构多年来一直是这样的:
- 每次下载一个宝石时,请求会首先通过我们的 Nginx 负载均衡器。
- 在将用户重定向到我们的宝石 CDN 之前,Nginx 会向一个内部服务发送一个辅助的 http 请求,其中包含宝石信息。
- 这个内部服务是一个小型 C 后端,用于解析请求以及增加 Redis 中的计数器。
- 显示在各种 RubyGems.org 页面和 API 响应中的计数器正是来自 Redis。
正如你所想的那样,RubyGems.org 收到了很多下载请求。我们希望直接从我们的 CDN 提供下载,但我们需要请求先击中 Nginx 才能跟踪这些下载。此外,如果这个架构的任何部分宕机,我们将会永久丢失下载计数。
我们现在使用什么
我们现在可以通过我们的 CDN 提供商 Fastly 直接提供所有宝石下载。以下是我们新的架构:
- 宝石下载请求击中 Fastly,在这里宝石可能已经根据地理位置缓存,并且离用户很近。
- Fastly 每五分钟生成一个日志文件,并将其推送到 S3。
- S3 向队列推送 SQS 通知消息。
- RubyGems.org Rails 应用程序使用这些 SQS 消息并计划一个后台作业来处理每个新的 S3 文件。
- DelayedJob 运行后台作业并更新 PostgreSQL 中的计数器。
- Rails 应用程序现在可以使用直接来自 PostgreSQL 数据库的计数器。
新架构的缺点
- 计数器每五分钟仅更新一次。不过,这是一个小小的代价,它允许我们执行更多的缓存。
新架构的优点
- 我们可直接从边缘位置的 CDN 为 .gem 下载提供服务。
- 永久数据存储的数量从 2 个减少至 1 个,这对我们的服务弹性来说是一项巨大的进步。
- gem 下载不再需要 Nginx,从而消除了 SPOF。
- 我们的堆栈变得更加简化。
- 更小的停机窗口。
- 更简单的本地开发设置。
- 如果需要,我们可以暂停后台处理或甚至重新统计下载的数量。
未来计划
我们在撰写此文时仍通过 Nginx 为 gem 提供服务,并重定向到 Fastly。随着我们完成向 Fastly 的完全过渡,这将在近期内得到更改。期待另一篇博文来解释这种过渡。
链接
- https://github.com/rubygems/rubygems-infrastructure/issues/35
- https://github.com/rubygems/rubygems.org/issues/1208
- https://github.com/rubygems/rubygems.org/issues/1089
- https://github.com/rubygems/rubygems.org/pull/1176
特别感谢
Aaron Suggs、David Radcliffe、Arthur Neves,感谢他们在这个项目中辛勤工作。