storm数据流分组

数据流分组定义了一个数据流中的tuple如何分发给topology中不同bolt的task。


Shuffle grouping(随机分组):这种方式会随机分发 tuple 给 bolt 的各个 task,每个bolt 实例接收到的相同数量的 tuple。
Fields grouping(按字段分组):根据指定字段的值进行分组。比如说,一个数据流根据“word”字段进行分组,所有具有相同“word”字段值的 tuple 会路由到同一个 bolt 的 task 中。
All grouping(全复制分组):将所有的 tuple 复制后分发给所有 bolt task。每个订阅数据流的 task 都会接收到 tuple 的拷贝。
Globle grouping(全局分组):这种分组方式将所有的 tuples 路由到唯一一个 task 上。Storm 按照最小的 task ID 来选取接收数据的 task。注意,当使用全局分组方式时,设置 bolt 的 task 并发度是没有意义的,因为所有 tuple 都转发到同一个 task 上了。使用全局分组的时候需要注意,因为所有的 tuple 都转发到一个 JVM 实例上,可能会引起 Storm 集群中某个 JVM 或者服务器出现性能瓶颈或崩溃。
None grouping(不分组):在功能上和随机分组相同,是为将来预留的。
Direct grouping(指向型分组):数据源会调用 emitDirect() 方法来判断一个 tuple 应该由哪个 Storm 组件来接收。只能在声明了是指向型的数据流上使用。
Local or shuffle grouping(本地或随机分组):和随机分组类似,但是,会将 tuple 分发给同一个 worker 内的 bolt task(如果 worker 内有接收数据的 bolt task)。其他情况下,采用随机分组的方式。取决于 topology 的并发度,本地或随机分组可以减少网络传输,从而提高 topology 性能。



随机分组

最经常用的也就是Shuffle grouping,Fields grouping,Direct grouping等等
现在我们看一个例子:
就是最经常见的数单词的例子

[java]  view plain  copy
 
  1. public class WordCountBolt extends BaseRichBolt{  
  2.     private OutputCollector collector;  
  3.     private HashMap<String, Long> counts = null;  
  4.   
  5.   
  6.     public void prepare(Map config, TopologyContext context,   
  7.             OutputCollector collector) {  
  8.         this.collector = collector;  
  9.         this.counts = new HashMap<String, Long>();  
  10.     }  
  11.   
  12.   
  13.     public void execute(Tuple tuple) {  
  14.         String word = tuple.getStringByField("word");  
  15.         Long count = this.counts.get(word);  
  16.         if(count == null){  
  17.             count = 0L;  
  18.         }  
  19.         count++;  
  20.         this.counts.put(word, count);  
  21.         this.collector.emit(new Values(word, count));  
  22.     }  
  23.   
  24.   
  25.     public void declareOutputFields(OutputFieldsDeclarer declarer) {  
  26.         declarer.declare(new Fields("word""count"));  
  27.     }  
  28. }  
在添加这个bolt的时候,使用的是按字段分组,如下
[java]  view plain  copy
 
  1. builder.setBolt(COUNT_BOLT_ID, countBolt,4)  
  2.                .fieldsGrouping(SPLIT_BOLT_ID, new Fields("word"));  
如果我们分组模式改成
[java]  view plain  copy
 
  1. builder.setBolt(COUNT_BOLT_ID, countBolt,4)  
  2.                .shuffleGrouping(SPLIT_BOLT_ID);  
那么对单词的统计就会偏少。
为什么?
大家想想恩,有4个countbolt实例(咱们暂时称之为countbolta,b,c,d),如果我是随机分组,the这个单词出现了3回,前两回被分配到了countbolta,第三回被分配到了countboltb,那么后面的reportbolt先收到了<the,2>这个tuple(来自countbolta),然后又收到了<the,1>这个tuple(来自countboltb),最后的输出肯定是the:1喽
那么如果使用
[java]  view plain  copy
 
  1. builder.setBolt(COUNT_BOLT_ID, countBolt,4)  
  2.                .fieldsGrouping(SPLIT_BOLT_ID, new Fields("word"));  
自然就不会出现刚才的问题了,为什么,大家自己想。



直接分组

这里我引用一个用storm给句子加感叹号的例子,代码在最后

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

以下为16-7-4修改

其实我下面这例子不好

直接分组,主要是保证把消息给bolt中某一个特定的task

而下面的例子的实际效果是想吧 messagea给bolta,messageb给boltb

那么其实还有更方便的做法,就是

在发送是:

public void execute(Tuple tuple, BasicOutputCollector collector) {
     tpsCounter.count();

     Long tupleId = tuple.getLong(0);
     Object obj = tuple.getValue(1);

     if (obj instanceof TradeCustomer) {

         TradeCustomer tradeCustomer = (TradeCustomer)obj;

         Pair trade = tradeCustomer.getTrade();
         Pair customer = tradeCustomer.getCustomer();

            collector.emit(SequenceTopologyDef.TRADE_STREAM_ID, 
                    new Values(tupleId, trade));

            collector.emit(SequenceTopologyDef.CUSTOMER_STREAM_ID, 
                    new Values(tupleId, customer));
     }else if (obj != null){
         LOG.info("Unknow type " + obj.getClass().getName());
     }else {
         LOG.info("Nullpointer " );
     }

 }

在提交时:

builder.setBolt(SequenceTopologyDef.SPLIT_BOLT_NAME, new SplitRecord(), 2).shuffleGrouping(
                        SequenceTopologyDef.SEQUENCE_SPOUT_NAME);

                builder.setBolt(SequenceTopologyDef.TRADE_BOLT_NAME, new PairCount(), 1).shuffleGrouping(
                        SequenceTopologyDef.SPLIT_BOLT_NAME,  // --- 发送方名字
                        SequenceTopologyDef.TRADE_STREAM_ID); // --- 接收发送方该stream 的tuple

                builder.setBolt(SequenceTopologyDef.CUSTOMER_BOLT_NAME, new PairCount(), 1)
                        .shuffleGrouping(SequenceTopologyDef.SPLIT_BOLT_NAME, // --- 发送方名字
                                SequenceTopologyDef.CUSTOMER_STREAM_ID);      // --- 接收发送方该stream 的tuple
定义输出格式

public void declareOutputFields(OutputFieldsDeclarer declarer) {
  declarer.declareStream(SequenceTopologyDef.TRADE_STREAM_ID, new Fields("ID", "TRADE"));
  declarer.declareStream(SequenceTopologyDef.CUSTOMER_STREAM_ID, new Fields("ID", "CUSTOMER"));
 }
最后接收的时候还得判断一下

if (input.getSourceStreamId().equals(SequenceTopologyDef.TRADE_STREAM_ID) ) {
            customer = pair;
            customerTuple = input;

            tradeTuple = tradeMap.get(tupleId);
            if (tradeTuple == null) {
                customerMap.put(tupleId, input);
                return;
            }

            trade = (Pair) tradeTuple.getValue(1);

        }

参考资料

数据的分流

以上为16-7-4修改

////////////////////////////////////////////////////////////////////////////////////////////////////////////////


最开始的时候
运行的结果如下:
[html]  view plain  copy
 
  1. mystorm.PrintBolt@67178f5d   String recieved: edi:I'm happy!  
  2. mystorm.PrintBolt@67178f5d   String recieved: marry:I'm angry!  
  3. mystorm.PrintBolt@393ddf54   String recieved: ted:I'm excited!  
  4. mystorm.PrintBolt@393ddf54   String recieved: john:I'm sad!  
  5. mystorm.PrintBolt@5f97cfcb   String recieved: marry:I'm angry!  
不同的task都平均收到了tuple


然后我想让指定某些句子只让某个task接受,怎么办?
首先看ExclaimBasicBolt
[java]  view plain  copy
 
  1. public class ExclaimBasicBolt extends BaseBasicBolt {  
  2.   
  3.   
  4.     /** 
  5.      *  
  6.      */  
  7.     private static final long serialVersionUID = -6239845315934660303L;  
  8.     private List<Integer> list;  
  9.     private List<Integer> list2;  
  10.   
  11.   
  12.     @Override  
  13.     public void execute(Tuple tuple, BasicOutputCollector collector) {  
  14.         //String sentence = tuple.getString(0);  
  15.         String sentence = (String) tuple.getValue(0);  
  16.         String out = sentence + "!";  
  17.         if (out.startsWith("e")) {  
  18.             collector.emitDirect(list.get(0),new Values(out));  
  19.         }else {  
  20.             collector.emitDirect(list2.get(0),new Values(out));  
  21.         }  
  22.           
  23.     }  
  24.   
  25.   
  26.     @Override  
  27.     public void declareOutputFields(OutputFieldsDeclarer declarer) {  
  28.         declarer.declare(true,new Fields("excl_sentence"));  
  29.     }  
  30.       
  31.       
  32.     @Override  
  33.     public void prepare(Map stormConf, TopologyContext context) {  
  34.         list =context.getComponentTasks("print");  
  35.         list2=context.getComponentTasks("print2");  
  36.     }  
  37. }  
在构建topology的时候
使用directGrouping
[java]  view plain  copy
 
  1. builder.setSpout("spout"new RandomSpout());  
  2. builder.setBolt("exclaim"new ExclaimBasicBolt(),3).shuffleGrouping("spout");  
  3. builder.setBolt("print"new PrintBolt(),3).directGrouping("exclaim");  
  4. builder.setBolt("print2"new PrintBolt2(),3).directGrouping("exclaim");  
PrintBolt2与PrintBolt类似
只是打印的时候打印出 System.err.println(this+"  i am two   String recieved: " + rec);
OK这下运行的时候我们就能看到 
[java]  view plain  copy
 
  1. mystorm.PrintBolt2@238ac8bf   String recieved: ted:I'm excited!  
  2. mystorm.PrintBolt2@238ac8bf   String recieved: john:I'm sad!  
  3. mystorm.PrintBolt2@238ac8bf   String recieved: marry:I'm angry!  
  4. mystorm.PrintBolt2@238ac8bf   String recieved: ted:I'm excited!  
  5. mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!  
  6. mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!  
  7. mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!  
  8. mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!  
  9. mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!  
  10. mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!  
  11. mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!  
  12. mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!  
  13. mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!  
  14. mystorm.PrintBolt2@238ac8bf   String recieved: marry:I'm angry!  
  15. mystorm.PrintBolt2@238ac8bf   String recieved: ted:I'm excited!  
  16. mystorm.PrintBolt2@238ac8bf   String recieved: marry:I'm angry!  
所有e开头的句子 都跑到Print2这个Bolt的某个task里面了。

本节的整体代码见

[java]  view plain  copy
 
  1. package mystorm;  
  2.   
  3. public class ExclaimBasicTopo {  
  4.     public static void main(String[] args) throws Exception {  
  5.         TopologyBuilder builder = new TopologyBuilder();  
  6.           
  7.         builder.setSpout("spout"new RandomSpout());  
  8.         builder.setBolt("exclaim"new ExclaimBasicBolt(),3).shuffleGrouping("spout");  
  9.         builder.setBolt("print"new PrintBolt(),3).shuffleGrouping("exclaim");  
  10.   
  11.         Config conf = new Config();  
  12.         conf.setDebug(false);  
  13.   
  14.         if (args != null && args.length > 0) {  
  15.             conf.setNumWorkers(3);  
  16.             StormSubmitter.submitTopology(args[0], conf, builder.createTopology());  
  17.         } else {  
  18.             LocalCluster cluster = new LocalCluster();  
  19.             cluster.submitTopology("test", conf, builder.createTopology());  
  20.         }  
  21.     }  
  22. }  
  23.   
  24.   
  25. package mystorm;  
  26.    
  27. public class RandomSpout extends BaseRichSpout {  
  28.   
  29.     private SpoutOutputCollector collector;  
  30.     private Random rand;  
  31.     private int index;  
  32.     private static String[] sentences = new String[] {  
  33.         "edi:I'm happy""marry:I'm angry""john:I'm sad""ted:I'm excited""laden:I'm dangerous"};  
  34.       
  35.     @Override  
  36.     public void open(Map conf, TopologyContext context,SpoutOutputCollector collector) {  
  37.         this.collector = collector;  
  38.         this.rand = new Random();  
  39.     }  
  40.   
  41.     @Override  
  42.     public void nextTuple() {  
  43.         if (index<10*sentences.length) {  
  44.             String toSay = sentences[rand.nextInt(sentences.length)];  
  45.             this.collector.emit(new Values(toSay));  
  46.             index++;  
  47.         }else {  
  48.             try {  
  49.                 Thread.sleep(1000);  
  50.                 System.out.println("我停了一秒");  
  51.             } catch (InterruptedException e) {  
  52.                 // TODO Auto-generated catch block  
  53.                 e.printStackTrace();  
  54.             }  
  55.         }  
  56.           
  57.     }  
  58.   
  59.     @Override  
  60.     public void declareOutputFields(OutputFieldsDeclarer declarer) {  
  61.         declarer.declare(new Fields("sentence"));  
  62.     }  
  63.   
  64. }  
  65.   
  66. package mystorm;  
  67.   
  68. public class ExclaimBasicBolt extends BaseBasicBolt {  
  69.   
  70.     @Override  
  71.     public void execute(Tuple tuple, BasicOutputCollector collector) {  
  72.         //String sentence = tuple.getString(0);  
  73.         String sentence = (String) tuple.getValue(0);  
  74.         String out = sentence + "!";  
  75.         collector.emit(new Values(out));          
  76.     }  
  77.   
  78.     @Override  
  79.     public void declareOutputFields(OutputFieldsDeclarer declarer) {  
  80.         declarer.declare(new Fields("excl_sentence"));  
  81.     }  
  82.   
  83. }  
  84.   
  85.   
  86. package mystorm;  
  87.   
  88. public class PrintBolt extends BaseBasicBolt {  
  89.   
  90.     @Override  
  91.     public void execute(Tuple tuple, BasicOutputCollector collector) {  
  92.         String rec = tuple.getString(0);  
  93.         System.err.println(this+"   String recieved: " + rec);  
  94.     }  
  95.     @Override  
  96.     public void declareOutputFields(OutputFieldsDeclarer declarer) {  
  97.         // do nothing  
  98.     }  
  99.   
  100. }  

你可能感兴趣的:(storm数据流分组)