Storm是如何成为Apache顶级项目的

jopen 9年前

Apache Storm是一个免费、开源的分布式实时计算系统,不久前刚刚升级为Apache顶级项目。近日,该项目创建者Nathan Marz撰文回顾了Storm的发展历史以及相关的经验教训。他认为,任何项目要想取得成功必须具备如下两个条件:

  1. 解决了某一类需求;
  2. 项目创建者需要让别人知道该项目是相关需求的最佳解决方案。

Nathan认为,许多开发人员并没有认识到达成第二个条件与构建项目本身一样困难和有趣。他希望,读者在了解了Storm的历史后能够对这一点有一个清晰的认识。

创建Storm之前

Storm源于Nathan在分析平台创业公司BackType的工作。在那里,他们构建分析产品,帮助企业了解其在社交媒体上的影响,其中包括历史数据分析和实时数据分析。在创建Storm之前,实时数据分析部分是使用一种标准的队列和工作进程的方法。这种方法在构建应用程序时非常繁琐。业务逻辑不得不处理消息的发送/接收、序列化/反序列化等问题,而实际的业务逻辑只占代码库的一小部分。

第一次思考

2010年12月,Nathan首先提出将“流(Stream)”作为一个分布式的抽象概念,然后又提出了“spouts”和“bolts”的想法,前者生成全新的流,而后者以流作为输入,并生成流作为输出。Bolts只需订阅它们需要处理的流,并指明作为输入的流应该如何划分。最后,他提出了最上层的抽象概念“拓扑(Topology)”,它是一个由spouts和bolts组成的网络。

之后,Nathan在BackType的应用场景中对上述概念进行了测试,发现它们非常适合这些场景。发送/接收消息、序列化、发布这些他们先前需要处理的繁琐工作实现了自动化。

为了在更多的应用场景中验证他的想法,在开始构建Storm之前,Nathan发了一条Tweet征集应用场景。在进一步证实了这些概念的合理性之后,他开始构建Storm,但很快就遇到了麻烦,他没能找到一种在spouts和bolts之间传递消息的理想方法。最初,他想模拟先前的队列和工作进程的方法,使用一个类似RabbitMQ的消息代理传递中间消息。为此,他花了很多时间研究RabbitMQ,但总觉得这一方案不够理想,于是就暂停了构建过程。

第二次思考

Nathan最初认为需要消息代理,是因为他觉得这可以为消息处理提供保障。如果bolt处理消息失败,那它可以从获取消息的代理那里重新开始处理过程。但使用中间消息代理有诸多不妥之处:

  1. 它们随着Storm扩展会非常复杂。
  2. 它们会导致一些难以处理的情况。比如,在拓扑重新部署后,代理上的中间消息可能与新版本的拓扑不兼容,那么这些消息就需要清理/忽略。
  3. 它们会使容错更难实现。既要处理工作进程终止的情况,还要处理单个代理终止的情况。
  4. 它们会降低速度。

因此,Nathan希望在spouts和bolts之间直接传递消息,同时又能为消息处理提供保障。在经过数周的思考之后,他基于随机数和XOR开发了一个算法。该算法只需20个字节就可以跟踪每个spout“元组(Tuple)”,而不管它在下游触发了多少个处理过程。它不仅避免了上述问题,而且性能更好。

构建第一个版本

在接下来的五个月里,Nathan用Clojure构建了Storm的第一个版本。为了将来开源,Storm的所有API都是用Java编写的。这也可以确保Storm有大量的潜在用户。

其次,为了使非JVM语言能够使用Storm,Nathan将拓扑定义为Thrift数据结构。拓扑使用Thrift API进行提交。此外,Nathan还设计了一种协议,使spouts和bolts可以用任何语言实现。他认为,其它语言可以使用Storm使该项目可以为更多人所用,而且向Storm迁移的过程也会变得更简单,因为他们不需要用Java重写现有的实时处理代码。

Nathan是Hadoop的忠实用户,他相信使用已有的Hadoop知识可以更好地设计Storm。比如,Hadoop会产生“僵尸进程”,这些进程会不断的累积占用资源,并最终摧毁集群。产生这个问题的根本原因是,在Hadoop中,终止工作进程的任务由工作进程本身负责。但出于各种原因,它们可能会无法终止。所以,在Storm的设计中,工作进程由启动工作进程的Storm守护进程负责。这使Storm更健壮,永远不会产生僵尸进程。

他还指出了Hadoop的另一个问题。如果JobTracker因为某个原因死掉了,那么任何正在运行的作业都将终止。Storm无法承受这种情况,因为拓扑需要永远运行。因此,Nathan为Storm设计了“进程容错能力”:Storm的守护进程被杀死并重启不会影响正在运行的拓扑。

此外,在Storm开发之初,他就安排实习生Jason Jackson开发了一个在AWS上部署Storm的自动化工具。这极大地加快了迭代速度。

推ter收购BackType

2011年5月,推ter与BackType开始了收购谈判。期间,Nathan在BackType的博客上向外界介绍了Storm,希望以此提升推ter对BackType的估值。推ter对此很感兴趣,他达到了目的。同时,他在博文中将Storm描述为“实时的Hadoop”,这吸引了很多人的注意。这张意外得来的名片非常有益于Storm的后续发展。

开源Storm

2011年7月,正式加入推ter后,Nathan立即就开始计划发布Storm。发布开源软件有两种方式。一种是大肆宣传,并在发布时尽可能地增加曝光。这种方式的风险在于,如果产品质量不高或者信息传达不准确,那么就会在一天之内损失大批的潜在用户。另一种是悄悄地发布代码,让软件慢慢的为人们所接受。这种方法的缺陷在于,人们可能把它看成不重要的项目而忽视它。

Nathan选择了第一种方式。他认为Storm是一款高质量的、有用的软件。而且,基于其第一个项目Cascalog的开源经验,他有信心传达正确的信息。开发布会有如下好处:

  1. 有助于市场营销和推广;
  2. 可以集中地向潜在的早期用户进行演讲,而他们会立即发博客/Tweet/电子邮件,极大地增加曝光;
  3. 可以在会议上大肆宣传,构建人们的项目预期,并保证发布当日能有大量的人关注项目。

因此,他选择了这种方式,并开始了准备工作。首先,他准备在9月份的Strange Loop大会上以《Storm发布》为主题做演讲。在演讲描述部分,他使用了推ter的品牌。接着,他通过推ter公布了Storm的邮件列表,希望以此获得社会认同。社会认同有多种形式,比如项目实际使用情况的文档、GitHub关注者、邮件列表活跃度、邮件列表订阅人、推ter粉丝、关于项目的博文等等。Nathan 指出,如果他在发布当天开启邮件列表,那人们会看到它的活跃度为0,而且几乎没有人订阅。提前公布邮件列表非常有利。如果有人提问和订阅,那他可以借此构建社会认同;如果没人提问和订阅也没关系,因为项目还没发布。

Nathan提到,他在这里有一个失误,就是没有为项目开启推ter账户,因为微博是一个很好的保持项目曝光率的方法。

在会议开始之前这段时间里,Nathan把大部分时间都用在了编写Storm的文档上。他认为文档很重要,人们不了解它,就无法使用它。

一切都按计划进行。2011年9月19日,项目发布当天Storm便获得了大量的关注,在GitHub上有1000多人关注了该项目。项目立即成为了Hacker News的头条。演讲结束后,他就在Hacker News、邮件列表和推ter上在线回答问题。

发布余波

四天内,Storm就成了GitHub上查看最多的Java、Scala或Clojure项目。不到两周,spider.io就宣布已经在生产环境中使用了Storm

Storm发布之后,Nathan开始收到用户的反馈。为了使他们获得尽可能好的使用体验,他在第一个周里就发布了三个小版本。此外,他还在Storm里增加了日志信息,以便用户反馈时可以向他提供。之后,有一年多的时间,他每天都要花1到2个小时回答邮件列表里的问题。

在这一年多的时间里,Nathan在会议、聚会及企业中做了超过25场关于Storm的演讲,使Storm获得了越来越多的曝光。2012年1月,他做过一项调查。结果显示,已经有10个用户在生产环境中使用了Storm,有15个用户即将使用,还有30家企业正在试验。

Nathan为Storm建立了一个“客户案例”页面,以完成社会认同的最后一块拼图。该页面不仅列出了使用Storm的企业,还简单描述了他们的用法。这样,人们就可以了解更多Storm的应用场景,从而可以进一步扩展其应用场景。

Storm的技术演进

在发布后的一年半时间里,Nathan及其团队继续开发Storm,以便它能在推ter内部推广。

大企业对技术的要求不同于创业公司。在创业公司,一个小型团队负责开发、运维和发布所有这些工作。而在大公司,这些工作由多个团队完成。因此,Nathan意识到,他们需要创建一个大型的、共享的集群,可以运行许多独立的应用程序。该集群既要确保应用程序可以得到足够的资源,又要保证一个应用程序出现问题不会影响集群中的其它应用程序。这就是“多租户”。

在建成共享集群后,他们又发现了一个问题。用户总是为他们的拓扑配置远远超出实际需要的资源。这降低了集群效率,用户也失去了优化拓扑的动力。Nathan通过开发“隔离调度器(isolation scheduler)”解决了这些问题。

随着推ter内部Storm用户的增多,他们又发现,用户需要用指标监控他们的拓扑。为此,他们开发了Storm的监控指标API,使用户可以收集任意完全自定义的指标,然后把它们发送给任意监控系统。

Storm的另一大技术跃进是Trident。它是Storm上的一个“微批处理(micro-batching)”API,提供了“仅执行一次”的处理语义。这使Storm可以应用到许多新的场景里。

此外,在使用体验和性能方面还有许多重要的改进。在第一年里,他们平均一个月发布一个版本。每个版本的发布都会提升Storm的知名度。而且,这也从侧面反映出,项目团队能够及时响应用户的问题。

构建开发者社区

Nathan认为,构建社区并使开发人员为项目做贡献是构建开源项目最难的部分。

在Storm发布后的一年半时间里,Nathan推动了Storm的所有开发,所有的变更都要经过他的认可。这样做的好处是,他可以控制项目的每个细节,确保它的质量、使用体验及发展方向。但是,“智者驱动(visionary-driven)” 的开发有一个很大的缺点,就是难以建立一个活跃的开发者社区。首先,Nathan控制着一切,其他人鲜有机会做出重大贡献。其次,他本人成了项目的瓶颈。随着“拉请求(pull request)”越来越多,他疲于处理,这延长了反馈/合并周期,打击了贡献者的积极性。

还有一个缺点是,用户会把他看成是项目的单点故障点。这会限制Storm的发展。

最后,这种方式最糟糕的一方面是Nathan本人承担了太多的工作。其他人无法深入了解整个代码库,从而不可避免地会产生预想不到的变更结果。

离开推ter

2013年3月,Nathan离开了推ter。几个月后,他认识到“共识驱动(consensus-driven)”的开发会更利于Storm的发展。

他认为,在项目的解决方案尚未明确之前,智者驱动的开发是最好的。因为一些关键的设计问题只有对整个项目有深入了解的人才能解决好。但到他离开 推ter的时候,Storm的解决方案已经比较明确了。此后的许多创新工作,如从ZeroMQ切换到Netty、实现安全/身份验证、改进性能/扩展性、提高拓扑可视化等,都是意料之中的。

在Nathan离开推ter之前四个月,Yahoo!的Andy Feng就极力建议他将Storm提交给Apache。其时,他也恰巧在考虑这个问题。他与Hadoop创建者Doug Cutting进行了交谈,从他那里了解了Apache的运作,以及提交到Apache的优缺点。Doug的建议使他真正了解了共识驱动的工作机制。

在Storm最初切换到共识驱动模型时,大部分提交者对代码库的整体把握都非常有限。这是前期智者驱动的结果。但模型切换后,随着时间推移,部分提交者会学习代码库的更多部分,从而在整体上有一个更深层的理解。

Nathan曾担心,转到共识驱动模型会降低软件质量。实际上,也确实有些变更引入了Bug。但这不是大问题,下个版本可以修复这些问题。其实,智者驱动的开发也是如此。

提交给Apache

在离开推ter后,Nathan的精力都用在了新的创业公司上。他需要为Storm选一个长远的家。而之所以选择Apache,是因为它能为Storm提供一个强大的品牌、坚实的法律基础以及共识驱动的模型。

Storm使用ZeroMQ库进行内部进程通信,但ZeroMQ的许可协议与Apache基金会的政策不一致。因此,Yahoo!几名开发人员(后来成为了Storm的提交者)基于Netty创建了替代方案。

在形成Storm最初的提交者列表时,Nathan选择了一些已经为项目做过较大贡献的公司里的开发人员,其中包括其时尚在Health Market Science的Taylor Goetz。他现在就职于Hortonworks,专门从事Storm方面的工作,并担任Storm项目管理委员会主席。

2014年9月,在Andy Feng的帮助下,Nathan向Apache提交了Storm孵化申请

Apache孵化

Nathan写道,Storm进入孵化状态后,他不再是项目瓶颈,开发速度变得越来越快。提交/反馈周期的缩短,这对提交者来说也是一种激励。同时,他会邀请做出重要贡献的人加入提交者行列。

2014年9月17日,Storm正式毕业,升级为顶级项目,而此时离Storm开源尚不足三年。



来自:http://www.infoq.com/cn/news/2014/10/storm-apache-top-level-project