Strom
storm
实时分析概念
离线分析
通常是 需要一段时间的数据积累 积累到一定数量数据后 开始离线分析 无论数据量多大 离线分析 有开始 也有结束 最终得到一个处理的结果 这样的分析过程 得到的结果是有较大的延迟的
实时分析
通常 数据不停的到来 随着数据的到来 来进行增量的运算 立即得到新数据的处理结果 并没有一个数据积累的过程 有开始 但是没有明确的结束的时刻 数据实时的进行运算 基本没有延迟
Storm概述
Storm是一个开源的分布式实时计算系统,可以简单、可靠的处理大量的数据流。
Storm有很多使用场景:如实时分析,在线机器学习,持续计算,分布式RPC,ETL等等。
Storm支持水平扩展,具有高容错性,保证每个消息都会得到处理。
Storm性能优良,处理速度很快(在一个小集群中,每个结点每秒可以处理数以百万计的消息)。
Storm的部署和运维都很便捷,而且更为重要的是可以使用任意编程语言来开发应用。
Storm组件
Topology(拓扑)
Topology由Spout(喷嘴) 和 Bolt(阀门)组成
Spout(喷嘴)
Spout负责连接外部数据源 接收数据 并将数据 转换为Tuple格式 向后发送 可以发送给 一个或多个Bolt
Spout通常只负责接收 转换 发送数据,不会进行任何的业务处理
Bolt(阀门)
Bolt负责接收上游传入的Tuple数据,进行相应的运算,再将结果发送给后续的零个或多个Bolt
从而利用Spuot和Bolt组建起复杂的数据流处理网络 实现实时处理
Storm将实时运算的过程 拆分为若干简单的步骤 再组装在一起完成复杂计算任务 以便于实现分布式的流式处理
Storm的并发机制
Storm可以在如下的四个级别上存在并发
1)Node服务器
配置在Storm集群中的一个服务器,会执行Topology的一部分运算,一个Storm集群中包含一个或者多个Node
2) Worker进程
JVM虚拟机、进程。指一个Node上相互独立运作的JVM进程,每个Node可以配置运行一个或多个worker。一个Topology会分配到一个或者多个worker上运行。
3)Executor线程
指一个worker的jvm中运行的java线程。多个task可以指派给同一个executer来执行。除非是明确指定,Storm默认会给每个executor分配一个task。
4)Task任务
bolt/spout实例。task是spout或bolt的实例,他们的nextTuple()和execute()方法会被executors线程调用执行。一个线程可以同时处理多个Task。
storm的并发控制
默认并发控制
如果不进行任何配置,默认并发度为1,也即,默认情况下一个Node服务器上运行一个Worker进程,一个Worker进程里分配多个Executor线程,而每个Executor线程分别负责一个Task任务,此时唯一的并发处在线程级别。
人为指定并发度
Node级别
Node级别的并发度无法通过代码控制,只能增加服务器硬件资源。
Worker级别
可以通过代码为指定的Topology配置多个Worker。Config conf = new Config();conf.setNumWorkers(2);
**单机测试模式下,设定Worker的数量没有意义,不会起效果。
Executor级别
可以通过代码指定Spout或Bolt需要几个线程builder.setSpout(spout_id,spout,2)builder.setBolt(bolt_id,bolt,2)
** 如果只是指定线程数量,而未指定Task数量,则这个组件就会有指定数量的线程来处理,而默认每个线程内都执行一个该组件的Task
Task级别
可以通过代码指定Spout或Bolt需要并发几个Task。builder.setSpout(...).setNumTasks(2);builder.setBolt(...).setNumTasks(2);
**指定的Task会在线程内部执行,如果指定的Task的数量多于线程的数量,则这些Task自动分配到这些线程内执行。
数据流分组方式---内置了七种数据流分组方式
组件向后发送tuple时,涉及到如何为后续组件的多个task分配这些tuple的问题,这是通过在连接组件时使用不同的方法,从而指定数据流分组方式来解决的。
Shuffle Grouping(随机分组)
随机分发数据流中的tuple给bolt中的各个task,每个task接收到的tuple数量相同。
Fields Grouping(按字段分组)
根据指定字段的值进行分组。指定字段具有相同值的tuple会路由到同一个bolt中的task中。
All Grouping(全复制分组)
所有的tuple复制后分发给后续bolt的所有的task。
Globle Grouping(全局分组)
这种分组方式将所有的tuple路由到唯一一个task上
Storm按照最小task id来选取接受数据的task
这种分组方式下配置bolt和task的并发度没有意义。
这种方式会导致所有tuple都发送到一个JVM实例上,可能会引起Strom集群中某个JVM或者服务器出现性能瓶颈或崩溃。
None Grouping(不分组)
在功能上和随机分组相同,为将来预留。
Direct Grouping(指向型分组)
数据源会通过emitDirect()方法来判断一个tuple应该由哪个Strom组件来接受。
只能在声明为指向型的数据流上使用。
Local or shuffle Grouping(本地或随机分组)
和随机分组类似,但是,会优先将tuple分发给同一个worker内的bolt的task,只有在同一个Worker内没有目标bolt的task的情况下采用随机分组方式。
这种方式可以减少网络传输,从而提高topology的性能。
Storm可靠性概述
Storm是分布式程序,进程之间是通过网络来通信的,此时可能因为网络的不稳定、节点宕机等原因造成数据可能存在丢失的情况,这就给Storm的实时处理带来了数据丢失的可能
事务性拓扑
Storm中的可靠性分为三个级别:
至多一次 - 不多数据,但是可能会丢数据
WC案例中,没有任何并发控制,此时就是 至多一次的并发状态,不会多数据,但是可能会因为网络延迟 程序出错 造成数据的丢失。
至少一次 - 不丢数据,但是可能会多数据
在分布式的环境下,因为网络的不稳定性,数据丢失是无法彻底避免的,所以为了保证数据不丢失,应该检测数据丢失的情况,并重发丢失的数据。
这种方式下可以保证不丢数据,但是可能因为重发造成多数据。
对于Spout:
持有已发数据一段时间
实现ack()和fail()
对于Bolt:
需要在发送tuple的过程中实现锚定
调用collector.ack() 或 collector.fail()
恰好一次 - 不丢也不多,恰好完整的处理
在实现了至少一次语义之后,已经可以保证数据不丢,但是可能会多
想要实现恰好一次的语义,需要在以上的基础上,去除重复的数据
想要去除重复的数据,需要能够识别每条数据,并在处理过程中对已经处理过的数据的重发的数据进行抛弃
方案1:
想要唯一识别每条数据,可以为每条数据指定唯一的编号,这个编号是一个递增的数字,在重发时保持不变,通过这个编号唯一的识别tuple
后续处理过程中,记录所有已经处理过的tuple的编号,后续的tuple要处理之前,需要和这些已经处理过的tuple的编号做比较,如果发现已经处理过,则抛弃,如果没有处理过就正常处理
此方案,需要记录所有已经处理过的tuple的编号,且由于在任何时间点都无法确定之后会不会有指定tuple的重发,这些编号都不能删除,这样一来,随着storm的运行,编号越记越多,浪费大量存储空间,且,比较的速度越来越慢。在空间和时间上都无法接收。此方法不可行。
方案2:
想要解决方案1的问题,可以让所有的tuple的编号严格按照顺序递增,且要求整个topology严格按照顺序处理这些tuple,则只需要记录最后一个处理过的tuple的编号即可,后续再有tuple过来,如果编号小于等于记录中的tuple编号,则说明是之前已经处理过的tuple重发的数据,抛弃该数据。如果编号大于记录中的编号,则是正常数据,进行处理并更新记录中的最后一次处理的tuple编号即可。
此方案,需要严格保证tuple处理的顺序,可以要求所有的tuple依次处理,一个tuple处理时,其他tuple排队等待,可以保证顺序,但是效率非常低下。此方案不可行。
方案3:
想要解决方案2的问题,则要求每次发一个批的数据,一个批中有若干tuple,每个批都被赋予一个递增的编号,批中任何一个tuple出错,整批重发,重发过程中编号不变。所有的批依次处理,一个批处理的过程中,其他批排队等待,保证顺序,批之间串行,但批内部有很多tuple,是可以并发的,这种方案,比起方案2,效率有了一定提升。
此方案,虽然比方案2效率提升了,但是批和批之间仍然串行处理,性能依然受限。
方案4:
通过分析发现,在整个Topology中,并不是所有的Bolt都需要严格保证一次且一次的语义,只要抓住最关键的一个或几个步骤,保证一次且一次的语义,就可以保障数据计算不出问题,基于这样的分析,可以将整个Topology中的Bolt分为Process阶段 和 Commit阶段两类。批在Porcess阶段的Bolt中随意并发保证性能,但一旦遇到Commit阶段的Bolt,则要严格按照顺序进入,保证顺序,再在Commit阶段的Bolt中记录最后一个处理过的批的编号,和后续批的编号做比较,进行去重操作,就可以保证一次且一次的语义。
此方案,即保证了一次且一次的语义,也在最大程度上,保证了程序的并发效率,Storm采用的正是这个方案。
这三种级别,越往下可靠性越高,但效率越低,越往上可靠性越低,但是效率越高,在真正的开发中应该根据业务需求,选择一个在满足的要求的可靠性的前提下,性能尽量好的可靠性级别。
Storm集群配置及任务提交
Storm集群的基本概念
Storm集群遵从主/从模式
Storm的主节点称之为nimbus 从节点称之为supervisor
Storm通过Zookeeper来实现集群协调
Storm集群提供了半容错机制
Storm中的nimbus
nimbus守护进程主要的责任是管理:
协调和监控在集群上运行的topology
包括topology的发布
任务的指派
事件处理失败时重新指派任务
Storm中的supervisor节点
supervisor守护进程等待nimbus分配任务后生成并监控workers执行任务。
supervisor和worker都是运行在不同的JVM进程上,如果supervisor启动的worker进程因为错误异常退出,supervisor将会尝试重新生成新的worker进程。