Storm基础篇

来源:互联网 发布:java上线项目实战视频 编辑:程序博客网 时间:2024/05/16 15:38

什么是Storm呢?

Storm是自由的,开源的分布式实时计算系统,和hadoop的批处理相似,都具有强大的对数据处理的能力,只不过处理方式不同罢了。

Storm可以对大量的数据流进行一条一条可靠的实时处理,而hadoop则是对数据进行一批一批的处理,不像storm一样可以实时处理,分布式计算系统流处理方式

是大数据处理的一个重要方向。

下面就给大家简单的介绍一下Storm基本概念:

Storm系统中包含以下几个基本概念:

  1. 拓扑(Topologies

  2. 流(Streams

  3. 数据源(Spouts

  4. 数据流处理组件(Bolts

  5. 数据流分组(Streamgroupings

  6. 可靠性(Reliability

  7. 任务(Tasks

  8. 工作进程(Workers

拓扑(Topologies

Storm的拓扑是对实时计算应用逻辑的封装,它的作用与MapReduce的任务(Job)很相似,区别在于MapReduce的一个Job在得到结果之后总会结束,而拓扑会一直在集群中运行,直到你手动去终止它。拓扑还可以理解成由一系列通过数据流(StreamGrouping)相互关联的SpoutBolt组成的的拓扑结构。SpoutBolt称为拓扑的组件(Component)。我们会在后文中给出这些概念的解释。



数据流(Streams

数据流(Streams)是Storm中最核心的抽象概念。一个数据流指的是在分布式环境中并行创建、处理的一组元组(tuple)的无界序列。数据流可以由一种能够表述数据流中元组的域(fields)的模式来定义。在默认情况下,元组(tuple)包含有整型(Integer)数字、长整型(Long)数字、短整型(Short)数字、字节(Byte)、双精度浮点数(Double)、单精度浮点数(Float)、布尔值以及字节数组等基本类型对象。当然,你也可以通过定义可序列化的对象来实现自定义的元组类型。

在声明数据流的时候需要给数据流定义一个有效的id。不过,由于在实际应用中使用最多的还是单一数据流的SpoutBolt,这种场景下不需要使用id来区分数据流,因此可以直接使用OutputFieldsDeclarer来定义“无id”的数据流。实际上,系统默认会给这种数据流定义一个名为“default”id

数据源(Spouts

数据源(Spout)是拓扑中数据流的来源。一般Spout会从一个外部的数据源读取元组然后将他们发送到拓扑中。根据需求的不同,Spout既可以定义为可靠的数据源,也可以定义为不可靠的数据源。一个可靠的Spout能够在它发送的元组处理失败时重新发送该元组,以确保所有的元组都能得到正确的处理;相对应的,不可靠的Spout就不会在元组发送之后对元组进行任何其他的处理。

一个 Spout可以发送多个数据流。为了实现这个功能,可以先通过OutputFieldsDeclarerdeclareStream方法来声明定义不同的数据流,然后在发送数据时在SpoutOutputCollectoremit方法中将数据流id作为参数来实现数据发送的功能。

Spout中的关键方法是nextTuple。顾名思义,nextTuple要么会向拓扑中发送一个新的元组,要么会在没有可发送的元组时直接返回。需要特别注意的是,由于Storm是在同一个线程中调用所有的Spout方法,nextTuple不能被Spout的任何其他功能方法所阻塞,否则会直接导致数据流的中断(关于这一点,阿里的JStorm修改了Spout的模型,使用不同的线程来处理消息的发送,这种做法有利有弊,好处在于可以更加灵活地实现Spout,坏处在于系统的调度模型更加复杂,如何取舍还是要看具体的需求场景吧——译者注)。

Spout中另外两个关键方法是ackfail,他们分别用于在Storm检测到一个发送过的元组已经被成功处理或处理失败后的进一步处理。注意,ackfail方法仅仅对上述“可靠的”Spout有效。

数据流处理组件(Bolts

拓扑中所有的数据处理均是由 Bolt完成的。通过数据过滤(filtering)、函数处理(functions)、聚合(aggregations)、联结(joins)、数据库交互等功能,Bolt几乎能够完成任何一种数据处理需求。

一个 Bolt可以实现简单的数据流转换,而更复杂的数据流变换通常需要使用多个Bolt并通过多个步骤完成。例如,将一个微博数据流转换成一个趋势图像的数据流至少包含两个步骤:其中一个Bolt用于对每个图片的微博转发进行滚动计数,另一个或多个Bolt将数据流输出为“转发最多的图片”结果(相对于使用2Bolt,如果使用3Bolt你可以让这种转换具有更好的可扩展性)。

Spout相同,Bolt也可以输出多个数据流。为了实现这个功能,可以先通过OutputFieldsDeclarerdeclareStream方法来声明定义不同的数据流,然后在发送数据时在OutputCollectoremit方法中将数据流id作为参数来实现数据发送的功能。

在定义 Bolt的输入数据流时,你需要从其他的Storm组件中订阅指定的数据流。如果你需要从其他所有的组件中订阅数据流,你就必须要在定义Bolt时分别注册每一个组件。对于声明为默认id(即上文中提到的“default”——译者注)的数据流,InputDeclarer支持订阅此类数据流的语法糖。也就是说,如果需要订阅来自组件“1”的数据流,declarer.shuffleGrouping("1")declarer.shuffleGrouping("1",DEFAULT_STREAM_ID)两种声明方式是等价的。

Bolt的关键方法是execute方法。execute方法负责接收一个元组作为输入,并且使用OutputCollector对象发送新的元组。如果有消息可靠性保障的需求,Bolt必须为它所处理的每个元组调用OutputCollectorack方法,以便Storm能够了解元组是否处理完成(并且最终决定是否可以响应最初的Spout输出元组树)。一般情况下,对于每个输入元组,在处理之后可以根据需要选择不发送还是发送多个新元组,然后再响应(ack)输入元组。IBasicBolt接口能够实现元组的自动应答。

数据流分组(Streamgroupings

    为拓扑中的每个 Bolt的确定输入数据流是定义一个拓扑的重要环节。数据流分组定义了在Bolt的不同任务(tasks)中划分数据流的方式。

Storm中有八种内置的数据流分组方式(原文有误,现在已经已经有八种分组模型——译者注),而且你还可以通过CustomStreamGrouping接口实现自定义的数据流分组模型。这八种分组分时分别为:

  1. 随机分组(Shufflegrouping):这种方式下元组会被尽可能随机地分配到Bolt的不同任务(tasks)中,使得每个任务所处理元组数量能够能够保持基本一致,以确保集群的负载均衡。

  2. 域分组(Fieldsgrouping):这种方式下数据流根据定义的“域”来进行分组。例如,如果某个数据流是基于一个名为“user-id”的域进行分组的,那么所有包含相同的“user-id”的元组都会被分配到同一个任务中,这样就可以确保消息处理的一致性。

  3. 部分关键字分组(PartialKey grouping):这种方式与域分组很相似,根据定义的域来对数据流进行分组,不同的是,这种方式会考虑下游Bolt数据处理的均衡性问题,在输入数据源关键字不平衡时会有更好的性能1。感兴趣的读者可以参考这篇论文,其中详细解释了这种分组方式的工作原理以及它的优点。

  4. 完全分组(Allgrouping):这种方式下数据流会被同时发送到Bolt的所有任务中(也就是说同一个元组会被复制多份然后被所有的任务处理),使用这种分组方式要特别小心。

  5. 全局分组(Globalgrouping):这种方式下所有的数据流都会被发送到Bolt的同一个任务中,也就是id最小的那个任务。

  6. 非分组(Nonegrouping):使用这种方式说明你不关心数据流如何分组。目前这种方式的结果与随机分组完全等效,不过未来Storm社区可能会考虑通过非分组方式来让Bolt和它所订阅的SpoutBolt在同一个线程中执行。

  7. 直接分组(Directgrouping):这是一种特殊的分组方式。使用这种方式意味着元组的发送者可以指定下游的哪个任务可以接收这个元组。只有在数据流被声明为直接数据流时才能够使用直接分组方式。使用直接数据流发送元组需要使用OutputCollector的其中一个emitDirect方法。Bolt可以通过TopologyContext来获取它的下游消费者的任务id,也可以通过跟踪OutputCollectoremit方法(该方法会返回它所发送元组的目标任务的id)的数据来获取任务id

  8. 本地或随机分组(Localor shuffle grouping):如果在源组件的worker进程里目标Bolt有一个或更多的任务线程,元组会被随机分配到那些同进程的任务中。换句话说,这与随机分组的方式具有相似的效果。

可靠性(Reliability

    Storm可以通过拓扑来确保每个发送的元组都能得到正确处理。通过跟踪由Spout发出的每个元组构成的元组树可以确定元组是否已经完成处理。每个拓扑都有一个“消息延时”参数,如果Storm在延时时间内没有检测到元组是否处理完成,就会将该元组标记为处理失败,并会在稍后重新发送该元组。

为了充分利用 Storm的可靠性机制,你必须在元组树创建新结点的时候以及元组处理完成的时候通知Storm。这个过程可以在Bolt发送元组时通过OutputCollecto实现:在emit方法中实现元组的锚定(Anchoring),同时使用ack方法表明你已经完成了元组的处理。


任务(Tasks

    在 Storm集群中每个SpoutBolt都由若干个任务(tasks)来执行。每个任务都与一个执行线程相对应。数据流分组可以决定如何由一组任务向另一组任务发送元组。你可以在TopologyBuildersetSpout方法和setBolt方法中设置Spout/Bolt的并行度。

工作进程(Workers

拓扑是在一个或多个工作进程(workerprocesses)中运行的。每个工作进程都是一个实际的JVM进程,并且执行拓扑的一个子集。例如,如果拓扑的并行度定义为300,工作进程数定义为50,那么每个工作进程就会执行6个任务(进程内部的线程)。Storm会在所有的worker中分散任务,以便实现集群的负载均衡。


原创粉丝点击