Shopify 处理大多数系统会崩溃的规模。
在单日(2024 年黑色星期五),平台处理 1730 亿请求,峰值每分钟 2.84 亿请求,并通过其边缘每分钟推送 12TB 流量。
这些数字不是异常。它们是 Shopify 努力满足的持续目标。在这个规模背后是外表欺骗简单的技术栈:Ruby on Rails、React、MySQL 和 Kafka。
但这种简单隐藏尖锐架构决策、多年重构和数千 deliberate 权衡。
后端架构
Shopify 的后端运行在 Ruby on Rails 上。早期 2000 年代编写的原始代码库仍然形成系统的核心。Rails 提供快速开发、约定优于配置和强大的数据库支持 Web 应用模式。Shopify 也使用 Rust 作为系统编程语言。
虽然大多数初创公司最终重写早期框架,Shopify 加倍下注以确保 Ruby 和 Rails 是 100 年工具,将继续值得在工具链中选择。他们没有转向另一个框架,而是进一步推送。他们投资:
- YJIT:基于 Rust 构建的 Ruby 即时编译器,提高运行时性能而不改变开发者 ergonomics
- Sorbet:专门为 Ruby 构建的静态类型检查器。Shopify 对 Sorbet 贡献巨大并使其成为栈的一等部分
- Rails Engines:内置 Rails 功能重新用作模块化机制。每个引擎行为像迷你应用,允许隔离、所有权和最终提取如果需要
结果是生产中最大和最长运行的 Rails 应用之一。
前端演进
Shopify 的前端经历多个架构转变,每个反映更广泛 Web 生态系统的变化和规模下学到的教训。
早期
- 服务器渲染 HTML 模板
- jQuery 和 prototype.js 增强
Batman.js
- 自研 SPA 框架
- 提供反应性和路由
- 长期维护开销
现代
- React:Admin 界面
- TypeScript:类型安全
- GraphQL:数据查询
- React Native:移动应用
数据库架构
MySQL 分片
Shopify 使用 MySQL 作为主要关系数据库,从平台早期就这样做。然而,随着商家量和事务吞吐增长,单实例限制变得不可避免。
2014 年,Shopify 引入分片。每个分片持有整体数据的分区,商家基于确定性规则分布在这些分片上。这在商务中效果良好,租户隔离是自然的。一个商家的订单不需要查询另一个商家的库存。
Pods 架构
随时间,Shopify 用 Pods 替换平面分片模型。Pod 是 Shopify 的完全隔离切片,包含自己的 MySQL 实例、Redis 节点和 Memcached 集群。每个 Pod 可以独立运行,每个可以在单独地理区域部署。
这个模型解决两个问题:
- 移除单点故障。一个 Pod 中的问题不会跨集群级联
- 允许 Shopify 通过添加更多 Pod 水平扩展而不是垂直扩展数据库
缓存和异步
Memcached
作为分布式内存键值存储用于许多用例。两个主要例子是查询缓存和全/部分页面缓存。通过将读重操作(如产品列表和元数据)和其他频繁请求内容物化在内存中,Memcached 提供个位数毫秒访问时间并从主数据库集群卸载大量流量百分比。
Redis
为后台作业处理供电队列。它支持 Shopify 的异步工作流,包括 webhook 交付、邮件发送、支付重试和库存同步。
消息队列
Shopify 使用 Kafka 作为消息和事件分发的骨干。它形成平台内部通信层的脊柱,解耦生产者和消费者,缓冲高容量流量,并支持实时管道为搜索、分析和业务工作流供电。
峰值时,Shopify 的 Kafka 处理每秒 6600 万消息,这个吞吐水平 Few 系统在大型金融或流媒体平台外遇到。
部署和 CI/CD
CI/CD 流程
- Buildkite:CI 编排器
- 40 万 + 单元测试:每次单体构建
- 并行测试:数百并行 worker
- 反馈时间:15-20 分钟窗口
部署策略
- 无 staging 环境
- Canary 部署
- 功能标志:控制暴露和快速回滚机制
- Graphite 工具:用于堆叠 PR 和合并队列
规模和性能指标
- 50 亿美元:黑色星期五 GMV
- 2.84 亿:每分钟峰值请求
- 1730 亿:24 小时总请求
- 12TB:每分钟流量
- 4500 万:每秒数据库读查询
- 760 万:每秒数据库写
- 6600 万:每秒 Kafka 消息
- 40 万+:单元测试
- 2.16 亿:每天嵌入处理
本文为学习目的的个人翻译,译文仅供参考。
原文链接:Shopify Tech Stack。
版权归原作者或原刊登方所有。本文为非官方译本;如有不妥,请联系删除。