博客公告:
(1)本博客所有博客文章搬迁至《博客虫》http://blogchong.com/
(4)该博客内容还会继续更新,不过会慢一些。
该文档为原创文档,若要转载,请标明出处:http://blog.sina.com.cn/s/blog_8c243ea30101ili0.html
类型 | 详细 |
备注 | 该文档为storm 0.8.2的调研及部署文档,相关代码部分随后提供 |
相关描述 | ² ² ² ² |
目录
2.3.1安装Python-2.6.6或以上版本... 10
1 Storm调研
1.1 Storm简介
Twitter Storm 是使用 Clojure(发音同 closure)语言实现的。 Clojure 是Lisp 语言的一种现代方言。类似于 Lisp,Clojure 支持一种功能性编程风格,但 Clojure还引入了一些特性来简化多线程编程(一种对创建 Storm 很有用的特性)。Twitter列举了Storm的三大类应用:
1. 信息流处理{Stream processing}
Storm可用来实时处理新数据和更新数据库,兼具容错性和可扩展性。
2. 连续计算{Continuous computation}
Storm可进行连续查询并把结果即时反馈给客户端。比如把Twitter上的热门话题发送到浏览器中。
3. 分布式远程程序调用{Distributed RPC}
Storm可用来并行处理密集查询。Storm的拓扑结构是一个等待调用信息的分布函数,当它收到一条调用信息后,会对查询进行计算,并返回查询结果。举个例子DistributedRPC可以做并行搜索或者处理大集合的数据。
Storm集群由一个主节点和多个工作节点组成。主节点运行了一个名为“Nimbus”的守护进程,用于分配代码、布置任务及故障检测。每个工作节点都运行了一个名为“Supervisor”的守护进程,用于监听工作,开始并终止工作进程。Nimbus和Supervisor都能快速失败,而且是无状态的,这样一来它们就变得十分健壮,两者的协调工作是由ApacheZooKeeper来完成的。Storm的术语包括Stream、Spout、Bolt、Task、Worker、StreamGrouping和Topology。Stream是被处理的数据。Sprout是数据源。Bolt处理数据。Task是运行于Spout或Bolt中的线程。Worker是运行这些线程的进程。StreamGrouping规定了Bolt接收什么东西作为输入数据。数据可以随机分配(术语为Shuffle),或者根据字段值分配(术语为Fields),或者广播(术语为All),或者总是发给一个Task(术语为Global),也可以不关心该数据(术语为None),或者由自定义逻辑来决定(术语为Direct)。Topology是由Stream Grouping连接起来的Spout和Bolt节点网络。
1.2 Storm特点
Storm的主要特点:
1.
2.
3.
4.
5.
6.
7.
Storm与Hadoop的对比:
hadoop是实现了mapreduce的思想,将数据切片计算来处理大量的离线数据。hadoop处理的数据必须是已经存放在hdfs上或者类似hbase的数据库中,所以hadoop实现的时候是通过移动计算到这些存放数据的机器上来提高效率而storm不同,storm是一个流计算框架,处理的数据是实时消息队列中的,所以需要我们写好一个topology逻辑放在那,接收进来的数据来处理,所以是通过移动数据平均分配到机器资源来获得高效率。
hadoop的优点是处理数据量大(瓶颈是硬盘和namenode,网络等)[[,分析灵活,可以通过实现dsl,mdx等拼接hadoop命令或者直接使用hive,pig等来灵活分析数据。适应对大量维度进行组合分析缺点就是慢:每次执行前要分发jar包,hadoop每次map数据超出阙值后会将数据写入本地文件系统,然后在reduce的时候再读进来
storm的优点是全内存计算,因为内存寻址速度是硬盘的百万倍以上,所以storm的速度相比较hadoop非常快(瓶颈是内存,cpu)缺点就是不够灵活:必须要先写好topology结构来等数据进来分析
1.3 Storm架构
1.3.1 Storm集群组成
Storm集群类似于一个Hadoop集群。 然而你在Hadoop的运行“MapReduce job”,在storm上你运行“topologies (不好翻译)”。 “job”和“topologies ”本身有很大的不同 -一个关键的区别是,MapReduce的工作最终完成,而topologies处理消息永远保持(或直到你杀了它)。Strom集群有主要有两类节点:主节点和工作节点。 主节点上运行一个叫做“Nimbus”的守护进程,也就是类似Hadoop的“JobTracker”。 Nimbus负责在集群分发的代码,将任务分配给其他机器,和故障监测。
每个工作节点运行一个叫做”Supervisor”的守护进程 。Supervisor监听分配给它的机器,根据Nimbus 的委派在必要时 启动和关闭工作进程。 每个工作进程执行topology的一个子集。一个运行中的topology 由很多运行在很多机器上的工作进程组成。
Nimbus 和Supervisors 之间所有的协调工作是通过 一个Zookeeper 集群。 此外,Nimbus的守护进程和Supervisors 守护进程是无法连接和无状态的;所有的状态维持在Zookeeper中 或保存在本地磁盘上。这意味着你可以kill -9 Nimbus 或Supervisors进程,所以他们不需要做备份。这种设计导致storm集群具有令人难以置信的稳定性。
1.3.1 Storm拓扑结构
Storm 实现了一种数据流模型,其中数据持续地流经一个转换实体网络(参见 图1)。一个数据流的抽象称为一个流,这是一个无限的元组序列。元组就像一种使用一些附加的序列化代码来表示标准数据类型(比如整数、浮点和字节数组)或用户定义类型的结构。每个流由一个唯一ID 定义,这个 ID 可用于构建数据源和接收器 (sink) 的拓扑结构。流起源于喷嘴(spout),Spout将数据从外部来源流入Storm 拓扑结构中。
图 1. 一个普通的 Storm 拓扑结构的概念性架构
接收器(或提供转换的实体)称为螺栓(bolt)。螺栓实现了一个流上的单一转换和一个 Storm拓扑结构中的所有处理。Bolt既可实现 MapReduce之类的传统功能,也可实现更复杂的操作(单步功能),比如过滤、聚合或与数据库等外部实体通信。典型的 Storm拓扑结构会实现多个转换,因此需要多个具有独立元组流的Bolt。Bolt和Spout都实现为Linux系统中的一个或多个任务。
但是,Storm 架构中一个最有趣的特性是有保障的消息处理。Storm可保证一个Spout发射出的每个元组都会处理;如果它在超时时间内没有处理,Storm会从该Spout重放该元组。此功能需要一些聪明的技巧来在拓扑结构中跟踪元素,也是 Storm 的重要的附加价值之一。
除了支持可靠的消息传送外,Storm 还使用 ZeroMQ最大化消息传送性能(删除中间排队,实现消息在任务间的直接传送)。ZeroMQ合并了拥塞检测并调整了它的通信,以优化可用的带宽。
1.4 Storm原理
1.4.1 Storm组成
计算拓扑: Topologies
一个实时计算应用程序的逻辑在storm里面被封装到topology对象里面, 我把它叫做计算拓补.Storm里面的topology相当于Hadoop里面的一个MapReduce Job, 它们的关键区别是:一个MapReduceJob最终总是会结束的, 然而一个storm的topoloy会一直运行 —除非你显式的杀死它。一个Topology是Spouts和Bolts组成的图状结构, 而链接Spouts和Bolts的则是Streamgroupings。
消息流: Streams
消息流是storm里面的最关键的抽象对象。一个消息流是一个没有边界的tuple序列,而这些tuples会被以一种分布式的方式并行地创建和处理。对消息流的定义主要是对消息流里面的tuple的定义,我们会给tuple里的每个字段一个名字。并且不同tuple的对应字段的类型必须一样。也就是说:两个tuple的第一个字段的类型必须一样,第二个字段的类型必须一样,但是第一个字段和第二个字段可以有不同的类型。在默认的情况下,tuple的字段类型可以是: integer, long, short, byte, string, double, float,boolean和byte array。你还可以自定义类型 — 只要你实现对应的序列化器。
每个消息流在定义的时候会被分配给一个id, 因为单向消息流是那么的普遍,OutputFieldsDeclarer定义了一些方法让你可以定义一个stream而不用指定这个id。在这种情况下这个stream会有个默认的id:1.
消息源: Spouts
消息源Spouts是storm里面一个topology里面的消息生产者。一般来说消息源会从一个外部源读取数据并且向topology里面发出消息:tuple。消息源Spouts可以是可靠的也可以是不可靠的。一个可靠的消息源可以重新发射一个tuple如果这个tuple没有被storm成功的处理,但是一个不可靠的消息源Spouts一旦发出一个tuple就把它彻底忘了— 也就不可能再发了。
消息源可以发射多条消息流stream。要达到这样的效果,使用OutFieldsDeclarer.declareStream来定义多个stream,然后使用SpoutOutputCollector来发射指定的stream。
Spout类里面最重要的方法是nextTuple要么发射一个新的tuple到topology里面或者简单的返回如果已经没有新的tuple了。要注意的是nextTuple方法不能blockSpout的实现, 因为storm在同一个线程上面调用所有消息源Spout的方法。
另外两个比较重要的Spout方法是ack和fail。storm在检测到一个tuple被整个topology成功处理的时候调用ack,否则调用fail。storm只对可靠的spout调用ack和fail。
消息处理者: Bolts
所有的消息处理逻辑被封装在bolts里面。 Bolts可以做很多事情: 过滤, 聚合, 查询数据库等等等等。
Bolts可以简单的做消息流的传递。复杂的消息流处理往往需要很多步骤,从而也就需要经过很多Bolts。比如算出一堆图片里面被转发最多的图片就至少需要两步:第一步算出每个图片的转发数量。第二步找出转发最多的前10个图片。(如果要把这个过程做得更具有扩展性那么可能需要更多的步骤)。
Bolts可以发射多条消息流,使用OutputFieldsDeclarer.declareStream定义stream,使用OutputCollector.emit来选择要发射的stream。
Bolts的主要方法是execute,它以一个tuple作为输入,Bolts使用OutputCollector来发射tuple,Bolts必须要为它处理的每一个tuple调用OutputCollector的ack方法,以通知storm这个tuple被处理完成了。–从而我们通知这个tuple的发射者Spouts。 一般的流程是: Bolts处理一个输入tuple, 发射0个或者多个tuple,然后调用ack通知storm自己已经处理过这个tuple了。storm提供了一个IBasicBolt会自动调用ack。
Stream groupings: 消息分发策略
定义一个Topology的其中一步是定义每个bolt接受什么样的流作为输入。streamgrouping就是用来定义一个stream应该如果分配给Bolts上面的多个Tasks。
storm里面有6种类型的stream grouping:
1. Shuffle Grouping:随机分组,随机派发stream里面的tuple,保证每个bolt接收到的tuple数目相同。
2. FieldsGrouping:按字段分组,比如按userid来分组,具有同样userid的tuple会被分到相同的Bolts,而不同的userid则会被分配到不同的Bolts。
3. All Grouping: 广播发送, 对于每一个tuple, 所有的Bolts都会收到。
4. Global Grouping:全局分组,这个tuple被分配到storm中的一个bolt的其中一个task。再具体一点就是分配给id值最低的那个task。
5. Non Grouping:不分组,这个分组的意思是说stream不关心到底谁会收到它的tuple。目前这种分组和Shufflegrouping是一样的效果,有一点不同的是storm会把这个bolt放到这个bolt的订阅者同一个线程里面去执行。
6. Direct Grouping:直接分组,这是一种比较特别的分组方法,用这种分组意味着消息的发送者举鼎由消息接收者的哪个task处理这个消息。只有被声明为DirectStream的消息流可以声明这种分组方法。而且这种消息tuple必须使用emitDirect方法来发射。消息处理者可以通过TopologyContext来或者处理它的消息的taskid(OutputCollector.emit方法也会返回taskid)
1.4.2 Storm工作原理
在Storm中有对于流stream的抽象,流是一个不间断的无界的连续tuple,注意Storm在建模事件流时,把流中的事件抽象为tuple即元组,后面会解释storm中如何使用tuple。
Storm认为每个stream都有一个stream源,也就是原始元组的源头,所以它将这个源头抽象为spout,spout可能是连接twitterapi并不断发出tweets,也可能是从某个队列中不断读取队列元素并装配为tuple发射。
有了源头即spout也就是有了stream,那么该如何处理stream内的tuple呢,同样的思想twitter将流的中间状态转换抽象为Bolt,bolt可以消费任意数量的输入流,只要将流方向导向该bolt,同时它也可以发送新的流给其他bolt使用,这样一来,只要打开特定的spout(管口)再将spout中流出的tuple导向特定的bolt,又bolt对导入的流做处理后再导向其他bolt或者目的地。
我们可以认为spout就是一个一个的水龙头,并且每个水龙头里流出的水是不同的,我们想拿到哪种水就拧开哪个水龙头,然后使用管道将水龙头的水导向到一个水处理器(bolt),水处理器处理后再使用管道导向另一个处理器或者存入容器中。
为了增大水处理效率,我们很自然就想到在同个水源处接上多个水龙头并使用多个水处理器,这样就可以提高效率。没错Storm就是这样设计的,看到下图我们就明白了。
对应上文的介绍,我们可以很容易的理解这幅图,这是一张有向无环图,Storm将这个图抽象为Topology即拓扑(的确,拓扑结构是有向无环的),拓扑是storm中最高层次的一个抽象概念,它可以被提交到storm集群执行,一个拓扑就是一个流转换图,图中每个节点是一个spout或者bolt,图中的边表示bolt订阅了哪些流,当spout或者bolt发送元组到流时,它就发送元组到每个订阅了该流的bolt(这就意味着不需要我们手工拉管道,只要预先订阅,spout就会将流发到适当bolt上)。
插个位置说下storm的topology实现,为了做实时计算,我们需要设计一个拓扑图,并实现其中的Bolt处理细节,Storm中拓扑定义仅仅是一些Thrift结构体,这样一来我们就可以使用其他语言来创建和提交拓扑。
Storm将流中元素抽象为tuple,一个tuple就是一个值列表valuelist,list中的每个value都有一个name,并且该value可以是基本类型,字符类型,字节数组等,当然也可以是其他可序列化的类型。拓扑的每个节点都要说明它所发射出的元组的字段的name,其他节点只需要订阅该name就可以接收处理。
2 Storm安装部署
2.1 搭建Storm集群的主要步骤
1.
2.
Ø
Ø
Ø
Ø
1.
2.
2.2 布置ZK集群
安装ZK:
wgethttp://labs.mop.com/apache-mirror/zookeeper/zookeeper-3.3.5/zookeeper-3.3.5.tar.gz
配置ZK集群:
(1)配置Zookeeper路径下的conf/zoo.cfg
dataDir=/tmp/zookeeper
#即之前创建的文件夹,可以自定
clientPort=2181
server.1=XXX.XXX.XXX.XXX:3887:4887
server.2=XXX.XXX.XXX.XXX:3887:4887
server.3=XXX.XXX.XXX.XXX:3887:4887
(2)在对应IP的节点上的/tmp/zookeeper目录中,创建myid文件,该文件中只包涵一个数字(对应server1/2/3)。
(3)进入Zookeeper的安装目录的bin/目录,使用命令zkServer.sh start将zookeeper守护进程启动。
2.3 安装相关软件
2.3.1安装Python-2.6.6或以上版本
wget
tar zxvf Python-2.6.6.tgz.
cd Python-2.6.6 ./configure
make
make install
vi /etc/ld.so.conf
追加/usr/local/lib/
sudo ldconfig
2.3.2 安装zeromq
wget
tar zxf zeromq-2.1.7.tar.gz
cd zeromq-2.1.7
./configure
make
make install
sudo ldconfig
2.3.3 安装jzmq
git clone git://github.com/nathanmarz/jzmq.git
cd jzmq
./autogen.sh
./configure
make
make install
2.3.4 安装storm
wget http://cloud.github.com/downloads/nathanmarz/storm/storm-0.8.1.zip
unzip storm-0.8.1.zip
mv storm-0.8.1 /usr/local/
vim ~/.bashrc #配置环境变量
export STORM_HOME=/usr/local/storm-0.8.1
export PATH=$PATH:$STORM_HOME/bin
mkdir/tmp/storm
2.4 配置Storm.yaml
#配置storm集群使用的zookeeper集群的地址
storm.zookeeper.server:
-
-
-
#配置storm信息存储目录
Storm.local.dir: “/tmp/storm”
#配置java.library.path
#配置工作机器的工作端口。
-
-
-
-
2.5 启动Storm集群
(1)监控Nimbus
(2)Supervisor
(3)UI