1.1 简单介绍
实时处理Stream流的能力,有容错性,保证性处理机制。对于输入数据,支持消息队列,像RabbitMQ, JMS, Kafka等或者传统的数据库和Hbase。
1.2 基本概念
- Stream
Stream 是最基础的抽象和核心概念,一个没有开始和结束的一连串数据,可以并行创建,被分布式组件并行消费。 - Tuple
在Storm上下文中,stream是一连串没有结束tuple(元组);
A tuple is a named list of values, where each value can be any type
tuple就是一个值(有名称)列表,tuple中的值可以是任何类型的,Storm需要知道怎么序列化Tuple的类型,storm中的tuple支持私有类型、字符串、字节数组等作为它的字段值,如果使用其他类型,就需要序列化该类型。
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//定义输出字段描述
declarer.declareStream("source", new Fields("sentence", "sentence.length"));
}
@Override
public void nextTuple() {
if(index >= sentences.length){
return;
}
//发送字符串
this.collector.emit("source", new Values(sentences[index], sentences[index].length()), "messageID_"+index);
index++;
Utils.sleep(100);
}
1.3 其它简单概念
- 拓扑(Topology):打包好的实时应用计算任务,同Hadoop的MapReduce任务相似。
- 元组(Tuple):是Storm提供的一个轻量级的数据格式,可以用来包装你需要实际处理的数据。
- 流(Streams):数据流(Stream)是Storm中对数据进行的抽象,它是时间上无界的tuple元组序列(无限的元组序列)。
- Spout(喷嘴):Storm中流的来源。Spout从外部数据源,如消息队列中读取元组数据并吐到拓扑里。
- Bolts:在拓扑中所有的计算逻辑都是在Bolt中实现的。
- 任务(Tasks):每个Spout和Bolt会以多个任务(Task)的形式在集群上运行。
- 组件(Component):是对Bolt和Spout的统称。
1.4 Storm集群架构
Storm集群采用主从架构方式,主节点是Nimbus,从节点是Supervisor,有关调度相关的信息存储到ZooKeeper集群中。
- Nimbus
Storm集群的Master节点,负责分发用户代码,指派给具体的Supervisor节点上的Worker节点,去运行Topology对应的组件(Spout/Bolt)的Task。 - Supervisor
Storm集群的从节点,负责管理运行在Supervisor节点上的每一个Worker进程的启动和终止。通过Storm的配置文件中的supervisor.slots.ports配置项,可以指定在一个Supervisor上最大允许多少个Slot,每个Slot通过端口号来唯一标识,一个端口号对应一个Worker进程(如果该Worker进程被启动)。 - Worker
运行具体处理组件逻辑的进程。Worker运行的任务类型只有两种,一种是Spout任务,一种是Bolt任务。 - Task
worker中每一个spout/bolt的线程称为一个task. 在storm0.8之后,task不再与物理线程对应,不同spout/bolt的task可能会共享一个物理线程,该线程称为executor。 - ZooKeeper
用来协调Nimbus和Supervisor,如果Supervisor因故障出现问题而无法运行Topology,Nimbus会第一时间感知到,并重新分配Topology到其它可用的Supervisor上运行
1.5 Topology运行
在Storm中,一个实时应用的计算任务被打包作为Topology发布,这同Hadoop的MapReduce任务相似。但是有一点不同的是:在Hadoop中,MapReduce任务最终会执行完成后结束;而在Storm中,Topology任务一旦提交后永远不会结束,除非你显示去停止任务。计算任务Topology是由不同的Spouts和Bolts,通过数据流(Stream)连接起来的图。一个Storm在集群上运行一个Topology时,主要通过以下3个实体来完成Topology的执行工作:
(1). Worker(进程)
(2). Executor(线程)
(3). Task
worker进程(不同的jvm)执行的是1个topology的子集。1个worker进程会启动1个或多个executor线程来执行topology的component(spout或bolt)。因此,1个运行中的topology就是由集群中多台物理机上的多个worker进程组成的。
executor是1个被worker进程启动的单独线程。每个executor只会运行topology的1个component(spout或bolt)的task(注:task可以是1个或多个,storm默认是1个component只生成1个task,executor线程里会在每次循环里顺序调用所有task实例)。
task是最终运行spout或bolt中代码的单元(注:1个task即为spout或bolt的1个实例,executor线程在执行期间会调用该task的nextTuple或execute方法)。topology启动后,1个component(spout或bolt)的task数目是固定不变的,但该component使用的executor线程数可以动态调整(例如:1个executor线程可以执行该component的1个或多个task实例)。默认情况下task的数目等于executor线程数目,即1个executor线程只运行1个task。
1.6 Storm Streaming Grouping
在Storm中, 开发者可以为上游spout/bolt发射出的tuples指定下游bolt的哪个/哪些task(s)来处理该tuples。这种指定在storm中叫做对stream的分组,即stream grouping
- Shuffle Grouping :随机分组,上游spout/bolt发射的tuples被随机地在下游bolt的tasks中选择一个来处理。bolt的tasks之间的负载比较均衡。
- Fields Grouping :根据指定的字段的值进行分组,举个栗子,流按照“user-id”进行分组,那么具有相同的“user-id”的tuple会发到同一个task,而具有不同“user-id”值的tuple可能会发到不同的task上。这种情况常常用在单词计数,而实际情况是很少用到,因为如果某个字段的某个值太多,就会导致task不均衡的问题。task不共享同一个对象,不是单实例的。
- All grouping :广播分组:将所有的tuple都复制之后再分发给Bolt所有的task,每一个订阅数据流的task都会接收到一份相同的完全的tuple的拷贝。
- Global grouping :全局分组,这种分组会将所有的tuple都发到一个taskid最小的task上。由于所有的tuple都发到唯一一个task上,势必在数据量大的时候会造成资源不够用的情况。所有上游消息全部汇总,便于合并、统计等。
- None grouping :不分组 目前等同于shuffle grouping。
- LocalOrShuffle Grouping : 如果下游bolt的某些task与上游spout/bolt的某些task运行在同一个worker进程中,那么上游spout/bolt的这些task所发射的所有tuples均由下游bolt的同进程的tasks来处理;否则,这种分组方式等同于shuffle grouping。因优先选择同进程task间传输而降低tuple网络传输代价,但因寻找同进程的task而消耗CPU和内存资源
- Direct grouping :直接分组,允许上游spout/bolt决定其发射出的任一tuple由下游bolt的哪个task接收并处理