【系统架构设计】Shopify 架构:Rails 单体的极限突破

💡 原文中文,约33300字,阅读约需80分钟。
📝

内容提要

Shopify采用模块化单体架构,通过Pod隔离数据与故障域,以Sorting Hat路由请求,Packwerk管理代码边界。Checkout留在单体,Storefront独立渲染。BFCM期间通过生产压测与韧性工具保障性能,PCI合规通过token化隔离卡数据。

🔎

延伸解读

Pod 与 Component:两种正交的隔离维度

Shopify 的架构中,Pod 和 Component 是两个容易混淆的概念。Pod 是运行时隔离单元,用于水平扩展和故障隔离,每个 Pod 拥有独立的 MySQL、Redis 等数据存储;而 Component 是代码层面的模块化边界,通过 Packwerk 强制依赖和隐私规则。理解两者的区别至关重要:Pod 解决的是数据面和故障域的问题,Component 解决的是开发者协作和代码耦合的问题。

模块化单体:微服务之外的另一种选择

Shopify 没有选择微服务架构,而是采用模块化单体,通过 Packwerk 在进程内模拟服务边界。这种选择避免了分布式系统的复杂性,同时获得了清晰的代码边界。对于许多团队而言,模块化单体可能是一个比微服务更务实的选择,尤其是在团队规模有限或系统复杂度尚未达到需要分布式部署的情况下。

PCI 合规的工程化实践:token 化隔离卡数据

Shopify 通过将卡数据隔离在 CardSink 和 CardServer 等专用应用中,使主单体应用不接触卡号,从而缩小 PCI DSS 的审计范围。这种 token 化方案不仅降低了合规成本,还提高了安全性。对于处理支付数据的系统,这种隔离思路值得借鉴,可以避免整个应用被纳入 PCI 合规范围。

Q&A

Shopify 为什么没有选择微服务架构,而是采用模块化单体?

Shopify 认为微服务会带来分布式系统的测试/部署流水线倍增、跨服务数据访问延迟、大重构需协调多服务发布等问题。他们喜欢单体的优点(同仓同部署、进程内调用),痛点在于缺少边界,因此选择模块化单体,用 Packwerk 在进程内强制边界,既保留单体的优势,又获得类似微服务的清晰边界。

Shopify 的 Pod 架构是什么?它和 Kubernetes Pod 有什么区别?

Shopify 的 Pod 是自 2016 年使用的运行时隔离单元,一组 shop 共享完全隔离的 MySQL、Redis、memcached 等数据存储,与 Kubernetes Pod 无关。Kubernetes Pod 是容器编排的最小部署单元,而 Shopify Pod 是租户数据隔离和故障域单元。

Shopify 如何实现租户隔离?

Shopify 通过 Pod 架构实现租户隔离:每个 Pod 包含一组 shop 和完全隔离的数据存储,请求通过 Sorting Hat 路由到特定 Pod,业务代码只感知 shop 不感知 Pod。超大商户可独占 Pod,普通商户通过 Shop Mover 进行再平衡。

Shopify 如何保证 BFCM 大促期间的性能和稳定性?

Shopify 通过加 Pod、Shop Mover 再平衡、生产环境压测(Genghis)、韧性工具(Semian、Toxiproxy、Resumption)以及故障演练(Game Day)来保证 BFCM 性能。压测包括架构测试和规模测试,并模拟真实流量和故障场景。

Shopify 如何处理 PCI 合规,避免卡数据泄露?

Shopify 通过 token 化隔离卡数据:买家在 iframe 中输入卡号,提交到 CardSink 加密生成 token,主应用只处理 token,后台任务将 token 交给 CardServer 解密并调用支付处理器。这样 PCI scope 被限制在支付专用应用,主 monolith 不接触卡号。

Shopify 为什么将 Storefront 拆出单体,而 Checkout 留在单体?

Storefront 读多、可缓存,适合独立扩展;Checkout 写密集、与订单、支付等组件高度耦合,拆出等于重写 Shopify,风险高。因此 Storefront 独立渲染,Checkout 留在单体。

Shopify 如何实现分钟级容灾?

Shopify 通过 Pod Mover 实现分钟级容灾:每个 Pod 配对两个数据中心,一个 active 一个 recovery,Pod Mover 可在约一分钟内将 Pod 迁移到 recovery DC,且不丢弃请求或任务。

Shopify 如何管理代码边界,避免模块化单体变成大泥球?

Shopify 使用 Packwerk 静态分析工具强制代码边界,通过 dependency 和 privacy 规则防止跨组件依赖。Wedge 用于追踪隔离进度,CI 中违规会 break build。

🏷️

标签

➡️

继续阅读