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

  • 谢丽

2014 年 10 月 14 日

话题:Apache语言 & 开发架构

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 的自动化工具。这极大地加快了迭代速度。

Twitter 收购 BackType

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

开源 Storm

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

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

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

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

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

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

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

发布余波

四天内,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,以便它能在 Twitter 内部推广。

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

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

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

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

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

构建开发者社区

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

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

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

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

离开 Twitter

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

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

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

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

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

提交给 Apache

在离开 Twitter 后,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 开源尚不足三年。


感谢郭蕾对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。

Apache语言 & 开发架构