基础设施与扩容第 4 部分:Valkey、队列和日志,先坏掉的安静部分
NovaCommerce 案例研究第 4 部分:2,854 个 Valkey 超时、一个仍是单点故障的 worker、延迟 merit 卡 115 分钟的队列、说不的 AI 提供商,以及计数建筑而不是学生的速率限制。
七个测量、一个真实考试晚上和一个 scale-in 错误:负载测试、容量模型和诚实 soak 计划如何塑造 NovaCommerce 最终架构、成本以及解决方案架构师清单。第 5 部分结束系列。
Author
Anichur Rahaman

Part 5 of 5
从单服务器到考试日就绪在 10 月 7 日晚上,661 名学生开始 NovaCommerce 上的一个考试,峰值 86 开始一分钟,439 个更多坐了第二考试在它之后。后端达到约 69 requests/s 的峰值并在二分钟平均上达到 47% CPU。数据库使用不到 25% 它的 CPU。没背景 job 失败。
然后一个池最小被降低在考试中间。一个服务 node 消失不排水,约 360 请求在两分钟内失败。
那晚的两半都是测试结果:冷静半来自在天前的短测试运行,丑陋半来自没有测试覆盖过还的东西。这部分按顺序走旅程(基准、负载测试、瓶颈、优化、重新测试、缩放、soak、最终结果),然后显示最终架构、它的成本,和我现在在每个评论中使用的清单。
这是五部分案例研究 "从单服务器到考试日就绪" 的第 5 部分。NovaCommerce 是虚构名称;架构、数字和错误都是真实的。
本系列中的每个改变来自一个循环:测量、找瓶颈、理解原因、改变设计、再测试、再测量。一个改变在原因被理解前造是一个猜测,一个猜测在一个服务器上发生每月花钱。想象一个有打结的花园软管:如果水弱,一个更大的水龙头什么都不做,所以你走软管直到你发现打结。我们的移动了三次,每次它是一个不同类型的问题。

第一件我们在生产测量不是 CPU。在 9 月 25 日,学生在考试期间得到 HTTP 429(太多请求)。API 速率限制器按 IP 地址计键,因为针对基于令牌学生的默认 auth guard 是空的。一所学校或移动运营商把数百学生放在一个 NAT IP 后,所以整个建筑共享一个桶。一个 429 是应用说不,不是服务器呼吸急促,所以没额外 node 能帮。Implemented:限制器现在按学生或讲师账户计键,仅客人按 IP。
在 10 月 6 日 02:28,后端负载测试返回了 2,854 应用 500,每一个 RedisException: Operation timed out 在连接时(5 秒 connect timeout)。原因:Valkey(4 GB,primary 和 standby)无法快速接受连接,每个请求打开它的新 TLS 连接,约 5.4 ms CPU 每个请求针对约 MySQL 的 3.3 ms。更多 web node 会仅打开更多连接到同一 Valkey。
Implemented:Valkey 在原地调整到 8 GB 两个 node(primary 和 standby),数据保留,主机不变。它花更多钱,它移除了真实故障。
重新测试:从 25 到 500 requests/s 的逐步爬升,开始两个后端 node 而池扩展出,给 0 应用错误和 0 后端 5xx。有 143 错误在 load balancer,但仅在 node 在 CPU 上饱和时。那是预期的 "超出容量" 信号,不是 bug,它显示我们每个 node 跑出。
后端测试也给了我们每个之后的决定倚靠的数字。在满 CPU,一个 4 vCPU / 8 GB 后端 node 服务约 65 到 70 requests/s 真实 API 混合,约 59 ms CPU 每个请求。数字同意:4 vCPU 是 4,000 ms CPU 每秒,4,000 除以 59 约 68。Autoscale 在 CPU 触及 99% 后添加第一个额外 node 约 7 分钟。
那作三个瓶颈按顺序:规则(限制器)、数据层(Valkey 连接),然后仅纯后端 CPU。每一个需要不同的修复,仅最后一个由更多服务器解决。
有前端重建作一个池(nginx 在两个相同的 Next.js 容器前每 node),两个 node 服务约 356 requests/s,p95 0.05 s,0 错误,在 55 到 60% CPU,我们对比了 12 真实页面针对旧服务器。余地付了自己:池从专用 CPU droplet 移到共享 AMD 2 vCPU / 4 GB droplet。Implemented。
在 10 月 5 日,从 21:17 到 21:33,k6 重放了真实考试峰值请求混合(仅 GET,什么都没写):300 到 450 requests/s,然后约 750 六分钟。
课程在时间。DigitalOcean 的 CPU 指标滞后真实负载 5 到 8 分钟,所以缩放开始迟,然后过冲:后端短暂扩展到 4 在负载停止后。第一个额外后端 node 到达测试开始后十二分钟,考试不等十二分钟。所以对定时考试我们 pre-scale,在一个大考试前提高池最小约 45 分钟,保持反应式 autoscaling 作安全网。流量峰值自动扩展 中的通用版本论证。
Tested,然后 fixed:我们第一个后端模板 rollout 产生了一个短 503 闪烁,因为 DigitalOcean 删除旧 droplet 而 load balancer 仍路由到它们。受保护的 rollout 仅改变镜像,等直到新 node 应答 /lb-health,等约 40 秒给平衡器接纳它们,排水旧 node 首先。10 月 7 日两个池的完整 rollout 在真实页面上每 2 秒运行一个正常运行时间探针:219 探针,0 错误。
这是我祝我在开始前看过的表。对每个问题测试或生产暴露,它问一个问题:更多服务器会修复它吗?
| 发现的问题 | 更多服务器? | 什么修复它 | 状态 |
|---|---|---|---|
| 后端 CPU 在 65 到 70 requests/s 每 node 饱和 | 是的 | Autoscale 池(2 到 10 node)加 pre-scaling | Implemented |
| 整个学校得 429(9 月 25 日) | 否 | 限制器按学生账户计键,IP 仅客人 | Implemented |
| 路由限制仍按 IP 计:74% 呼叫到一个仪表板端点返回 429 在考试(10 月 7 日) | 否 | 限制按学生每路由计;0 之后这样 429 | Implemented |
| 2,854 Valkey 连接超时(10 月 6 日) | 否 | Valkey 在原地调整到 8 GB x 2 | Implemented |
| Merit 卡(0.34 秒 job)等 10 到 115 分钟在 AI job 约 10 秒后(10 月 6 日) | 否 | AI job 的单独队列通道;merit 卡在约 1 分钟准备 | Implemented |
| 后端看前端每个学生一个 IP | 否 | 在一个签名头中通过真实学生 IP | Planned |
在六个问题,一个是容量问题。其他五个是两个规则、连接限制、队列布局和缺失头。数据层一侧这样模式覆盖在 负载下的数据层 中。
一个负载测试问系统能取多少。一个 soak 测试问它能取多久:一个引擎在完整转一分钟证明很少关于三个小时,因为缓慢问题显示上升晚(记忆爬升、连接泄漏、磁盘填充、队列漂移)。"我们 soak 测试了它" 容易说而难以防守,所以我将精确:我们还没运行一个正式 soak 测试。
| 证据 | 它覆盖什么 | 状态 |
|---|---|---|
| 短持续运行:25 到 500 requests/s 爬升、16 分钟 autoscale 测试在直到约 750 requests/s、2 秒正常运行时间探针通过 rollout | 分钟持续负载:0 应用错误在爬升、0 服务器错误在 autoscale 测试 | Tested |
| 10 月 7 日考试晚上:约两个小时、两个考试背对背 | 稳定 CPU,0 失败 job,一个缩减错误 | 在生产中观察 |
| 一个多小时 soak 一起下一个大负载测试,在一个同意维护窗口 | 记忆增长、连接泄漏、队列漂移 | Planned |
考试晚上是最接近我们有的,但它是观察,不是控制测试。一个相关的检查确实保有:后端 node 在 24 小时内向磁盘写 0 文件,所以磁盘不是 soak 风险那边。前端 node 仍保持约 440 MB nginx 日志一天本地,那是为什么 Fluent Bit 在前端在计划列表。
没什么替代真实学生。两个考试运行背对背,我们的一次每分钟的概要看他们:学生开始和提交、请求和 5xx 每 node、池 CPU、worker 负载和失败 job。
| 测量 | 结果 |
|---|---|
| 考试 A(20 分钟) | 661 开始,638 提交(96.5%);开始达到每分钟 86 |
| 考试 B(25 分钟) | 439 开始,425 提交(96.8%) |
| 前端 | 直到 1,741 唯一访问者在考试 A,峰值 403 在一分钟;约 732,000 请求从 6 到 8 pm;CPU 直到 24% |
| 后端 | 峰值约 69 requests/s(每分钟日志;63 在提供商二分钟平均);CPU 直到 47% 在二分钟平均;一个 node 在 86% 一分钟 |
| MySQL | CPU 最大 24.5%(平均 13.4%),最多 5 运行查询,0 锁等待 |
| Valkey | 12.5% 内存 |
| Worker | CPU 46%,0 失败 job |
三件事站出来。
在考试中间,某人降低了后端池最小。DigitalOcean 移除了一个服务 node 不排水,约 360 请求在两分钟内失败。早期的测试教了我们 scale-out 慢和晚。考试教了另一半:scale-in 快速而不说再见。Implemented 作 runbook 规则:在考试前提高最小,仅在后降低,永不信任 autoscale scale-in 排水一个 node。
在考试后我打开真实数据到一个小模型,所以下一个考试可以用算术而不是恐惧计划。它有四个测量输入:约 15 请求每学生当考试打开,然后约 1.5 一分钟(0.025 一秒);约 30 requests/s 基准流量;50 requests/s 作为一个后端 node 的安全负载(75% CPU);30% 为不均匀分割的边际。
用简洁词汇:取你将有的 node,乘以 50,除以 1.3 对于边际,减去基准流量的 30。剩下的是学生当最后一个加入可能使用。每个学生花他们 15 打开请求在加入窗口上展开,加 0.025 为现在。
取 4 个 node。4 x 50 / 1.3 约 154,减 30 留下约 124。如果学生在 10 分钟加入,每个花 15 / 600 + 0.025 = 0.05 requests/s,所以 124 / 0.05 给约 2,500 学生;表轮下到 2,400。如果他们全部在 2 分钟内加入,每个花 15 / 120 + 0.025 = 0.15,给约 800。
| 后端 node | 加入超过约 10 分钟 | 所有加入在约 2 分钟内 | 安全后端负载(衍生) |
|---|---|---|---|
| 2 | 约 900 | 约 300 | 约 77 requests/s |
| 4 | 约 2,400 | 约 800 | 约 154 requests/s |
| 6 | 约 4,000 | 约 1,300 | 约 231 requests/s |
| 8 到 10 | 5,500 或更多 | 1,800 到 2,300 | 约 308 到 385 requests/s |

真实晚上适配第一行:峰值 86 开始一分钟针对约 90 那行允许。模型停在哪里:
实心盒子今天运行;虚线条是计划。

sticky = true);Valkey 8 GB 在两个 node;PostgreSQL 与 pgvector(2 vCPU / 4 GB)保有分开,所以向量查询永不与考试写竞争。| 区域 | 之前(直到 10 月 5 日) | 现在 |
|---|---|---|
| 前端 | 一个 8 vCPU / 16 GB droplet,一个 Next.js 容器,没 load balancer | 负载平衡 2 到 10 个 node 的池,两个容器每个 |
| 后端 | 两个固定 droplet,按 ID 针对,所以新服务器无法加入 | 平衡针对标签;2 到 10 个池从一个快照,前 pre-scaled 考试 |
| MySQL | 一个 Advanced node,8 vCPU / 32 GB,没 standby | Standard 4 vCPU / 16 GB,primary 和 standby |
| "Standby" 服务器 | 两个关闭 droplet,$112 一月,分钟开启 | 移除;真实 standby 在 MySQL 和 Valkey 内(现在 8 GB,上升从 4) |
| 项目(月,DigitalOcean 列表价格) | 成本 |
|---|---|
| 之前:整个 web 层(16 GB 前端、2 后端、worker、1 load balancer、2 关闭 standby) | 约 $416 |
| 现在,web 层:2 后端(每个 $56)、2 前端(每个 $28)、worker、两个 load balancer | $112 + $56 + $56 + $48 |
| 现在,数据和日志:MySQL 对、Valkey 8 GB x 2、PostgreSQL、OpenSearch | 约 $389 + $240 + $60 + $20 |
| 现在:快照、Spaces | 约 $5 + $5 和使用 |
| 现在:总生产,后端池在 2 个 node | 约 $990 |
总值不相同:$416 是 web 层仅,$990 是整个生产堆栈。在同样列表价格 web 层单独现在约 $272。其他 $709 是托管 MySQL、Valkey、PostgreSQL 和 OpenSearch,那是额外钱买的:一个在 node 故障后活的数据库、有房间的 Valkey 和 standby、给考试写保持出的方式向量搜索、和活得比删除 node 久的日志。Pre-scaling 是便宜部分:额外后端 node 花约 $0.08 一小时。数字背后的决定在下面的清单。
这是我现在在架构评论中使用的清单。每项是这个项目做或付的。
/lb-health 上的 503)在移除前。当这个系列开始时,平台是一个前端 droplet、两个后端 droplet 按 ID 固定和一个大数据库 node。10 月 7 日考试晚上在不同系统上运行,没有差异来自为自己的缘故添加服务器。它来自同一循环,再一次,包括时间的测量告诉我们我们错了。
测量。找瓶颈。理解它。改变一件事。测试。再测量。
那循环是架构;池、standby 和图表是它留后。如果你从这五部分保持一件事,保持循环,在你下一个考试晚上、销售或启动前使用它。
感谢阅读所有五部分。要回去,开始与 第 1 部分,起点和高可用性,然后 第 2 部分,缩放应用层,第 3 部分,数据库 和 第 4 部分,Valkey、worker 和日志。这是系列的最后部分。
Series
从单服务器到考试日就绪About the Author
Anichur Rahaman
Continue Reading