EMC中国研究院 向东
提起Big Data,人们往往会提起大数据的4个V: Volume,Velocity , Variety 以及Value。这四个V从各个侧面说明了大数据并不是新瓶装旧酒: 面对数据产生来源,产生方式,处理方式等等一系列质变,原来适用的数据挖掘/BI工具已经不再满足实际需要,人们迫切需要新的计算模式,基础架构以及开箱即用的工具集来使自己的业务运行的更好。这也是当前大数据如此火热的原因。
流处理(Stream Processing) 或者复杂事件处理(CEP,complex event processing) 也不是一个新概念,对此相关的研究和相应的产品已经有很多了,其中最有名的应该算开源CEP引擎Esper(
http://esper.codehaus.org/)。 相对于原有的产品,现在的流处理新贵,比如来自Yahoo!的S4和来自Twitter的Storm,到底有哪些独到的长处,让人们趋之若鹜? 本文试图在Storm的基础上对此解读。
Storm简介
Storm可以用于3种不同场景: 事件流(stream processing),持续计算(continuous computation)以及分布式RPC (DistributedRPC)。针对这些场景,Storm设计了自己独特的计算模型:
图一: Storm Topology
1. 如图一所示,Storm计算模型以Topology为单位。 一个Topology是由一系列Spout和Bolt构成的图。Events stream会在构成Topology的Spout和Bolt之间流动。Spout负责产生event,而bolt负责处理对接收到的event进行各种处理,得出需要的计算结果。Bolt可以级联,也可以外发送event (往外发送的event可以和接收到的event是同一种类型或者不同类型)。Storm提供了一个简单的教程(
https://github.com/nathanmarz/storm/wiki/Tutorial)来示例Spout/Bolt都做了些什么,是如何被组装成一个Topology的,感兴趣的同学可以深入了解。 @EMC中国研究院 版权所有
2. Streamgrouping控制着在event在Topology中如何流动。如图二所示的Topology中,Spout有2个实例,Bolt A,B, C 分别有4,3,2个实例。Bolt A的实例之一向外送event 时,Stormruntime将按照用户创建Topology时指定的Stream Grouping策略把event发送到Bolt B特定的实例。Storm提供了多种Stream Grouping的实现,比如Shuffle Grouping,Shuffle Grouping可以保证event在Boltinstance间随机分布,每个instance都收到相同数量的event。
图二: Storm Stream grouping
为什么
1. 生逢其时。 MapReduce计算模型打开了分布式计算的另一扇大门,极大的降低了实现分布式计算的门槛。有了MapReduce架构的支持,开发者只需要把注意力集中在如何使用MapReduce的语义来解决具体的业务逻辑,而不用头疼诸如容错,可扩展性,可靠性等一系列硬骨头。一时间,人们拿着MapReduce这把榔头去敲各种各样的钉子,自然而然的也试图用MapReduce计算模型来解决流处理想要解决的问题。各种失败的尝试之后,人们意识到,改良MapReduce并不能使之适应于流处理的场景,必须发展出全新的架构来完成这一任务(MapReduce不适合做流处理的原因Yahoo!在其S4的介绍论文里面有比较详细的阐述,而UC Berkeley的SparkStreaming项目现在正在尝试挑战这一结论,感兴趣的同志请自行查看)。另一方面,人们对传统的CEP解决方案心存疑虑,认为其非分布式的架构可扩展性不够,无法scale out来满足海量的数据处理要求。这时候,Yahoo!的S4以及Twitter的Storm恰到好处的挠到了人们的痒处。
2. 可扩展性。更加明确的说,是scale out的能力。所谓Scale out (
http://en.wikipedia.org/wiki/Scalability#Scale_horizontally_.28scale_out.29), 简单来说就是当一个集群的处理能力不够用的时候,只要往里面再追加一些新的节点,计算有能力迁移到这些新的节点来满足需要。可能的情况下,选择Scale out 而非Scale up,这个观念已经深入人心。一般来说,实现Scale out的关键是Shared nothing architecture,即计算所需要的各种状态都是自满足的,不存在对特定节点强依赖,这样,计算就可以很容易的在节点间迁移,整个系统计算能力不够用的时候,加入新的节点就可以了。Storm的计算模型本身是Scale out友好的,Topology 对应的Spout和Bolt 并不需要和特定节点绑定,可以很容易的分布在多个节点上。此外,Storm还提供了一个非常强大的命令(rebalance),可以动态调整特定Topology中各组成元素(Spout/Bolt)的数量以及其和实际计算节点的对应关系。
3. 系统可靠性。Storm这个分布式流计算框架是建立在Zookeeper的基础上的,大量系统运行状态的元信息都序列化在Zookeeper中。这样,当某一个节点出错时,对应的关键状态信息并不会丢失,换言之Zookeeper的高可用保证了Storm的高可用。文档(https://github.com/nathanmarz/storm/wiki/Fault-tolerance)讨论了Storm各个子系统的错误冗余行为,可以进一步参考。
4. 计算的可靠性。分布式计算涉及到多节点/进程之间的通信和依赖,正确的维护所有参与者的状态和依赖关系,是一件非常有挑战性的任务。Storm实现了一整套机制,确保消息会被完整处理(https://github.com/nathanmarz/storm/wiki/Guaranteeing-message-processing)。 此外,通过Transactional Topology(https://github.com/nathanmarz/storm/wiki/Transactional-topologies) ,Storm可以保证每个tuple“被且仅被处理一次”。@EMC中国研究院 版权所有
6. Clojure基础上的实现。Storm的核心代码是Clojure和Java。Clojure是一门JVM基础上的函数式编程语言(
http://clojure.org/),是支持STM(softwaretransactional memory)的少数几门语言之一。Clojure推出以来,得到了广泛关注,人们普遍认为,其函数式编程所具有的各种特性能在分布式环境中大有用武之地, 而Storm则给出了一个很好的实例。从另一个角度来说,Storm也能大大的推动Clojure的普及。
总言之,时势造英雄,Storm在正确的时间出现在了正确的地点,而且刚刚好做了正确的事情,想不红都没有道理。
Storm系统架构介绍
前面提到了Storm诸多优点,人们不禁会好奇,这一切是如何做到的捏? Storm的设计/实现有什么不同的地方,让它能够脱颖而出?
图三: Storm High Level Architecture
从图三可以看出Storm的总体架构非常简洁和优雅。在一个Storm集群中,有主从两种不同的节点,三种不同的daemon:Nimbus运行在主节点上,通关全局;从节点上运行Supervisor,管理相关节点上的任务;每个从节点上还有一系列的worker process来运行具体任务。和我们熟知的Hadoop不一样的是, 这些daemon间并不直接发送心跳信息或者存在其他RPC控制协议。如图所示,他们之间的信息交换统统是通过Zookeeper来实现。这样的设计虽然引入了Zookeeper这个第三方依赖,但其极大的简化了Nimbus/Supervisor/Worker本身的设计,考虑到Zookeeper已经被广泛接受,已经成为分布式系统metadata store的事实解决方案,Storm在设计时所做的这个折中相当不错。文档(
http://xumingming.sinaapp.com/466/twitter-storm-code-analysis-zookeeper-dirs/)讨论了Storm到底保存了那些状态信息到Zookeeper中,可以详细参考。
作为主节点, Nimbus类似于Hadoop中的Jobtracker,主要负责接收客户端提交的Topology,进行相应的验证,分配任务,进而把任务相关的元信息写入Zookeeper相应目录,此外,Nimbus还负责通过Zookeeper来监控任务执行情况。而Supervisor则类似于TaskTracker,负责会监听任务分配情况,根据实际情况启动/停止工作进程(worker)。相应的,Worker和Hadoop中的map/reduce task很类似,实际的数据处理发生在这里。 不同的是,map/reduce task 终究会结束,但worker则会一直执行下去。同样,Storm引入了WorkerSlot的概念,也就是说,slave节点上的worker的数量是有限的。
图四: Topology实例代码
上述代码定义的Topology共有2个worker,BlueSpout/Green Bolt/Yellow Bolt各自的executor以及task的数量分别是 2/2/6以及2/4/6 (注:1. 如果没有显式的定义task的数量,Storm会默认其数量和component对应的executor数量相同 2. 实际在Storm中执行的Topology和用户定义的有稍许差别,系统会自动增加acker Bolt来保证消息能被处理)。此Topology被Storm执行时,可能的映射如下:
图五: Topology 映射
对于特定的Topology来说,其Task的数量在其整个生命周期是固定的,但是Worker/Executor的数量可以通过命令行和Web UI工具使用rebalance命令来调整。
总结
上文仅仅讨论了Storm系统层面简介优雅的设计保证其做到高可扩展性,高可用的。实际上,Storm的文档代码是一个宝库,值得研究的研究的地方很多,比如,确保消息会被完整处理的实现机制以及Transactional Topology的实现机制;再在比如ZMQ这个高性能的网络通信库是如何被集成的。任何对分布式计算以及大数据感兴趣的同志都应该深入的研究Storm,展望将来,Storm必将被更加广泛的接受的采纳。
原文地址:http://qing.blog.sina.com.cn/2294942122/88ca09aa33002dsh.html