并行度说白了就是同时运行当前topology的子topology的个数----->进程级别来说
从线程级别来说并行度就是一个work进程中有几个executor来运行---->线程级别
task任务一个executor线程中有几个task实例---->task级别
worker进程
每一台supervisor上面运行子topology的进程就是worker,默认一台supervisor可以运行4个worker进程,
在storm.yaml中的supervisor.slots.ports进行配置(一个端口指定一个worker-->slot)
同时一个worker进程运行一个topology任务的子topology(因为是分布式的程序,我们可以设置,当前topology被分配到多个worker里面去运行)
executor线程
每一个worker进程中有一个或者多个executor线程来组成,这些线程就用来运行我们topology任务,默认情况先也是一对一的。
task任务
task就是用来执行具体代码的一个单元,也可以理解为spout和bolt的实例(就像mr中一个maptask和reducetask),一般情况下,
executor线程threadNums<=taskNums
默认情况下,一个supervisor节点最多可以启动4个worker进程,每一个topology默认占用一个worker进程,
每个spout或者bolt会占用1个executor,每个executor启动1个task。
并行度测试
Worker(slot)
默认一个从节点上面可以启动4个worker进程,参数是supervisor.slots.ports。在storm配置文件中已经配置过了,默认是在strom-core.jar包中的defaults.yaml中配置的有。
默认一个topology只使用一个worker进程,可以通过代码来设置使用多个worker进程。
通过config.setNumWorkers(workers)设置
通过conf.setNumAckers(0);可以取消acker任务(点击topology页面最下面的show system stats,可以显示系统级别的bolt,可以验证acker线程的存在)
最好一台机器上的一个topology只使用一个worker,主要原因是减少了worker之间的数据传输
如果worker使用完的话再提交topology就不会执行,会处于等待状态
注意:worker之间通信是通过Netty?进行通信的
Executor
默认情况下一个executor运行一个task,可以通过在代码中设置
builder.setSpout(id, spout, parallelism_hint);
builder.setBolt(id, bolt, parallelism_hint);
Task
通过boltDeclarer.setNumTasks(num);来设置实例的个数
executor的数量会小于等于task的数量(为了rebalance)
设置workerNums
Config config = new Config();
config.setNumWorkers(2);
在原先代码的基础之上,将topology的worker个数由1提升到2,原先一个worker进程中有3个executor线程,这三个executor线程分别是:
spout线程,bolt线程,以及系统线程__acker,那么案例说,worker进程变成2之后,executor线程个数应该也是原来2倍?
是因为咱们当前应用程序比较小,任务只分配到了一个worker中,所以我们看到还是有2个executor来运行spout和bolt,另外两个executor
就是运行我们的形同线程__acker(默认每一个worker进程,持有一个__acker executor线程),所以最终结果看到了
有2个worker、4个executor、4个task
设置ackerNums
acker,是用来监控storm topology中数据是否被完全消费,也就是类似于db中的事务,默认如果我们没有进行配置topology.acker.executors
的话,则一个worker进程对应一个acker executor,如果这个参数设置为0的话,则storm topology不会保证数据能被完全消费掉,
这样的话就大大降低了storm集群的数据的可靠性。
在上述基础上:
设置的话,通过config.setNumAckers(0);
---->2个worker,2个executor、2个task
设置executor
设置线程个数就需要在
topologyBuilder.setSpout(spoutID, new SumNumSpout(), 2);
topologyBuilder.setBolt(boltID, new SumNumBolt(), 2)
中的第三个参数,设置当前spout和bolt分别有几个executor来进行运行,
去掉上述worker和acker的设置的话,则有1个worker、5个executor(1个acker+2个spout+2个bolt)、5个task(一个executor对应一个task)
设置task
设置task的个数,实际就是设置参数:topology.tasks的个数
定义了一个executor中有几个spout和blot的实例个数。一个executor线程中可以运行0个或者多个同名的spout或者blot的实例,
注意:task的个数一旦topology被部署之后就不能再进行改变了,只能改变executor线程的个数(动态调整并行度)
topologyBuilder.setSpout(spoutID, new SumNumSpout()).setNumTask(2);--->设置一个executor中可以有两个spout的task实例
topologyBuilder.setBolt(boltID, new SumNumBolt()).shuffleGrouping(spoutID).setNumTasks(2);-->设置一个executor中可以有两个blot的task实例
问,一下代码会有几个worker进程,几个executor线程,几个task线程?
topologyBuilder.setSpout(spoutID, new SumNumSpout());
topologyBuilder.setBolt(boltID, new SumNumBolt(), 2).shuffleGrouping(spoutID).setNumTasks(2);
StormTopology stormTopology = topologyBuilder.createTopology();
String topologyName = RemoteParallismSumTopology.class.getSimpleName();
Config config = new Config();
config.setNumWorkers(2);
config.setNumAckers(0);//设置系统executor个数
2 worker进程
3 executor线程
3 task任务
说明,案例说应该有5个task任务,因为当前程序,虽然设置了两个worker进程,但是任务只被分配到了一个worker进程中去运行,
所以另外一个executor进程中什么都没有运行。
动态调整并行度----弹性计算
通过UI调整
不推荐使用
通过代码调整
topologyBuilder.setBolt("green-bolt", new GreenBolt(),2)
.setNumTasks(4).shuffleGrouping("blue-spout);
通过shell调整
# 10秒之后开始调整
# Reconfigure the topology "mytopology" to use 5 worker processes,
# the spout "blue-spout" to use 3 executors and
# the bolt "yellow-bolt" to use 10 executors.
storm rebalance topologyName -w 10 -n 5 -e spout_id=3 -e id_bolt=10
注意:acker数目运行时是不会变化的,所以多指定几个worker进程,acker线程数也不会增加。
-w:表示超时时间,rebalance首先会在一个超时时间内注销掉拓扑,然后在整个集群中重新分配 worker。
问题:-e spout_id=3 -e id_bolt=10 有时不会增加并发度
原因:You can only increase the parallelism (number of executors) to the number of tasks. So if your component is having for example (number of executors: 50, number of tasks: 50) then you can not increase the parallelism, however you can decrease it.
就是说spout和bolt的并行数,最多可以调整到它的taskNum,默认情况下,taskNum是和你设置的paralismNum相同的。#threads<=#tasks
======================================================================================================
流分组
数据是如何从spout到bolt中的呢,如果bolt是多个情况呢?
这就是我们所说的流分组,也就是在Spout与Bolt、Bolt与Bolt之间传递Tuple的方式,我们称之为流分组storm grouping。
stream grouping分类
Shuffle Grouping:
随机分组,随机派发stream里面的tuple, 保证每个excutor中的每个任务[bolt]接收到的tuple数目相同.(它能实现较好的负载均衡)
具体设置,见代码ShuffleGroupingSumTopology
Fields Grouping:
按字段分组,比如按userid来分组,具有同样userid的tuple会被分到同一bolt任务, 而不同的userid则会被分配到不同的任务
具体设置,见代码FieldsGroupingSumTopology,需要在fieldsGrouping第二个参数设置分组字段
All Grouping:
广播发送,对于每一个tuple,Bolts中的所有bolt任务都会收到.
具体设置,见代码AllGroupingSumTopology,这种方式会使得所有的bolt都处理相同数据,未免有点浪费资源,不建议大家使用。
Global Grouping:
全局分组,这个tuple被分配到storm中的一个bolt的其中一个task.再具体一点就是分配给id值最低的那个task.
见代码GlobalGroupingSumTopology
Non Grouping:
随机分派,意思是说stream不关心到底谁会收到它的tuple.目前他和Shuffle grouping是一样的效果,
减NoGroupingSumTopology
Direct Grouping:
直接分组,这是一种比较特别的分组方法,用这种分组意味着消息的发送者具体由消息接收者的哪个task处理这个消息.只有被声明为Direct Stream的消息流可以声明这种分组方法.而且这种消息tuple必须使用emitDirect方法来发射.消息处理者可以通过TopologyContext来或者处理它的消息的taskid (OutputCollector.emit方法也会返回taskid)
localOrShuffleGrouping
CustomStreamGrouping
======================================================================================================
worker进程死掉
worker进程挂掉,storm集群会在重新启动一个worker进程。
supervisor进程死掉
supervisor进程挂掉,不会影响之前已经提交的topology,只是后期不能向这个节点分配任务,因为这个节点已经不是集群的一员了。
nimbus进程死掉(存在HA的问题)快速失败
nimbus进程挂掉,也不会影响之前已经提交的topology,只是后期不能向集群再提交新的topology了。1.0以下的版本存在HA的问题,1.0之后已经修复了这个问题,可以有多个备选nimbus。
节点宕机
ack/fail消息确认机制(确保一个tuple被完全处理)
在spout中发射tuple的时候需要同时发送messageid,这样才相当于开启了消息确认机制
如果你的topology里面的tuple比较多的话, 那么把acker的数量设置多一点,效率会高一点。
通过config.setNumAckers(num)来设置一个topology里面的acker的数量,默认值是1。
注意: acker用了特殊的算法,使得对于追踪每个spout tuple的状态所需要的内存量是恒定的(20 bytes)
注意:如果一个tuple在指定的timeout(Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS默认值为30秒)时间内没有被成功处理,那么这个tuple会被认为处理失败了。
完全处理tuple
在storm里面一个tuple被完全处理的意思是: 这个tuple以及由这个tuple所衍生的所有的tuple都被成功处理。
代码见LocalAckerSumTopology
======================================================================================================
Storm定时器
一般的业务数据存储,最终还是要落地,存储到RDBMS,但是RDBMS无法达到高访问量,能力达不到实时处理,或者说处理能力是有限的,会造成连接中断等问题,为了数据落地,我们可以采取迂回方式,可以采用比如说先缓存到高速内存数据库(如redis),然后再将内存数据库中的数据定时同步到rdbms中,而且可以定期定时来做。
可以每隔指定的时间将数据整合一次存入数据库。
或者每隔指定的时间执行一些
1:在main中设置
conf.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, 10);
// 设置多久发送一个系统的tuple定时发射数据
但是我们一般都会对特定的bolt设置定时任务,而没有必要对全局每一个bolt都发送系统的tuple,这样非常的耗费资源,所以就有了局部定时任务,也是我们常用的。 2:在bolt中使用下面代码判断是否是触发用的bolt
tuple.getSourceComponent().equals(Constants.SYSTEM_COMPONENT_ID)
如果为true,则执行定时任务需要执行的代码,最后return,如果为false,则执行正常的tuple处理的业务逻辑
具体案例见代码com.uplooking.bigdata.storm.test.TimerSchedulerSumTopology
======================================================================================================
StormUI参数的介绍
deactive:
未激活(暂停)
emitted:
emitted tuple数
transferred:
transferred tuple数
emitted的区别:如果一个task,emitted一个tuple到2个task中,则 transferred tuple数是emitted tuple数的两倍
complete latency:
spout emitting 一个tuple到spout ack这个tuple的平均时间(可以认为是tuple以及该tuple树的整个处理时间)
process latency:
bolt收到一个tuple到bolt ack这个tuple的平均时间,如果没有启动acker机制,那么值为0
execute latency:
bolt处理一个tuple的平均时间,不包含acker操作,单位是毫秒(也就是bolt 执行 execute 方法的平均时间)
capacity:
这个值越接近1,说明bolt或者spout基本一直在调用execute方法,说明并行度不够,需要扩展这个组件的executor数量。
总结:execute latency和proces latnecy是处理消息的时效性,而capacity则表示处理能力是否已经饱和,从这3个参数可以知道topology的瓶颈所在。