# StubHub 体系结构：全球最大的票务市场背后的惊人复杂性

> 原文： [http://highscalability.com/blog/2012/6/25/stubhub-architecture-the-surprising-complexity-behind-the-wo.html](http://highscalability.com/blog/2012/6/25/stubhub-architecture-the-surprising-complexity-behind-the-wo.html)

![](img/4fbc80a9b3d12c301a08338a63eea311.png)

StubHub 是一个有趣的架构，因为作为门票的做市商，他们所从事的业务与我们通常考虑的业务不同。

StubHub 规模惊人，每年以 20％的速度增长，每小时可处理 80 万个复杂页面，每年可售出 500 万张门票，每小时可处理 200 万次 API 调用。

票务空间异常复杂。 StubHub 的流量非常棘手。 突发性，围绕不可预测的游戏结果，事件，时间表和季节。 涉及很多钱。 有很多不同的参与者。 涉及许多复杂的业务流程。 StubHub 在业务上有几个互补但又截然不同的部分：它们有一个广告服务器组件，可为 ESPN 等网站提供广告，丰富的交互式 UI 和实时门票市场组件。

对我而言，最有趣的是 StubHub 如何将票务，销售点系统，FedEx 交付，买卖双方以及金钱等曾经一度是高接触的实体世界带入数字领域。 他们通过与组织（例如美国职业棒球大联盟）的深度电子集成以及将复杂业务流程移出应用程序空间的生命周期总线来实现这一目标。

这是一个很有趣的问题，因为在处理业务构建功能成为首要任务时必须继续处理构建的旧系统，这使问题变得更加复杂。 让我们看看 StubHub 如何使其全部工作...

## 资源

*   查理·芬曼（Charlie Fineman）在 QCon 上的演讲： [StubHub：为全球最大的票务市场](http://www.infoq.com/presentations/StubHub-Designing-for-Scale-and-Innovation-for-the-World-s-Largest-Ticket-Marketplace)设计规模和创新

## 商业模式

*   **StubHub 是用于门票** 的 eBay。 为门票买卖双方提供一个市场。 他们不像 Ticketmaster 。
*   **托管模型用于为卖家** 提供信任和安全。 信用卡记录在 StubHub 上，当确认订单已收到时，才转移款项。
*   **门票不是商品** 。 买家想要特定的门票，而不是看台座位。 数量有限，没有滞票。 卖方不断更新价格和数量以响应市场力量。 这是一个非常活跃的市场。

## 统计信息

*   每年 500 万订单
*   200 万个活动列表/门票。
*   门票围绕 45,000 个赛事进行。 美国职业棒球大联盟的职缺赛季和超级碗是最活跃的时期。
*   销售额每年以 20％的速度增长。
*   每小时可处理 600k-800k 复杂页面。 在后期赛季每小时爆破一百万。
*   在美国 10 到 12 个核心醒着时间的狭窄时段中，流量突发。 例如，季后赛结束后，会有买家为下一场比赛疯狂。
*   每小时有 200 万来自会员的 API 调用。
*   24-36 工程师。
*   这是一项高触感的业务。 交易的支持电话过去是 1-1，但现在好多了。 员工最大的部分是客户服务和后台业务运营支持。

## 平台

*   Java
*   冷融合（旧版）
*   ActiveMQ
*   [SEDA](http://www.eecs.harvard.edu/~mdw/proj/seda/) （分段事件驱动的体系结构）
*   Lucene / Solr
*   Jboss
*   内存缓存
*   Infiniband -到 SAN 的快速网络
*   XSL
*   Oracle 的高级队列
*   TeamWorks -IBM 工作流构建器
*   Splunk
*   Apache HttpClient
*   Log4j（使用消息格式）
*   挂毯

## 架构

*   门票的三种购买来源：Web，销售点系统，批量上传。 批量上传允许将票证单上传到系统中。
*   Manager 层在 Ticket 数据库上方提供了业务对象抽象。 它调解了所有与票证表的对话。
*   票务表会因买卖双方的活动以及事件周围的自然流量突发性而受到影响。
*   活跃的市场可能导致移动客户端与系统的当前状态过时，因此买家对旧数据做出反应。
*   两个数据泵将来自 Tickets 数据库的数据馈送到内部和外部系统：我的帐户，查找和公共馈送。 我的帐户是用户帐户的界面。 查找是一种搜索功能。 Public Feeds 为 ESPN 和 eBay 等网站提供支持。
    *   内部供稿-包含用于仪表板，卖家是谁，销售情况，销售速度，热图等敏感信息，例如帐户信息。它还根据敏感的市场数据供稿主页的某些部分， 例如热门话题，他们不想与公众分享。
    *   外部供稿（LCS）-广告商，例如 ESPN ，都通过此供稿供稿。 广告是通过 IP 地址映射的地理位置。
*   LCS （上市目录服务）：
    *   我在这里道歉，幻灯片有些偏离，并且很难使演讲者与演示文稿匹配。 所有错误都是我的。
    *   触发器用于使修改表保持最新状态，以进行数据库中发生的更改。
    *   更改数据捕获作业不断轮询更改，并将消息注入到 ActiveMQ 代理中。 这是路由的第一级，包含 sma ll 有效负载：对象 ID，对象类型和更改日期。
    *   更改数据被路由到主服务器，这是在数据中心之间进行复制的基本机制。 辅助数据中心订阅了这些主题，这就是在数据中心之间复制数据的方式。
    *   进入主数据后，数据将被注入 SEDA 队列中进行处理（稍后会有更多介绍）。
    *   未使用管理器是因为存在许多不使用管理器而直接进入数据库的旧系统，因此数据库是分发增量的常见点。
    *   他们的大多数广告是 Flash，但有些广告使用 HTML 呈现。
    *   LCS 提供了购物体验，例如从体育场的交互式图形中选择门票。 Solr 使添加这样的新功能非常容易。
*   SEDA （分段事件驱动的体系结构）在主服务器中使用
    *   主服务器接收 sma ll 更新并弄清楚如何 构建进入内存缓存的内容，以便最终将路由到 Solr 。
    *   使用协议缓存格式将消息缓存在 memcached 中。
    *   消息发送到第二个代理，该代理将消息分散到边缘，即 Lucene / Solr。
    *   消息使用者从代理接收消息。
        *   从数据库加载实体。
        *   确定更新是否有任何级联影响。 因为 Solr 和其他 NoSQL 数据库不进行联接，所以例如，如果更改了乐队名称，则该更改必须传播到 ll 事件。
        *   序列化实体。 将其存储在内存缓存中。
        *   将消息发送到第二个 ActiveMQ 代理，该代理将消息路由到边缘，即 Solr。
        *   通过单独的路由处理代理侦听代理。 这曾经在 Jboss 中，但是 Jboss 会不知所措，他们遇到了饥饿问题，因此将其移出 Jboss。 该侦听器成为系统中用于操作管理的有用阀门。 如果他们需要换出新的 Solr 索引以放入新架构，则可以关闭阀门，让消息在消息代理中备份，然后再次打开阀门，消息将再次开始流动。 从 Solr 故障中恢复，复制索引和更新模式时，这些阀门对其操作稳定性产生了巨大影响。
    *   A ll 这些都是阻塞操作，因此使用线程池可以防止数据库连接风暴。
    *   SEDA 是一种平滑负载的技术。 从资源管理的角度来看，这对他们是有效的。 想法是将工作分解成足够小的部分，以至于缓慢的工作不会窃取其他用户的线程。 工作是分阶段建模的，每个阶段都有自己的线程池，可作为工作节流阀。
*   Solr
    *   Solr 提供了一个不错的文档存储和自然语言文本查询功能。
    *   所有搜索都基于 Solr，包括构面。
    *   快速。 查询以 10 毫秒或更短的时间返回。 他们在 SAN 上使用了 Infiniband 网络，发现他们不需要在内存中缓存数据，他们可以通过快速的网络以足够快的速度为 SAN 提供数据服务。
    *   潜力。 灵活的查询语言，可以使用 ll 文本搜索以及 geo -特殊搜索。
    *   支持许多输出格式：XML，Atom， RSS ，CSV， Json 。 使与各种客户端的集成变得更加容易。
    *   高频写入不是很好。 在高频写入下复制似乎集成得不好。 看到成千上万的更改时执行 rsync 不起作用。 您会获得真正过时的数据。 因此，他们必须建立自己的复制机制。
    *   平面数据结构。 仍然几乎是面向行的。 他们希望支持结构更复杂的文档。
*   DCL -双击介绍浏览
    *   URL 映射到 ID ：类型 ID，地理位置 ID，渲染类型 ID。
    *   DCL （仅是 XSL ）使用类型 ID 和 Geo ID 为 LCS 创建查询。 然后可以一般性地呈现返回的数据。 因此 ll footba ll 小组可以使用 URL 映射 XSL 和 LCS 为他们生成具有完全相同结构的相似页面 ]。
    *   巨大的生产率提高，使添加新功能变得更加容易。 页面中的每个块都是通过 CMS 管理的单个资产。 它们是 XSL 的大块，针对从 LCS 中检索到的上下文文档进行渲染。
    *   A RenderChunkByName ca ll 使在 Facebook 等其他服务上呈现事件变得容易。
    *   在后端进行所有这些操作以实现 SEO 目的。 当搜索引擎可以为 Ajax 编制索引时，他们可能不需要这样做。
*   gifs ，样式表等的边缘缓存，但是数据缓存在它们的服务器上。
*   减少每笔交易的客户互动次数：
    *   客户互动是其营业收入的最大消耗。 当情况恶化时，需要与买卖双方共同努力以解决问题。
    *   增加客户自助服务。 顾客想知道什么时候拿到钱和票。 MyAccount 屏幕允许客户在不使用客户服务的情况下检查订单进度。
    *   向卖家公开 API ，以便他们可以将这些功能集成到他们的系统中。
    *   IVR -集成的语音识别系统支持客户打电话以查找其帐户状态。
    *   现金流量对供应商很重要，因此他们致力于更快地付款。
    *   与美国职棒大联盟（Major League Baseball）的电子集成，因此他们可以在卖方实际拥有票证之前直接从卖方将票证转让给买方。 即时交付并极大地提高了客户满意度并消除了故障点。
*   生命周期总线
    *   用于防止硬链接应用程序中的复杂工作流。 签出应用程序不必担心 ll 下游存在的不同业务流程。 您只需要担心已验证的信用卡和订单，而不要担心配送和电子邮件之类的问题。
    *   在处理遗留问题和管理站点更改时很有用。
    *   有关于 ll 主要生命周期事件的主题。 代理在 Oracle 队列中侦听主题。 下订单时，它将进入未确认状态。 侦听器将收听“未经确认”的主题，并通过电子邮件向卖方发送电子邮件以访问该站点并确认订单。 当卖方确认订单后，那里的代理商将从买方的托管账户中提取款项，向买方发送电子邮件，说明卖方已确认并在何处找到机票。 确认票证后，付款便退给了卖方。
    *   所有这些逻辑都与面向网站的最终用户分离。 这些都是后台引擎。
    *   TeamWorks 为这些流程建模，发现弱点，监视流程，检查 SLA 并触发操作。 帮助更好地优化后台业务流程。 由于他们每年以 20％的速度增长，因此他们不希望运营团队的年增长率达到 20％。
    *   FedEx 是最初的履行方式。 电子实现已添加。 业务流程如下：未确认->自动确认； 已确认->条码重发和付款 PDF； 实现。 您要做的就是编写在相同订单生命周期内生存的代理，以实现新的实现模式。 该逻辑不在应用程序中。 它在代理中，是一个可单独部署和测试的单元。
    *   避免欺诈。 使用相同的生命周期模型来实现，但增加了两个新状态：已购买和已批准。 他们无需进行任何更改即可添加避免欺诈的功能。 他们只需要更改状态机即可。 代理决定将其移至批准状态或未批准状态。
*   销售点系统集成
    *   使用两个阶段的提交：在外部系统上预订票证，将其标记为 StubHub 中声明的内容，在外部系统上提交购买。
    *   希望对此进行概括，以便其他系统可以在交易中购买门票。 将门票与旅行或酒店购买捆绑在一起。
*   Splunk 和染料
    *   在 StubHub 上最大的投资回报率项目之一。 节省了很多调试时间。
    *   染色-工件被放置在每个请求的 HTTP 标头中。
    *   这些使用 Log4j 记录。
    *   使用 Splunk ，如果订单有问题，您可以使用染料标记查看日志行以向后推 ll ，然后查看 ll 属于请求的呼叫，包括对 LCS 之类的其他服务的二次呼叫。 追溯活动很容易。
    *   确实类似于 Splunk 。 就像日志行的文档存储。 将染料标记和 ID 排序到日志行，就像一系列键值对一样， Splunk 使得查看日志变得非常容易。 他们的仪表板是使用 Splunk 编写的，用于统计信息，例如每分钟的事务，每分钟的失败。 您可以根据需要对数据进行切片和切块。
    *   将 Log4j 与消息格式一起使用，以便它不会创建动态字符串。

## 得到教训

*   **可扩展性是专业化** 。 每个问题空间都有其独特的特征，必须构建任何系统来解决该特定问题。 StubHub 受制于对安全购买体验的需求，票务市场的独特性质，突发流量以及事件多变的局面。 他们的系统必须反映这些要求。
*   **从一开始就使用抽象层** 。 否则，您将无法继续为旧客户提供支持，而超出了您的承受能力。
*   **生产中的烘烤** 。 实施多种解决方案，并在生产中进行审核，以确定哪个版本更好。 StubHub 在生产中测试了两个不同的数据泵版本，以发现哪种版本更合适。 您不想支持多种基础架构。
*   **将工作移出 Jboss** 。 大量请求可能会导致 Jboss 饿死，因此他们将工作移到 Jboss 之外。
*   **通过因果链** 渗滤染料。 检测请求，以便可以在整个堆栈中跟踪请求。 这是一个巨大的投资回报，能够调试整个堆栈中的问题。
*   **优化业务流程。** 系统之间的电子集成。 通过充当卖方，买方和 MLB 之间的协调者，他们能够提高客户满意度并消除交易中的大量可能失败点。 购买了受欢迎的销售点程序，以便他们可以与它集成。
*   **建立在您自己的 API** 上。 他们花费大量时间尝试在自己的 API 上构建自己的应用程序，以便他们可以更好地管理其用户和合作伙伴的体验。
*   **一般定义资产** 。 定义资产以便可以在任何上下文中呈现它们，可以轻松组成不同格式的页面并在其他网站上呈现事件。
*   **从 ROI 透视图** 最大化开发。 寻找可提高开发人员 ROI 的项目。 使用 Solr 对他们来说是一个巨大的胜利。 它易于使用，快速且非常实用，可以立即满足许多类型的查询。
*   **SEDA 适合阻止读取** 。 他们的许多系统都基于阻止读取，因此 SEDA 非常适合该用例。 线程池可防止淹没他们要使用的资源。
*   **在客户端** 上呈现。 对于像体育场馆地图这样非常酷的交互式地图，在客户端上通过 ll 处理 ll 的 UI 交互可节省大量服务器负载。 即使对于具有 10-20K 列表的大型活动，下载整个列表也是一个更好的解决方案。
*   **比重框架**更薄。 沉重的框架易于使用和滥用。 隐藏的复杂性使您很快失去对站点的控制。 示例：Hibernate 和基于组件的框架。 验证和业务逻辑可能会泄漏到表示层中。 做出明智的决定。 了解遗留问题方面的问题。
*   **糟糕的经历是最好的训练** 。 就像没有教给如何正确做事一样。 刚开始 StubHub 的家伙正在通过尽快发布功能来建立业务，但这留下了一笔遗产。 管理遗留物的关键是管理依赖关系。 使用基于代理的 Lifecycle Bus 样式解决方案可帮助他们了解对遗留系统的依赖性。
*   **使用工作流将状态机与应用程序分离。** 不要在应用程序逻辑中嵌入复杂的流。 外部化逻辑，以便业务流程可以更灵活的方式耦合在一起。 这使系统在未来变得无限灵活和适应性强。
*   **避免使用 ETL** 。 它引入了许多您不想处理的依赖项。 有风险。 当您尝试确定财务上依赖的变更对您而言是否很大时，旧式数据模型可以真正占用资源。
*   **不要缩短 CM 和部署**。 当前，对于开发人员来说，这是最大的浪费时间。 非常痛苦 现在投资您的 CM 和部署系统。
*   **投资持续改进** 。 它不是免费提供的。 在项目上运行验尸。 确保问题不再出现。 随着公司的发展，这些东西无法扩展。 立即做出正确的决定，否则将来 ll 需要花费 3 到 5 倍来解决。
*   **在系统** 中建立操作阀。 例如，如果您需要换出新的架构，请使用一个阀门，您可以在其中关闭事件并重新启动它们。

该 Java Framework Tapestry 在哪里使用？

您是在我们网站上还是其他地方？

“当您试图找出变更是否会带来巨大收益时，它们将成为您财务上依赖的系统。”

句子里有错字，对不对？ 难道不是“当您试图确定变更是否会创建您在财务上依赖的系统时”吗？ 谢谢。

爱德华

我认为这个家伙在获得我的要点方面做得很好，但是在这种情况下...是的...这很想念...我认为不是错字。 :-)在不返回原始录音版本的情况下，它应该类似于：

“当您试图确定更改是否会在您所依赖的系统中造成回归时”

我真的想了解更多有关如何在 HTTP 标头中使用 splunk 和 dye 来跟踪整个堆栈中的每个请求的信息，从调试的角度来看，这是非常有用的功能。