我是一条淡水鱼

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

1.topology

  一个topolgy是spouts和bolts组成的图,通过stream groupings将图中的spout和bolts连接起来:如图所示:

  

  一个topology会一直运行知道你手动kill掉,Storm自动重新分配执行失败的任务,并且Storm可以保证你不会有数据丢失(如果开启了高可靠性的话)。如果一些机器意外停机它上面的所有任务会被转移到其他机器上;

  运行一个toplogy很简单,首先,把你所有的代码以及所依赖的jar打进一个jar中。然后运行类似下面的命令:

  storm jar all-my-code.jar backtype.storm.MyTopology arg1 arg2

  这个命令会运行主类:backtype.storm.MyTopology,参数是arg1,arg2。这个类的main函数定义这个topology并且把它提交给Nimbus。storm jar负责连接到Nimbus并且上传jar包;

  Topology的定义是一个Thrift结构,并且Nimbus就是一个Thrift服务,你可以提交任何语言创建的topology。上面的方法就是用JVM-based语言提交的最简单的方法。

  如下代码即定义了一个topology:

  

TopologyBuilder builder = new TopologyBuilder();

builder.setSpout("spout",new RandomSentceSpout(),5);

builder.setBolt("split",new SplitSentence(),8).shuffleGrouping("spout")

builder.setBolt("count", new WordCount(),12).fieldsGrouping("spilt",new Fields("word"));

 

 

2、Streams

  小溪流stream是storm里的关键抽象。一个消息流是一个没有边界的tuple序列,而这些tuple序列会以一种分布式的方式并行的创建和处理。再默认的情况写,tuplr的字段类型可以是:integer,long,short,byte,string,double,boolean和byte array。也可以自定义类型(只要实现相应的序列化器)。

public void declareOutputFields(OutputFieldsDeclarer declarer){
        //默认ID的信息流定义
        declarer.declare(new Fields("word","cout"));
        //自定义ID的消息流
        declare.declareStream("streamId",new Fields("word","count"));
}        

   每个消息流再定义的时候会被分配给一个id,因为单向消息流使用的相当普遍,OutputFieldsDeclarer定义了一些方法让你可以定义一个stream而不用制定这个id。在这种情况下这个stream会分配个值为‘default’默认的id。

  Storm提供的最近本的处理stream的原语是spout和bolt。你可以实现spout和bolt提供的接口来处理你的业务逻辑。

3、Soupts

  消息源spout是Storm里面一个topology里面的消息生产者。一般来说消息源会从一个外部源读取数据并且向topology里面发送出消息:tuple。Spout可以是可靠地也可以是不可靠的。如果这个tuple没有被storm成功处理,可靠地消息源spouts可以重新发射一个tuple,但是不可靠的消息源spouts一旦发出一个tuple就不能重发了。

  消息源可以发射多条消息流stream。使用OutputFieldsDeclarer。declareStream来定义多个stream,然后使用SpoutOutputCollector来发射指定的stream。

/**
*定义了2个消息流
*/
public void declareOutputFields (OutputFieldsDeclarer declarer){
      declarer.declareStream("streamId1",new Fields("words"));
       declarer.declareStream("streamId2",new Fields("word2"));   
}
/**
*根据消息流发射相应的消息
*/
public void nextTuple(){
     collector.emit("streamId1",new Values("streamid1's word1"));
     collector.emit("streamId2".new Values("streamid2's word2"));       
}

   Spout类里面最重要的方法是nextTuple。要么发射一个新的tuple到topology里面或者简单的返回如果已经没有新的tuple。要注意的是nextTuple方法不能阻塞,因为storm在同一个线程上面调用所有消息源spout的方法。

  另外两个比较重要的spout方法是ack和fail。storm在检测到一个tuple被整个topology成功处理的时候调用ack,否则调用fail。storm只对可靠的spout调用ack和fail。

4、Bolts

  所有的消息处理逻辑被封装在bolts里面。Bolts可以做很多事情:过滤,聚合,查询数据库等等;

  Bolts可以简单地做消息流的传递。复杂的消息流处理往往需要很多步骤,从而就需要经过很多bolts。

  Bolts和spouts一样,可以发射多条消息流,使用OutputFieldsDeclarer。declareStream定义stream,使用OutputCollector.emit来选择要发射的stream。

  Bolts的主要方法是execute,他以一个tuple作为输入,使用OutputCollector来发射tuple,bolts必须要为它处理的每一个tuple调用OutputCollector的ack方法,以通知Storm这个tuple被处理完成了,从而统治这个tuple的发射这spouts.一般的流程是:bolts处理一个输入tuplr,发射0个或者多个tuple,然后调用ack通知storm自己已经处理过这个tuple了。storm提供了一个IBasicBolt会自动调用ack。

5、Stream Grouping

  定义一个 toplogy的其中一步是定义每个bolt接受什么样的流作为输入。stream grouping就是用来定义一个stream应该如果分配数据个ibolts上面的多个tasks。

  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方法也会返回tsk的id)。

  --Local or shuffle grouping:如果目标bolt有一个或者多个task在同一个工作进程中,tuple将会被随机发送给这些tasks。否则,和普通的Shuffle Grouping行为一致。

代码示例:

TopologyBuisder builder = new TopologyBuilder();

builder.setSpout("spout",new RandomSentenceSpout(),5);
builder.setBolt("split",new SplitSentence(),8).shuffleGrouping("spout");
builder.setBolt("count",new WordCount(),12).fieldsGrouping("spilt",new Fields("word")):

 

 6.Reliability

  storm保证每个tuple会被topology完整的执行。Storm会追踪有每个spout tuple所含生的tuple树(一个bolt处理一个tuple之后可能会发射别的tuple从而形成树状结构),并且跟踪这颗tuple树什么时候成功处理完。每个topology都有一个消息超时的设置,如果storm在这个超市的时间内检测不到某个tuple树到底有没有执行成功,那么topology会把这个tuple标记为执行失败,并且过一会重新发射这个tuple。

  为了利用Strom的可靠性特性,在你发出一个新的tuple以及你完成处理一个tuple的时候你必须要通知storm。这一切是由OutputCollector来完成的。通过emit方法来通知一个新的tuple产生了,通过ack方法通知一个tuple处理完成了。

7、Tasks

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

代码示例如下:

ToplogyBuilder builder = new TopologyBuilder();

builder.setSpout("spout",new RandomSentenceSpout(),5).setNumTasks(10);
builder.setBolt("spilt",new SplitSentence(),8).shuffleGrouping("spout").setNumTasks(8);
builder.setBolt("count",new WoedCount(),12).fieldsGrouping("split",new Fields("woed")).setNumTasks(24);

表示“spout”的线程数为5,任务数为10,即一个线程运行两个任务;

“split”则为一个线程运行一个任务,和默认的一致。

8.Workers

  一个topology可能会在一个或者多个worker(工作进程)里面执行,每个worker是一个屋里JVM并执行整个topology的一部分。比如,对于并行度是300的topology来说,如果我们使用50个工作进程来执行,那么每个工作进程会开启6个线程,默认默认每个线程处理一个tasks。Storm会尽量均匀的工作分配给所有的worker。每个supervisor上运行着若干个worker进程(根据配置文件supervisor.slots.ports进行配置)。

9.Configuration

  Storm里面有一堆参数可以配置来调整Nimbus,Supervisor以及正在运行的topology的行为,一些配置是系统级别的,一些配置是topology级别的。default。yaml里面有所有的默认配置。你可以通过定义一个storm.yaml在你的classpath里面来覆盖这些默认的配置并且你也可以在代码里面设置一些topology相关的配置信息(使用StromSubmitter)。

 

posted on 2017-03-09 16:40  我是一条淡水鱼  阅读(1511)  评论(0编辑  收藏  举报