【51CTO快译】StubHub这个令人关注的架构之所以值得剖析,是因为作为票务做市商,它从事的行业不同于我们通常所考虑的行业。
StubHub规模大得惊人,每年以20%的速度发展,每小时提供80万个复杂页面,每年出售的各种票多达500万张,每小时处理200万次API(应用编程接口)调用。
而票务行业来得异常复杂。StubHub的流量很复杂。其流量具有突发性,主要围绕不可预测的比赛结果、文体活动、比赛时间表和赛季。这行当涉及许多的钱,涉及许多不同的演艺人员,涉及许多复杂的业务流程。而StubHub有几个互为补充但全然不同的业务部分:它有一个广告服务器组件(为ESPN等网站提供广告)、丰富的交互式用户界面以及实时票务市场组件。
我最感兴趣的是,StubHub怎样把曾经高度人性化的物理领域(各种票、销售点系统、联邦快递送货、买卖双方和钱财)搬入到数字领域。它实现这一切的手段是,与美国职业棒球大联盟等组织进行全面深入的电子集成,并借助生命周期总线(Lifecycle Bus)让复杂的业务流程与应用领域隔离开来。
由于StubHub既要向前发展,又要应对早期发布扩大业务的功能特性是优先项目时构建的遗留系统,这个令人关注的问题显得更加困难了。下面看看StubHub是如何成功做到的。
本文来源
■StubHub架构主管Charlie Fineman在QCon大会上的演讲:《StubHub:为扩展性和创新而设计,打造世界上最大的票务市场》
经营模式
■StubHub好比是票务市场的电子港湾(eBay)。为买票卖票双方提供了一个市场。它与世界上最大的票务公司特玛捷公司(Ticketmaster)不一样。
■第三方保管模式用来为买卖双方提供信任和安全机制。信用卡在StubHub存档起来,只有订单确认为票已收到后,买方才从信用卡上转账。
■门票不是普通商品。比如说,买方想要特定的门票,而不是露天看台座椅。门票数量有限,门票也不可以延期交货。面对不断变化的市场形势,卖方不断更新价格和数量。这是个非常活跃的市场。
统计数字
■每年500万笔订单。
■200万张待售门票。
■门票涉及大约4500场文体活动。美国职业棒球大联盟季后赛和美国橄榄球超级杯大赛是销售最旺的时期。
■销售幅度每年增长20%。
■每小时提供60万至80万个复杂页面。季后赛期间,猛增到每小时100万个页面。
■在美国观看比赛的黄金时段(每晚10点至12点)的短暂时间期间,流量的突发性非常强。比如说,季后赛结束后,会出现人们疯狂购买下一个赛季门票的情况。
■来自附属组织的API调用每小时多达200万次。
■24至36名工程师。
■这是高度人性化的行业。过去,支持电话与交易比是1:1,但现在情况好多了。工作人员最主要的工作是客户服务和后台业务运营支持。
平台
■Java
■Cold Fusion(遗留平台)
■ActiveMQ
■SEDA(分阶段事件驱动架构)
■Lucene/Solr
■Jboss
■Memcache
■Infiniband—连接至存储区域网络(SAN)的高速网络
■XSL
■甲骨文的高级队列(Advanced Queuing)
■TeamWorks—IBM工作流程构建器
■Splunk
■Apache HttpClient
■Log4j(使用消息格式)
■Tapestry
架构
■购买门票有三个来源:互联网、销售点系统和批量上传。批量上传允许多张门票上传到系统。
■Manager层在Ticket数据库上提供了一种业务对象抽象机制。它负责调控与订票系统的所有联系。
■订票系统因买卖双方的活动和文体活动天生具有突发性的流量而拥堵。
■活跃市场会使得使用移动设备的客户无法及时了解系统现状,所以买方应对的是旧数据。
■两个数据泵(data pump)将数据从Tickets数据库抽取到内外系统:My Account、Find和Public Feed。My Account是用户账户的接口。Find是一项搜索功能。Public Feeds则支持ESPN和电子港湾等网站。
■Internal Feed:含有用于仪表板的敏感信息,比如账户信息,包括卖方是谁、销售情况如何、销售速度和热图(heat map)等。它还提供主页中基于敏感市场数据的部分,比如什么热销,这些是StubHub不想拿来与公众共享的。
■External Feed(LCS)—通过该Feed为ESPN等广告商提供数据来源。广告由IP地址实现地理映射。
■LCS(列表目录服务)
■这里先说声抱怨,幻灯片有点错误,很难把演讲者与演讲文稿对应起来。所有错误都怪我。
■触发器用来确保修改表格及时更新;数据库一出现变更,这些变更内容就会出现在表格中。
■变更数据捕获(Change Data Capture)作业不断查询变更,并把消息注入到ActiveMQ中介。这是路由传送的第一道,含有较小的有效载荷:对象编号、对象类型和变更日期。
■变更数据传送到Master,这是数据中心之间进行复制的基本机制。辅助数据中心订阅这些主题,这就是数据中心之间复制数据的方式。
■一旦在Master中,数据被注入到SEDA队列,进行处理(稍后会有详细介绍)。
■并不使用Manager,因为存在不使用Manager、直接进入到数据库的许多遗留系统,所以数据库是分发变更内容的常见点。
■大部分广告是Flash广告,但有些使用HTML渲染。
■购票体验得到LCS的支持,比如从体育馆的交互图形中选择门票。Solr使得添加诸如此类的新功能特性变得很容易。
■SEDA(分阶段事件驱动架构)在Master中的使用
■SEDA是减轻负担的一种方法。从资源管理的角度来看,它对StubHub来说行之有效。其想法是,把工作负载分解成足够小的部分,那样这些细分后的工作负载不会窃取其他用户的线程。工作负载分阶段建模,每个阶段都有自己的线程池,这相当于有效控制了工作负载。
■Master收到小的更新内容后,弄清楚如何构建进入到memcache的内容,以便最终传送到Solr。
■消息使用一种协议缓冲器格式缓存在memcached中。
■消息发送到第二个中介,该中介将消息向外分发到边缘,即Lucene/Solr。
■消息使用者收到中介发来的消息
■从数据库装入实体。
■确定更新内容有没有带来任何级联效应。由于Solr及其他NoSQL数据库并不进行连接(join)操作,那样比如乐队名称出现了变更,该变更内容必须告知所有事件。
■实体序列化。把实体存储在memcache中。
■将消息发送到第二个ActiveMQ中介,由它将消息传送到边缘,即Solr。
■中介由负责路由传送的另一个进程进行侦听。这番操作一度在Jboss中进行,但是Jboss会不堪重负,而StubHub遇到了饥饿矛盾问题,于是它把侦听工作移到了Jboss外面。该侦听器成了系统中一个实用的阀门,起到操作管理。如果StubHub需要换掉新的Solr索引,引入一种新的数据库架构(schema),就可以关掉这个阀门,让消息在消息中介备份起来,再次打开阀门,消息就会再次开始流入。遇到Solr故障后恢复、复制索引和更新数据库架构时,阀门对StubHub的操作稳定性起到了巨大影响。
■所有这些都是阻塞操作,所以使用线程池可以防止同时出现大量数据库连接。
■Solr
■Solr提供了一项很好的文档存储和自然语言文本查询功能。
■所有搜索都建立在Solr上,包括分面搜索(faceting)。
■运行速度快。查询在10毫秒或更短时间内返回结果。StubHub使用与SAN相连的Infiniband网络,发现自己不需要把数据缓存到内存中,而是可以借助这个高速网络,以足够快的速度从SAN提供数据。
■功能强。灵活的查询语言、全文本搜索和地理空间搜索。
■支持许多输出格式:XML、Atom、RSS、CSV和Json等。因而更容易与各种客户端进行集成。
■高频率写入方面不是很好。在高频率写入环境下,复制似乎没有很好地集成。你看到成千上万的变更时,进行同步操作行不通。你得到的其实是过期数据。所以,StubHub只好扩建了自己的复制机制。
■平面数据结构。仍然几乎是面向行。StubHub希望能够支持结构更复杂的文档。
■DCL—双击浏览
■URL映射到编号上:性别编号、地理编号和显示类型编号。
■类别编号和地理编号由DCL(就是XSL)使用,为LCS创建查询。然后,以一般方式显示返回的数据。所以,所有足球队都可能有为它们创建的类似页面,有着一模一样的结构,使用URL映射、XSL和LCS。
■大大提升生产力,而且添加新的功能特性变得容易多了。页面中的每个块都是通过内容管理系统(CMS)来管理的一种资产。它们是大段的XSL,对照从LCS检索的上下文文档来进行显示。
■有了RenderChunkByName调用,很容易在Facebook等其他服务网站上显示文体活动。
■这一切都在后端进行,旨在实现搜索引擎优化(SEO)。由于搜索引擎可以检索Ajax,StubHub不需要进行这番操作。
■对gif文件和样式表等进行边缘缓存(edge caching),但是数据缓存在服务器上。
■减少每笔交易的客户交互次数:
■客户交互对StubHub的经营收入来说是最大的开销。万一出现什么问题,买卖双方就要花大量的精力来解决问题。
■增加客户自助服务。客户(买卖双方)想知道何时拿到钱和票。MyAccount屏幕让客户不用借助客户服务,就可以查看订单进度。
■把API展示在卖方面前,那样他们就能把这些功能特性集成到自己的系统中。
■IVR—集成语音识别(IVR)系统支持客户为查询账户状态而打来的电话。
■现金流量对商家来说很重要,所以StubHub致力于更快地完成支付。
■与美国职业棒球大联盟进行电子集成,那样StubHub就能在卖方实际拿到门票之前,将门票直接从卖方交给买方。优点包括:立即送票、客户满意度大幅提高、消除了故障点。
■生命周期总线
■用来防止在应用程序中硬连线复杂的工作流程。结账应用程序不想非得为下游的所有不同业务流程而操心。你想操心的只是经验验证的信用卡和订单,不是像订单履行和电子邮件这些东西。
■在处理遗留问题和管理网站出现的变更时很有用。
■存在所有主要生命周期事件的主题。软件代理侦听甲骨文的队列,了解主题。订单下达后,它进入到未经核实的状态。侦听器侦听到“未经核实的”主题后,发电子邮件给卖方,访问网站、核实订单。当卖方核实订单后,代理就会从买方的第三方托管账户收到款项,然后发邮件给买方,表示卖方已核实、何处拿票。门票确认无误后,款项付给卖方。
■所有这些逻辑与网站面向最终用户的部分脱离开来。这些都是后台引擎。
■TeamWorks为这些流程建模,查找薄弱环节,监控流程,核实服务级别协议 (SLA),以及触发操作。并帮助更好地优化后端业务流程。尽管StubHub每年的增长率达到20%,但是它不想操作团队的规模每年同比壮大20%。
■联邦快递是原始的订单履行模式。后来添加了电子订单履行。业务流程如下:未经核实->自动核实;经过核实->条形码重新发和分发PDF文档;订单履行。你只要编写遵循同一订单生命周期的软件代理,即可实施新的履行模式。这个逻辑并不在应用程序中,而是在代理中,它是可以单独部署和测试的单元。
■避免欺诈。使用与订单履行一样的生命周期模式,不过增加了两个新的状态:已购买和已批准。StubHub不必进行任何变得,即可添加避免欺诈的功能。StubHub只要更改状态机就行。软件代理决定把它移到已批准状态或未批准状态。
■销售点系统集成
■使用两个阶段的提交:在外部系统上预订门票,在StubHub中把它标为已认购,在外部系统上提交购买。
■考虑扩大这项功能的应用范围,那样作为交易的一部分,其他系统也能购买门票。门票与旅行或酒店客房预订捆绑起来。
■Splunk和Dye
■这是StubHub成效最显著的项目之一。在问题调试和排除方面节省了大量时间。
■Dye—工件注入到每个请求的HTTP头中。
■这些使用Log4j记入日志。
■使用Splunk;如果订单有问题,你可以使用Dye标记(Dye marker)查看日志里面的一行行内容,往回查看属于请求一部分的所有调用,包括对LCS等其他服务的二次调用。很容易追溯活动的根源。
■StubHub很喜欢Splunk。它就像长行的文档存储区。把Dye标记和订单编号放入到日志行,比如一系列键值对(key-value pair),Splunk就会让你很容易查看日志。StubHub的仪表板用Splunk编写而成,可显示每分钟交易和每分钟交易失败次数等统计数字。你可以随意对数据进行交叉分析。
■使用消息格式的Log4j,那样它不会进行动态字符串创建。
汲取的经验教训
■可扩展性就是专门化。每一个问题空间(problem space)都有其独特的特点;任何系统都必须是为了解决这个特定问题而构建的。StubHub受制于对安全购票体验的需要、票务市场具有的独特性、突发流量和事件的变幻无常。其系统必须体现这些要求。
■一开始就使用抽象层。否则,你就只好支持遗留客户端,早超出了你的容许范围也得支持。
■在生产环境中进行比较。实施多个解决方案,在生产环境中进行一番比较,确定哪个版本效果更好。StubHub在生产环境中曾测试了两种不同的数据泵版本,看看哪一个更合适。你不希望到头来支持多套基础设施。
■把工作移到Jboss外面。数量众多的请求可能会引起Jboss处于饥饿状态,于是StubHub把工作移到了Jboss外面。
■为dye赋予因果链。提交请求,那样可以跟踪请求在整个堆栈中的情况。能够跨整个堆栈调试问题是一大优点。
■优化业务流程。系统之间实现电子集成。通过在买方、卖方和职业棒球大联盟之间充当协调者,StubHub得以提高客户满意度,消除交易中大量可能存在的故障点。StubHub买下了一个流行的转售点计划,那样它就能与之集成起来。
■建立在自己的API上。StubHub在花大量的时间,努力在自己的API上构建自己的应用程序,那样它就能为用户和合作伙伴更有效地管理购票体验。
■一般化地定义资产。定义资产以便资产可以在任何上下文中显示,这样便于制作不同格式的页面,在其他网站上显示文体活动。
■从投资回报的角度,最大限度地提高开发效率。寻求提高开发人员投资回报的项目。使用Solr对StubHub来说是一大成功秘诀。它易于使用、运行速度快,又非常实用,能够直接满足许多类型查询的需要。
■SEDA适用于阻塞读取。StubHub的许多系统基于阻塞读取(blocking reads),所以SEDA很适合这种使用场合。线程池防止过量占用它所要使用的资源。
■客户端显示。对于很酷的交互式地图,如体育馆地图,客户端上处理所有的用户界面交互,可以为服务器减轻许多负担。即使对比较大的文体活动(待售门票达1万至2万张)来说,下载整个列表也是比较好的解决办法。
■选择简洁的框架而不是笨重的框架。笨重的框架容易使用,但也容易滥用。隐藏复杂性让你很快对自己的网站失去控制,比如基于Hibernate的框架和Component的框架。验证和业务逻辑可能会渗入到表示层。要做出谨慎的决定。要明白你在解决什么样的遗留问题。
■糟糕的经历其实是最好的培训机会。说到教你如何做好事情,没有比失败更理想的东西了。开办StubHub的一帮人当初通过尽快推出功能特性来发展业务,但是这留下了遗留系统。管理遗留系统的关键是,管理依赖关系。使用基于代理的生命周期总线式解决方案帮助StubHub了解了遗留系统上的依赖关系。
■使用工作流程把状态机与应用程序脱离开来。不要把复杂的流程嵌入到应用程序逻辑中。把逻辑放在应用程序的外面,那样业务流程能够以更灵活的方式结合起来。这使得系统在将来极具灵活性和适应性。
■避免抽取、转换和加载(ETL)。ETL带来了你宁愿不想处理的许多依赖关系。它是个风险因素。当你试图搞清楚某次变更会不会破坏你在经济上依赖的系统时,遗留数据模型其实会不断消耗资源。
■不要疏忽了配置管理(CM)及部署。现在对开发人员来说,这是目前最浪费时间的方面。这让人痛苦不堪。现在就要投入到你的CM和部署系统。
■致力于持续改进。持续改进不会自然到来,需要你付出心血。对项目进行事后分析。确保问题不会再次出现。贵公司在发展时,这个系统可能无法扩展。现在应该做出正确的决策,否则将来要花三五倍的精力来解决问题。
■把操作阀门做入到系统中。比如说,如果你需要换掉一种新的数据库架构,就要有一个阀门,那样你就能关闭事件、再次重启事件。
原文链接:
http://highscalability.com/blog/