# Wix 的 Nifty Architecture 技巧-大规模构建发布平台

> 原文： [http://highscalability.com/blog/2014/11/10/nifty-architecture-tricks-from-wix-building-a-publishing-pla.html](http://highscalability.com/blog/2014/11/10/nifty-architecture-tricks-from-wix-building-a-publishing-pla.html)

![](img/51ad9a97b669a9cbea8846d65dbba291.png)

[Wix](http://www.wix.com/) 长期运营网站。 作为基于 HTML5 的所见即所得（WYSIWYG）网络发布平台，他们已经创建了超过 5400 万个网站，其中大多数网站每天的网页浏览量不到 100。 因此，传统的缓存策略不适用，但仅需四个 Web 服务器即可处理所有流量。 这需要一些聪明的工作。

[Wix 后端工程负责人 Aviran Mordo](https://twitter.com/aviranm) 在精彩的演讲中描述了他们的解决方案：[规模](http://www.infoq.com/presentations/wix-architecture)的 Wix 体系结构。 他们开发的最佳传统就是专业化。 他们仔细分析了他们的系统，并弄清楚了如何以一些最有趣的方式实现其积极的高可用性和高性能目标。

Wix 使用**多个数据中心和云**。 我之前从未见过的是它们将数据复制到多个数据中心，Google Compute Engine 和 Amazon。 并且在失败的情况下它们之间具有备用策略。

Wix **不使用交易**。 相反，所有数据都是不可变的，并且它们使用简单的最终一致性策略来完美匹配其用例。

Wix **不缓存**（就像在大缓存层中一样）。 取而代之的是，他们非常注意优化渲染路径，以便每个页面的显示时间均不到 100ms。

Wix 从一开始就具有单一的体系结构，并有意识地**迁移到了服务体系结构**，该过程使用了一种非常刻意的过程来识别服务，该服务可以帮助任何考虑同一步骤的人。

这不是您的传统 LAMP 堆栈或本机云。 Wix 有点不同，您可以在这里学到一些东西。 让我们看看他们是如何做到的...

## 统计信息

*   54 个以上的网站，每月有 100 万个新网站。

*   超过 800 TB 的静态数据，每天有 1.5 TB 的新文件

*   3 个数据中心+ 2 个云（Google，Amazon）

*   300 台服务器

*   每天有 7 亿个 HTTP 请求

*   共 600 人，R 区& D 共 200 人

*   约有 50 个服务。

*   需要 4 个公共服务器才能为 4500 万个网站提供服务

## 平台 [

*   MySQL

*   Google 和亚马逊云

*   CDN

*   厨师

## 进化 [

*   简单的初始整体架构。 从一台应用服务器启动。 这是最简单的入门方法。 进行快速更改并部署。 它使您到达特定的位置。

    *   Tomcat，Hibernate，自定义 Web 框架

    *   使用的状态登录。

    *   忽略了性能和扩展性的任何概念。

*   快进了两年。

    *   仍然是一台完成所有任务的单片服务器。

    *   在一定规模的开发人员和客户眼中，它们阻碍了他们的发展。

    *   功能之间的依存关系问题。 一处的更改导致整个系统的部署。 不相关区域的故障导致整个系统停机。

*   是时候把系统分开了。

    *   采用了一种服务方法，但这并不容易。 您如何将功能分解为服务？

    *   查看了用户在系统中的工作，并确定了三个主要部分：编辑网站，查看由 Wix 创建的网站，提供媒体。

    *   编辑网站包括服务器中数据的数据验证，安全性和身份验证，数据一致性以及许多数据修改请求。

    *   网站制作完成后，用户将对其进行查看。 观看者比编辑者多 10 倍。 所以现在的问题是：

        *   高可用性。 高可用性是最重要的功能，因为它是用户的业务。

        *   高性能

        *   高流量

        *   长尾巴。 有很多网站，但是它们很小。 每个站点每天可能会获得 10 或 100 个页面浏览量。 长长的尾巴使缓存不适合可伸缩性策略。 缓存变得非常低效。

    *   媒体服务是下一个重要服务。 包括 HTML，javascript，css，图像。 需要一种在大量请求下为文件提供 800TB 数据的方法。 胜利是静态内容是高度可缓存的。

    *   新系统**看起来像**，位于三个网段服务下面的网络层：编辑器网段（任何编辑数据的文件），媒体网段（处理静态文件，只读），公共网段（文件的第一位是 已查看，只读）。

## 如何构建服务的准则 [

*   每个服务都有自己的数据库，只有一个服务可以写入数据库。

*   仅通过服务 API 访问数据库。 这支持将关注点分离并从其他服务隐藏数据模型。

*   出于性能原因，授予其他服务只读访问权限，但只能写入一个服务。 （是的，这与之前所说的相矛盾）

*   **服务是无状态的**。 这使得水平缩放变得容易。 只需添加更多服务器。

*   **没有交易**。 除帐单/金融交易外，所有其他服务均不使用交易。 这个想法是通过消除事务开销来提高数据库性能。 这使您考虑如何在不使用数据库事务的情况下将数据建模为具有逻辑事务，避免出现不一致的状态。

*   设计新服务**时，缓存不属于体系结构**。 首先，使服务尽可能地具有性能，然后部署到生产环境，看看其性能，然后，如果存在性能问题，并且您无法优化代码（或其他层），则只能添加缓存。

## 编辑器细分

*   编辑器服务器必须处理大量文件。

*   在 MySQL 中存储为不可变 JSON 页（每天约 250 万个）的数据。

*   **MySQL 是一个很棒的键值存储**。 密钥基于文件的哈希函数，因此密钥是不可变的。 通过主键访问 MySQL 非常快速高效。

*   **可伸缩性与权衡**有关。 我们要进行哪些权衡？ 不想使用 NoSQL，因为它们会牺牲一致性，并且大多数开发人员都不知道该如何处理。 因此，请坚持使用 MySQL。

*   **活动数据库**。 网站建成后发现，只有 6％的网站仍在更新中。 这样，这些活动站点就可以存储在一个真正快速且存储量相对较小（2TB）的数据库中。

*   **存档数据库**。 对于不经常访问的站点，所有过时的站点数据都移至相对较慢但具有大量存储空间的另一个数据库中。 三个月后，数据被推送到该数据库，因为访问率低。 （可能会说这是一种隐式缓存策略）。

*   为呼吸提供了很大的成长空间。 大型归档数据库的速度很慢，但这没关系，因为数据使用的频率不高。 首次访问时，数据来自存档数据库，但是随后将其移至活动数据库，因此以后的访问速度很快。

## 编辑器细分市场的高可用性

*   拥有大量数据，很难为所有内容提供高可用性。 因此，**查看关键路径**，对于网站而言，该路径就是网站的内容。 如果小部件出现问题，则大多数网站仍将正常工作。 在保护关键路径上投入了大量资金。

*   防止数据库崩溃。 想要快速恢复。 复制数据库并将故障转移到辅助数据库。

*   **防止数据损坏和数据中毒**。 不必一定是恶意的，一个漏洞足以破坏枪管。 所有数据都是不可变的。 修订存储了所有内容。 如果无法修复损坏，最坏的情况是还原到数据可以正常使用的版本。

*   防止不可用。 网站必须一直工作。 这推动了**在不同地理位置和多个云**之间复制数据的投资。 这使得系统非常有弹性。

    *   在网站编辑会话上单击保存会将 JSON 文件发送到编辑器服务器。

    *   服务器将页面发送到活动 MySQL 服务器，该服务器已复制到另一个数据中心。

    *   将页面保存到本地后，将启动一个异步过程，将数据上传到静态网格（即媒体段）。

    *   将数据上传到静态网格后，系统会将通知发送到在 Google Compute Engine 上运行的存档服务。 存档会转到网格，下载页面，然后将副本存储在 Google 云中。

    *   然后，通知会发送回编辑器，说明页面已保存到 GCE。

    *   另一个副本已从 GCE 保存到 Amazon。

    *   收到最后一条通知，这表示当前数据修订版有三份副本：一份在数据库，静态网格中，以及在 GCE 上。

    *   当前版本有 3 个副本。 对于旧版本，有两个版本（静态网格，GCE）。

    *   该过程是自修复的。 如果失败，则下次用户更新其网站时，所有未上传的内容都会重新上传。

    *   孤立文件被垃圾回收。

## 没有数据库事务的数据建模

*   不想出现这样的情况，即用户编辑了两个页面，而数据库中只保存了一个页面，这是一种不一致的状态。

*   获取所有 JSON 文件，并将它们一个接一个地粘贴在数据库中。 保存所有文件后，将发出另一个保存命令，其中**包含上载到的已保存页面的所有 ID** （这是内容的哈希，是静态服务器上的文件名）的清单。 静态服务器。

## 媒体段 [

*   存储大量文件。 800TB 的用户媒体文件，每天上传的 3M 文件和 500M 的元数据记录。

*   图像被修改。 它们针对不同的设备调整了大小并进行了锐化。 可以插入水印，还可以进行音频格式转换。

*   构建了一个最终一致的分布式文件系统，该系统具有多数据中心感知能力，并且可以跨 DC 自动回退。 这是在亚马逊之前。

*   难以忍受。 32 台服务器，每 9 个月翻一番。

*   计划将内容推送到云以帮助扩展。

*   **供应商锁定是一个神话**。 全部都是 API。 只需更改实施，您就可以在几周内迁移到不同的云。

*   **真正使您失望的是数据**。 将 800TB 的数据转移到另一个云上真的很困难。

*   **当他们将所有数据移至 GCE 时，他们破坏了 Google Compute Engine** 。 他们达到了 Google 云的极限。 经过 Google 的一些更改后，它现在可以使用了。

*   文件是不可变的，因此高度可缓存。

*   图像请求首先进入 CDN。 如果映像不在 CDN 中，则请求将转到其位于奥斯丁的主数据中心。 如果图片不在 Austin 中，则请求转到 Google Cloud。 如果不在 Google 云中，它将转到坦帕的数据中心。

## 公共细分

*   解析 URL（其中的 4500 万个），分派到适当的渲染器，然后渲染为 HTML，站点地图 XML 或机械手 TXT 等。

*   **公共 SLA 是在高峰流量**时，响应时间为< 100ms。 网站必须可用，而且速度也要很快。 记住，不要缓存。

*   当用户在编辑页面后单击“发布”时，包含对页面的引用的清单将被推送到“公共”。 路由表也已发布。

*   **最小化服务中断跳**。 需要 1 个数据库调用才能解析路由。 1 RPC 调用，将请求分派到渲染器。 1 个数据库调用以获取站点清单。

*   查找表缓存在内存中，每 5 分钟更新一次。

*   数据存储的格式与编辑器使用的格式不同。 **以非规范化格式**存储，已针对主键读取进行了优化。 所需的所有内容都在单个请求中返回。

*   **最小化业务逻辑**。 数据将被规格化和预先计算。 当您大规模处理每项操作时，每增加一毫秒，这就是 4,500 万次，因此必须证明在公共服务器上发生的每项操作都是合理的。

*   页面渲染。

    *   公用服务器返回的 html 是 **bootstrap html** 。 这是一个包含 JavaScript 导入和 JSON 数据（引用网站清单和动态数据）的外壳。

    *   **渲染已卸载到客户端**。 笔记本电脑和移动设备非常快，可以处理渲染。

    *   选择 JSON 是因为它易于解析和可压缩。

    *   **更容易修复客户端**上的错误。 只需重新部署新的客户端代码。 在服务器上完成渲染后，将缓存 html，因此，修复错误需要**再次重新渲染数百万**个网站。

## 公共段的高可用性

*   目标是永远可用，但事情还是会发生。

*   **在**美好的一天：浏览器发出请求，该请求通过负载平衡器到达数据中心，到达公共服务器，解析路由，到达渲染器，html 返回到 浏览器，然后浏览器运行 javascript。 javascript 会获取所有媒体文件和 JSON 数据，并呈现一个非常漂亮的网站。 然后，浏览器向存档服务发出请求。 存档服务以与浏览器相同的方式重放请求，并将数据存储在缓存中。

*   在糟糕的日子**，数据中心丢失**，这确实发生了。 所有不间断电源系统都死了，数据中心关闭了。 DNS 已更改，然后所有请求都转到了辅助数据中心。

*   在糟糕的日子**公众失去了**。 当负载均衡器获得一半配置，因此所有公共服务器都消失了时，就会发生这种情况。 或者可以部署一个错误版本，该版本开始返回错误。 如果公共服务器不可用，负载平衡器中的自定义代码通过路由到存档服务以获取缓存的缓存来解决此问题。 这种方法意味着即使 Public 崩溃，客户也不会受到影响，即使当时系统正在响起警报。

*   糟糕的一天**，互联网糟透了**。 浏览器发出请求，转到数据中心，转到负载平衡器，获取 html。 现在，JavaScript 代码必须获取所有页面和 JSON 数据。 转到 CDN，转到静态网格并获取所有 JSON 文件以呈现站点。 在这些过程中，Internet 问题可能会阻止文件被返回。 JavaScript 中的代码表示如果您无法到达主要位置，请尝试从存档服务获取它，如果失败，请尝试编辑器数据库。

## 吸取的教训 [

*   **确定您的关键路径和关注点**。 想一想您的产品如何运作。 制定使用方案。 将精力集中在这些方面，因为它们可以带来最大的收益。

*   **转到多数据中心和多云。** 在关键路径上构建冗余（以提高可用性）。

*   **对数据进行非规范化并最小化进程外跳数（以提高性能）**。 进行预校正并尽一切可能使网络颤动最小化。

*   **利用客户端的 CPU 能力**。 这样可以节省服务器数量，并且更容易修复客户端中的错误。

*   **从小处着手，完成任务，然后找出下一步**的去向。 Wix 做了他们需要做的事情，以使其产品正常工作。 然后，他们有条不紊地迁移到复杂的服务体系结构。

*   **长尾巴需要其他方法**。 Wix 不会缓存所有东西，而是选择优化渲染路径之外的内容，并将数据保留在活动数据库和存档数据库中。

*   **变为不可变**。 不变性对体系结构具有深远的影响。 它影响从客户端到后端的所有内容。 这是解决许多问题的理想解决方案。

*   **供应商锁定是一个神话**。 全部都是 API。 只需更改实施，您就可以在几周内迁移到不同的云。

*   **真正使您失望的是数据**。 将大量数据转移到不同的云真的很困难。

有一个问题：每天的流量如何达到 700 M？ StackExchange 每天有 1.5 亿，而 Alexa 评级更高。