1. 简介

流式计算的历史

早在7、8年前诸如UC伯克利、斯坦福等大学就开始了对流式数据处理的研究,但是由于更多的关注于金融行业的业务场景或者互联网流量监控的业务场景,以及当时互联网数据场景的限制,造成了研究多是基于对传统数据库处理的流式化,对流式框架本身的研究偏少。目前这样的研究逐渐没有了声音,工业界更多的精力转向了实时数据库。

2010年Yahoo!对S4的开源,2011年twitter对Storm的开源,改变了这个情况。以前互联网的开发人员在做一个实时应用的时候,除了要关注应用逻辑计算处理本身,还要为了数据的实时流转、交互、分布大伤脑筋。但是现在情况却大为不同,以Storm为例,开发人员可以快速的搭建一套健壮、易用的实时流处理框架,配合SQL产品或者NoSQL产品或者MapReduce计算平台,就可以低成本的做出很多以前很难想象的实时产品:比如一淘数据部的量子恒道品牌旗下的多个产品就是构建在实时流处理平台上的。

流式计算的最新进展

在数据处理时间和方式上,Storm与Hadoop MapReduce基本上是两个对立面,而这两个技术具备整合可能性极大程度该归结于YARN这个集群管理层。Hortonworks当下正在致力于通过新型处理框架Tez来提高Hive的速度,同时YARN还允许Hadoop用户运行Spark内存处理框架。同时, 微软也在使用YARN让Hadoop更加适合机器学习用例。
此外,通过YARN,同集群上同时运行HBase、 Giraph等不同技术也成为可能。此外,集群管理技术Mesos(加州大学伯克利分校出品,现已成为Apache项目) 同样支持了类似YARN功能,尽管其不是像YARN这样与HDFS捆绑。
更多技术的整合预示Hadoop这个大数据处理平台绝不是昙花一现,同时也会让Hadoop在大数据应用程序领域获得更高的统治力。

1.1 Storm的特点

Storm是一个开源的分布式实时计算系统,可以简单、可靠的处理大量的数据流。被称"实时的hadoop"。Storm有很多使用场景:如实时分析,在线机器学习,持续计算, 分布式RPC,ETL等等。Storm支持水平扩展,具有高容错性,保证每个消息都会得到处理,而且处理速度很快(在一个小集群中,每个结点每秒可以处理数以百万计的消息)。Storm的部署和运维都很便捷,而且更为重要的是可以使用任意编程语言来开发应用。

编程模型简单
在大数据处理方面相信大家对hadoop已经耳熟能详,基于Google Map/Reduce来实现的Hadoop为开发者提供了map、reduce原语,使并行批处理程序变得非常地简单和优美。
同样,Storm也为大数据的实时计算提供了一些简单优美的原语,这大大降低了开发并行实时处理的任务的复杂性,帮助你快速、高效的开发应用。

可扩展
在Storm集群中真正运行topology的主要有三个实体:工作进程、线程和任务。Storm集群中的每台机器上都可以运行多个工作进程,每个工作进程又可创建多个线程,每个线程可以执行多个任务,任务是真正进行数据处理的实体,我们开发的spout、bolt就是作为一个或者多个任务的方式执行的。
因此,计算任务在多个线程、进程和服务器之间并行进行,支持灵活的水平扩展。

高可靠性
Storm可以保证spout发出的每条消息都能被“完全处理”,这也是直接区别于其他实时系统的地方,如S4。
spout发出的消息后续可能会触发产生成千上万条消息,可以形象的理解为一棵消息树,其中spout发出的消息为树根,Storm会跟踪这棵消息树的处理情况,只有当这棵消息树中的所有消息都被处理了,Storm才会认为spout发出的这个消息已经被“完全处理”。如果这棵消息树中的任何一个消息处理失败了,或者整棵消息树在限定的时间内没有“完全处理”,那么spout发出的消息就会重发。

高容错性
如果在消息处理过程中出了一些异常,Storm会重新安排这个出问题的处理单元。Storm保证一个处理单元永远运行(除非你显式杀掉这个处理单元)。
当然,如果处理单元中存储了中间状态,那么当处理单元重新被Storm启动的时候,需要应用自己处理中间状态的恢复。

1.2 Storm的基本概念

DataSource:外部数据源

Topology : 拓扑,也俗称一个任务

Spout : 拓扑的消息源,接受外部数据源的组件,将外部数据源转化成Storm内部的数据,以Tuple为基本的传输单元下发给Bolt

Bolt : 拓扑的处理逻辑单元,接受Spout发送的数据,或上游的bolt的发送的数据。根据业务逻辑进行处理。发送给下一个Bolt或者是存储到某种介质上。介质可以是Redis可以是mysql,或者其他

tuple:消息元组,Storm内部中数据传输的基本单元,里面封装了一个List对象,用来保存数据

Streams : 流

Stream groupings :流的分组策略

Task : 任务处理单元,比物理线程还要小,默认一个Executor包括一个Task,也可以包括多个。一个Task属于一个Spout或者Bolt并发任务

Executor :工作线程,即物理线程

Worker :工作进程,可以执行两种类型的任务,Spout任务或者bolt任务

Configuration : topology的配置

1.3 Storm与Hadoop的对比

Storm集群和Hadoop集群表面上看很类似。Hadoop上运行的是MapReduce jobs,而在Storm上运行的是拓扑(topology);
Hadoop擅长于分布式离线批处理,而Storm设计为支持分布式实时计算;
Hadoop新的spark组件提供了在hadoop平台上运行storm的可能性。

Topology 与 Mapreduce
一个关键的区别是: 一个MapReduce job最终会结束, 而一个topology永远会运行(除非你手动kill掉)

Nimbus 与 ResourceManager
在Storm的集群里面有两种节点: 控制节点(master node)和工作节点(worker node)。控制节点上面运行一个叫Nimbus后台程序,它的作用类似Hadoop里面的ResourceManager。Nimbus负责在集群里面分发代码,分配计算任务给机器, 并且监控状态。

Supervisor (worker进程)与NodeManager(YarnChild)
每一个工作节点上面运行一个叫做Supervisor的节点。Supervisor会监听分配给它那台机器的工作,根据需要启动/关闭工作进程。每一个工作进程执行一个topology的一个子集;一个运行的topology由运行在很多机器上的很多工作进程组成。

Worker与topology
一个worker只属于一个topology,每个worker中运行的task只能属于这个topology。 反之,一个topology包含多个worker,其实就是这个topology运行在多个worker上。
一个topology要求的worker数量如果不被满足,集群在任务分配时,根据现有的worker先运行topology。如果当前集群中worker数量为0,那么最新提交的topology将只会被标识active,不会运行,只有当集群有了空闲资源之后,才会被运行。

2. Storm的体系结构

2.1 Storm中的Nimbus和Supervisor

Nimbus:任务分配。
Supervisor:接受任务,并启动worker。worker的数量根据端口号来的。

Nimbus和Supervisor之间的所有协调工作都是通过Zookeeper集群完成。
Nimbus进程和Supervisor进程都是快速失败(fail-fast)和无状态的。所有的状态要么在zookeeper里面, 要么在本地磁盘上。
这也就意味着你可以用kill -9来杀死Nimbus和Supervisor进程, 然后再重启它们,就好像什么都没有发生过。这个设计使得Storm异常的稳定。

2.2 Storm中的Topologies

一个topology是spouts和bolts组成的图, 通过stream groupings将图中的spouts和bolts连接起来,如下图:

2.3 Storm中的Stream

消息流stream是storm里的关键抽象;
一个消息流是一个没有边界的tuple序列, 而这些tuple序列会以一种分布式的方式并行地创建和处理;
通过对stream中tuple序列中每个字段命名来定义stream;
在默认的情况下,tuple的字段类型可以是:integer,long,short, byte,string,double,float,boolean和byte array;
可以自定义类型(只要实现相应的序列化器)。

2.4 Storm中的Spouts

消息源spout是Storm里面一个topology里面的消息生产者;
一般来说消息源会从一个外部源读取数据并且向topology里面发出消息:tuple;
Spouts可以是可靠的也可以是不可靠的:如果这个tuple没有被storm成功处理,可靠的消息源spouts可以重新发射一个tuple, 但是不可靠的消息源spouts一旦发出一个tuple就不能重发了;
消息源可以发射多条消息流stream:
使用OutputFieldsDeclarer.declareStream来定义多个stream,
然后使用SpoutOutputCollector来发射指定的stream。

2.5 Storm中的Bolts

所有的消息处理逻辑被封装在bolts里面;
Bolts可以做很多事情:过滤,聚合,查询数据库等等。
Bolts可以简单的做消息流的传递,也可以通过多级Bolts的组合来完成复杂的消息流处理;比如求TopN、聚合操作等(如果要把这个过程做得更具有扩展性那么可能需要更多的步骤)。

Bolts可以发射多条消息流:
使用OutputFieldsDeclarer.declareStream定义stream;
使用OutputCollector.emit来选择要发射的stream;

Bolts的主要方法是execute,:
它以一个tuple作为输入,使用OutputCollector来发射tuple;
通过调用OutputCollector的ack方法,以通知这个tuple的发射者spout;

Bolts一般的流程:
处理一个输入tuple,  发射0个或者多个tuple, 然后调用ack通知storm自己已经处理过这个tuple了;
storm提供了一个IBasicBolt会自动调用ack。

2.6 Storm中的Stream groupings

定义一个topology的关键一步是定义每个bolt接收什么样的流作为输入;
stream grouping就是用来定义一个stream应该如何分配数据给bolts;
Storm里面有7种类型的stream grouping:
Shuffle Grouping——随机分组, 随机派发stream里面的tuple,保证每个bolt接收到的tuple数目大致相同;
Fields Grouping——按字段分组, 比如按userid来分组, 具有同样userid的tuple会被分到相同的Bolts里的一个task, 而不同的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在同一个工作进程中,tuple将会被随机发生给这些tasks。否则,和普通的Shuffle Grouping行为一致。

2.7 Storm中的Workers

一个topology可能会在一个或者多个worker(工作进程)里面执行;
每个worker是一个物理JVM并且执行整个topology的一部分;
比如,对于并行度是300的topology来说,如果我们使用50个工作进程来执行,那么每个工作进程会处理其中的6个tasks;
Storm会尽量均匀的工作分配给所有的worker;

2.8 Storm中的Tasks

每一个spout和bolt会被当作很多task在整个集群里执行
每一个executor对应到一个线程,在这个线程上运行多个task
stream grouping则是定义怎么从一堆task发射tuple到另外一堆task
可以调用TopologyBuilder类的setSpout和setBolt来设置并行度(也就是有多少个task)

conf.setNumWorkers(4) 表示设置了4个worker来执行整个topology的所有组件

builder.setBolt("boltA",new BoltA(), 4) ---->指明 boltA组件的线程数excutors总共有4个
builder.setBolt("boltB",new BoltB(), 4) ---->指明 boltB组件的线程数excutors总共有4个
builder.setSpout("randomSpout",new RandomSpout(), 2) ---->指明randomSpout组件的线程数excutors总共有2个

-----意味着整个topology中执行所有组件的总线程数为4+4+2=10个
----worker数量是4个,有可能会出现这样的负载情况, worker-1有2个线程,worker-2有2个线程,worker-3有3个线程,worker-4有3个线程

如果指定某个组件的具体task并发实例数
builder.setSpout("randomspout", new RandomWordSpout(), 4).setNumTasks(8);
----意味着对于这个组件的执行线程excutor来说,一个excutor将执行8/4=2个task

整体架构

最新文章

  1. Python写各大聊天系统的屏蔽脏话功能原理
  2. 数据分析:.Net程序员该如何选择?
  3. git mac客户端使用提交与同步
  4. window.location 对象所包含的属性
  5. xshell5 启动显示 mfc110.dll msvcp110.dll 未找到问题 解决办法
  6. [iOS]利用系统NSRegularExpression使用正则表达式
  7. 无责任Windows Azure SDK .NET开发入门篇三[使用Azure AD 管理用户信息--3.3 Details用户详细信息]
  8. webdriver(python)学习笔记四——定位一组元素
  9. Matlab中plot、fplot、ezplot的使用方法和区别
  10. 私人定制javascript中数组小知识点(Only For Me)
  11. Linq第二讲
  12. chrome谷歌浏览器-DevTool开发者工具-详细总结
  13. UOJ #460 新年的拯救计划
  14. Redis主从数据库同步
  15. 51ll网产品信息保存为txt文件
  16. dedecms中调用隐藏栏目的方法
  17. nginx配置伪静态
  18. 是否只查看安全传送的网页内容? 去掉 IE弹出窗口
  19. js脚本代码调试小技巧
  20. Tensorflow currently has no official prebuild for your CUDA, cuDNN combination.

热门文章

  1. OceanBase支持索引查询啦!
  2. idea启动dubbo
  3. The WebSocket Protocol
  4. TestNG系列之三:TestNG忽略测试
  5. P2P网络借贷系统-核心功能-用户投标-业务解说
  6. ASP.NET MVC扩展之HtmlHelper辅助方法
  7. Eclipse中,快捷键使用总结
  8. IBM-ETP 实训项目前一天
  9. jQuery中的append()和prepend(),after()和before()的差别
  10. mac 查看目前哪些进程占用哪些端口