Storm进阶
并行度###
在Storm集群中真正运行Topology的主要有三个实体:worker、executor、task,下图是可以表示他们之间的关系。

数据流模型
对于一个Spout或Bolt,都会有多个task线程来运行,那么如何在两个组件(Spout和Bolt)之间发送tuple元组呢?Storm提供了若干种数据流分发(Stream Grouping)策略用来解决这一问题。在Topology定义时,需要为每个Bolt指定接收什么样的Stream作为其输入(注:Spout并不需要接收Stream,只会发射Stream)。
目前Storm中提供了以下7种Stream Grouping策略:Shuffle Grouping、Fields Grouping、All Grouping、Global Grouping、Non Grouping、Direct Grouping、Local or shuffle grouping。
- Shuffle Grouping: 随机分组, 随机派发stream里面的tuple,保证每个bolt接收到的tuple数目大致相同。
 - Fields Grouping:按字段分组, 比如按userid来分组, 具有同样userid的tuple会被分到相同的Bolts里的一个task(这句话很关键,代表了我对storm的理解。)而不同的userid则会被分配到不同的bolts里的task。
 - All Grouping:广播发送,对于每一个tuple,所有的bolts都会收到。
 - Global Grouping:全局分组, 这个tuple被分配到storm中的一个bolt的其中一个task。再具体一点就是分配给id值最低的那个task。
 - Non Grouping:不分组,这个分组的意思是说stream不关心到底谁会收到它的tuple。目前这种分组和Shuffle grouping是一样的效果, 有一点不同的是storm会把这个bolt放到这个bolt的订阅者同一个线程里面去执行。
 - Direct Grouping: 直接分组, 这是一种比较特别的分组方法,用这种分组意味着消息的发送者指定由消息接收者的哪个task处理这个消息。 只有被声明为Direct Stream的消息流可以声明这种分组方法。而且这种消息tuple必须使用emitDirect方法来发射。消息处理者可以通过TopologyContext来获取处理它的消息的task的id (OutputCollector.emit方法也会返回task的id)。
 - Local or shuffle grouping:如果目标bolt有一个或者多个task在同一个工作进程worker中,tuple将会被随机发生给这些tasks。否则,和普通的Shuffle Grouping行为一致。
 
一个运行中Topology的例子:
Topology里包含了三个component,一个是Blue Spout,另外两个分别是Green Bolt和Yellow Bolt。

Config conf = new Config();
conf.setNumWorkers(2);
topologyBuilder.setSpout("blue-spout", new BlueSpout(), 2);
topologyBuilder.setBolt("green-bolt", new GreenBolt(), 2)
           .setNumTasks(4)
           .shuffleGrouping("blue-spout");
topologyBuilder.setBolt("yellow-bolt", new YellowBolt(), 6)
           .shuffleGrouping("green-bolt");
StormSubmitter.submitTopology(
    "mytopology",
    conf,
    topologyBuilder.createTopology()
);
图和代码, 很清晰, 通过setBolt和setSpout一共定义2+2+6=10个executor threads 。并且同setNumWorkers设置2个workers, 所以storm会平均在每个worker上run 5个executors (线程)。而对于green-bolt, 定义了4个tasks, 所以每个executor中有2个tasks。
总结
- 一个Topology可以包含多个worker ,一个worker只能对应于一个topology。worker process是一个topology的子集。
 - 一个worker可以包含多个executor,一个executor只能对应于一个component(spout或者bolt)。
 - Task就是具体的处理逻辑, 一个executor线程可以执行一个或多个tasks。线程就是资源,task就是要运行的任务。
 
消息的可靠处理
Storm记录级容错
首先来看一下什么叫做记录级容错?storm允许用户在spout中发射一个新的源tuple时为其指定一个message id, 这个message id可以是任意的object对象。多个源tuple可以共用一个message id,表示这多个源 tuple对用户来说是同一个消息单元。storm中记录级容错的意思是说,storm会告知用户每一个消息单元是否在指定时间内被完全处理了。那什么叫做完全处理呢,就是该message id绑定的源tuple及由该源tuple后续生成的tuple经过了topology中每一个应该到达的bolt的处理。举个例子。在下图中,在spout由message 1绑定的tuple1和tuple2经过了bolt1和bolt2的处理生成两个新的tuple,并最终都流向了bolt3。当这个过程完成处理完时,称message 1被完全处理了。

在storm的topology中有一个系统级组件,叫做acker。这个acker的任务就是追踪从spout中流出来的每一个message id绑定的若干tuple的处理路径,如果在用户设置的最大超时时间内这些tuple没有被完全处理,那么acker就会告知spout该消息处理失败了,相反则会告知spout该消息处理成功了。在刚才的描述中,我们提到了”记录tuple的处理路径”,storm中却是使用了一种非常巧妙的方法做到了。在说明这个方法之前,我们来复习一个数学定理。
A xor A = 0.
A xor B…xor B xor A = 0,其中每一个操作数出现且仅出现两次。
storm中使用的巧妙方法就是基于这个定理。具体过程是这样的:在spout中系统会为用户指定的message id生成一个对应的64位整数,作为一个root id。root id会传递给acker及后续的bolt作为该消息单元的唯一标识。同时无论是spout还是bolt每次新生成一个tuple的时候,都会赋予该tuple一个64位的整数的id。Spout发射完某个message id对应的源tuple之后,会告知acker自己发射的root id及生成的那些源tuple的id。而bolt呢,每次接受到一个输入tuple处理完之后,也会告知acker自己处理的输入tuple的id及新生成的那些tuple的id。Acker只需要对这些id做一个简单的异或运算,就能判断出该root id对应的消息单元是否处理完成了。下面通过一个图示来说明这个过程。
第一步:初始化,spout中绑定message 1生成了两个源tuple,id分别是0010和1011.

第二步:计算一个turple达到第1个bolt

第三步:计算一个turple达到第2个bolt

第四步:消息到达最后一个bolt

即在正常情况下,每个id都会且只会被异或两次,因此最后的结果一定是0,但是容错过程存在一个可能出错的地方,那就是,如果生成的tuple id并不是完全各异的,acker可能会在消息单元完全处理完成之前就错误的计算为0。这个错误在理论上的确是存在的,但是在实际中其概率是极低极低的,完全可以忽略。
高可靠性下Spout需要做些什么
当Spout从队列中读取一个消息,表示它“打开”了队列中某个消息,这意味着,此消息并未从队列中真正删除,而是被置为“pending”状态,它等待来自客户端的应答,被应答之后,此消息才会被真正从队列中删除。处于“pending”状态的消息不会被其他客户端看到。另外,如果一个客户端意外断开连接,则由此客户端“打开”的所有消息都会被重新加入到队列中。当消息被“打开”的时候,队列同时会为这个消息提供一个唯一的标识。
Spout使用这个唯一标识作为这个tuple的id,当ack或fail被调用时,Spout会把ack或者fail连同id一起发送给队列,队列会将消息从队列中真正删除或者将它重新放回队列中。
选择合适的可靠性级别
如果并不需要每个消息必须被处理,那么可以关闭消息的可靠性机制,从而获取较好的性能。关闭消息的可靠处理机制意味着系统中的消息数会减半(每个消息不需要应答了)。另外,关闭消息的可靠性处理机制可以减少消息的大小(不需要每个tuple记录它的根id),从而节省带宽。
有三种方法调整消息的可靠处理机制:
- 将参数Config.TOPOLOGY_ACKERS设置为0,通过此方法,当Spout发送一个消息时,它的ack方法将立即被调用。
 - 第二种方法是Spout发送一个消息时,不指定此消息的id。当需要关闭特定消息的可靠性时,可以使用此方法。
 - 如果不在意某个消息派生出来的子孙消息的可靠性,则此消息派生出来的子消息在发送时不要做锚定,即在emit方法中不指定输入消息。因此这些子孙消息没有被锚定在任何tuple tree中,因此他们的失败不会引起任何Spout重新发送消息。
 
集群中各级容错
- Bolt任务crash引起的消息未被应答。此时,acker中所有与此Bolt任务关联的消息都会因为超时而失败,对应Spout的fail方法将会被调用。
 - acker任务本身失败,它在失败之前持有的所有消息都将因超时而失败。Spout的fail方法将被调用。
 - Spout任务失败,队列会将处于pending状态的所有消息重新放回队列里。
 - Worker失败,Supervisor负责监控Worker中的任务,Supervisor会尝试在本机重启它。
 - Supervisor失败,由于Supervisor是无状态的,只需将它重新启动即可。
 - Nimbus失败。由于Nimbus是无状态的,只需将它重新启动即可。
 - Storm中的集群节点故障,此时Nimbus会将此机器上所有正在运行的任务转移到其他可用的机器上运行。
 - Zookeeper集群中的节点故障,Zookeeper保证少于半数的机器宕机系统仍可正常运行,及时修复故障机器即可。
 
一致性事务
Storm如何实现既对tuple并行处理,又保证事务性呢?这里先从简单的事务性实现方法入手,逐步引出Transactional Topology的原理。
强顺序流
将tuple流变成强顺序性的,并且每次只处理一个tuple。从1开始,给每个tuple都顺序加上一个id,在处理tuple时,将处理成功的tuple id和计算结果存在数据库中。下一个tuple到来时,将其id与数据库中的id作比较,如果相同,则说明这个tuple已经被成功处理过了,那么忽略,如果不同,则将它的id和计算结果更新到数据库中。
但是这种机制使得系统一次只能处理一个tuple,无法实现分布式计算。

强顺序batch流
为了实现分布式,我们可以每次处理一批tuple,即一个batch,每个bacth中的tuple可以并行处理。这样数据库里存的就是bacth id和bacth的计算结果。
但是这种机制每次只能处理一个batch,batch之间无法并行。

CoordinateBolt的原理
- 每个CoordinateBolt记录两个值:有哪些task给我发送了tuple以及我要给哪些task发送信息。
 - 真正执行任务的bolt是real bolt,它发出一个tuple后,其外层的CoordinateBolt会记录下这个tuple发送给了哪个task。
 - 所有的tuple发送完了以后,CoordinateBolt会告诉它发送过tuple的task,它发送了多少tuple给这个task,下游task会将这个数字和自己接收到的tuple数量做对比,如果相等,则说明处理完了所有的tuple。
 

Transactional Topology
Storm提供的Transactional Topology将batch计算分为process和commit两个阶段,process阶段可以同时处理多个batch,不用保证顺序性;commit阶段保证batch的强顺序性,并且一次只能处理一个batch,第一个batch成功提交之前,第二个batch不能被提交。
Transactional Topology里发送的tuple都必须以TransactionAttempt作为第一个field,Storm根据这个field来判断tuple属于哪一个batch。
TransactionAttempt包含两个值:一个是transaction id,另一个是attempt id,transaction id对于每个batch中的tuple是唯一的,不管replay多少次都是一样的。attempt id是每个batch唯一的一个id,但是对于同一个batch,它replay之后的attempt id跟replay之前不一样。
当bolt收到某个batch所有的tuple以后,finishBatch会被调用,将当前的transaction id与数据库中存储的id做比较,如果相同则忽略,不同就把这个batch的计算结果加到总结果中,并更新数据库。

- TransactionSpout只能有一个,它将所有tuple分组为一个一个的batch,而且保证同一个batch的transaction id始终一样。
 - BatchBolt处理一个batch中所有的tuples。对于每一个tuple调用execute方法,而在整个batch处理完成时调用finishBatch方法。
 - 如果BatchBolt被标记为Committer,则只能在Committer阶段调用finishBolt方法,并且在commit阶段batch是强顺序性的。
 
Storm进阶的更多相关文章
- 大数据入门第十六天——流式计算之storm详解(三)集群相关进阶
		
一.集群提交任务流程分析 1.集群提交操作 参考:https://www.jianshu.com/p/6783f1ec2da0 2.任务分配与启动流程 参考:https://www.cnblogs.c ...
 - Storm 实战:构建大数据实时计算
		
Storm 实战:构建大数据实时计算(阿里巴巴集团技术丛书,大数据丛书.大型互联网公司大数据实时处理干货分享!来自淘宝一线技术团队的丰富实践,快速掌握Storm技术精髓!) 阿里巴巴集团数据平台事业部 ...
 - Java 入门进阶
		
Java 入门进阶 發表於 2015-04-16 http://xielong.me/2015/04/16/%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%B7%A5%E7%A8%8B% ...
 - 服务端工程师入门与进阶 Java 版
		
前言 欢迎加入我们.这是一份针对实习生/毕业生的服务端开发入门与进阶指南.遇到问题及时问你的 mentor 或者直接问我. 建议: 尽量用google查找技术资料. 有问题在stackoverflow ...
 - Java的进阶之道
		
Java的进阶之道 一.温馨提示 尽量用google查找技术资料.(条件允许的话) 有问题在stackoverflow找找,大部分都已经有人回答. 多看官方的技术文档. ibm developerwo ...
 - Java进阶步骤
		
一.基础篇 面向对象 什么是面向对象 面向对象.面向过程 面向对象的三大基本特征和五大基本原则 平台无关性 Java如何实现的平台无关 JVM还支持哪些语言(Kotlin.Groovy.JRuby.J ...
 - Storm常见模式——分布式RPC
		
Storm常见模式——分布式RPC 本文翻译自:https://github.com/nathanmarz/storm/wiki/Distributed-RPC,作为学习Storm DRPC的资料,转 ...
 - 年薪20万Python工程师进阶(7):Python资源大全,让你相见恨晚的Python库
		
我是 环境管理 管理 Python 版本和环境的工具 pyenv – 简单的 Python 版本管理工具. Vex – 可以在虚拟环境中执行命令. virtualenv – 创建独立 Python 环 ...
 - 转:java 进阶之路
		
转: https://www.zhihu.com/question/39139518 一.基础篇1.1 JVM1.1.1. Java内存模型,Java内存管理,Java堆和栈,垃圾回收 http:// ...
 
随机推荐
- 安装生物信息学软件-HUMAnN2
			
先挖坑 因为这个软件需要memory>16G,所以应该要安装在服务器上
 - TeamWork-天气美食
			
一. 团队情况 Hello,欢迎来到我们"Code Man"队的第一次团队作业页面,"代码侠"很明显我们是一个编程队伍,由大三在读的6位同班同学组成 ...
 - angular 滚动
			
AngularJs $anchorScroll.$controller.$document $anchorScroll 根据HTML5的规则,当调用这个函数时,它检查当前的url的hash值并且滚动到 ...
 - HTTP动词
			
对于资源的具体操作类型,由HTTP动词表示. 常用的HTTP动词有下面五个(括号里是对应的SQL命令). GET(SELECT):从服务器取出资源(一项或多项). POST(CREATE):在服务器新 ...
 - 创建【哆啦A梦】风格字体
			
学习canvas,为作画.对于一个毫无逻辑思维的人简直遭罪啊~想象坐标坐标坐标啊- - 好啦言归正传,基于本月16号,在春熙路IFS展出120只哆啦a梦,以及canvas的作用,在此介绍一种PS的美化 ...
 - hexo环境变量的配置问题
			
因为一些个人原因,想尝试在github上用hexo搭建一个博客,于是用npm安装,安装完成之后却一直无法确认hexo的版本问题,cmd中也一直提示hexo -v 不是有效的命令行,在重装了几次Node ...
 - iOS--基础控件总结一
			
UIWindow窗口 UIView视图 UIButten按钮 UILabel文本显示 UITextField输入框 UI TextView多行输入框 UISwitch开关 UISegmentedCon ...
 - Swift---- 可选值类型(Optionals) 、 断言(Assertion) 、 集合 、 函数
			
1 使用数组实现九宫格 1.1 问题 Swift提供经典的数组和字典两种集合类型来存储集合数据.本案例使用数组实现一个九宫格程序,如图-1所示: 图-1 1.2 方案 九宫格就是有一个n行n列的方格, ...
 - 学习maple
			
定义函数:$f:=(x,y) \rightarrow x^2+y^2$ 类似mathematica的manipulate功能:plots[animate](plot,[f(x,y),x=0..1],y ...
 - Nginx下css的链接问题
			
放在 Nginx 下的网页代码,在链接外部 css 文件时,可能出现没有链接成功的问题.需要在 nginx.conf 里的 http 下添加一行. http { include mime.types;