本文可作为 <<Storm-分布式实时计算模式>>一书1.5节的读书笔记

数据流分组定义了一个数据流中的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等等

现在我们看一个例子:

就是最经常见的数单词的例子

public class WordCountBolt extends BaseRichBolt{
    private OutputCollector collector;
    private HashMap<String, Long> counts = null;

    public void prepare(Map config, TopologyContext context,
            OutputCollector collector) {
        this.collector = collector;
        this.counts = new HashMap<String, Long>();
    }

    public void execute(Tuple tuple) {
        String word = tuple.getStringByField("word");
        Long count = this.counts.get(word);
        if(count == null){
            count = 0L;
        }
        count++;
        this.counts.put(word, count);
        this.collector.emit(new Values(word, count));
    }

    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("word", "count"));
    }
}

在添加这个bolt的时候,使用的是按字段分组,如下

 builder.setBolt(COUNT_BOLT_ID, countBolt,4)
                .fieldsGrouping(SPLIT_BOLT_ID, new Fields("word"));

如果我们分组模式改成

 builder.setBolt(COUNT_BOLT_ID, countBolt,4)
                .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喽

那么如果使用

 builder.setBolt(COUNT_BOLT_ID, countBolt,4)
                .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修改

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

最开始的时候

运行的结果如下:

mystorm.PrintBolt@67178f5d   String recieved: edi:I'm happy!
mystorm.PrintBolt@67178f5d   String recieved: marry:I'm angry!
mystorm.PrintBolt@393ddf54   String recieved: ted:I'm excited!
mystorm.PrintBolt@393ddf54   String recieved: john:I'm sad!
mystorm.PrintBolt@5f97cfcb   String recieved: marry:I'm angry!

不同的task都平均收到了tuple





然后我想让指定某些句子只让某个task接受,怎么办?

首先看ExclaimBasicBolt

public class ExclaimBasicBolt extends BaseBasicBolt {

	/**
	 *
	 */
	private static final long serialVersionUID = -6239845315934660303L;
	private List<Integer> list;
	private List<Integer> list2;

	@Override
	public void execute(Tuple tuple, BasicOutputCollector collector) {
		//String sentence = tuple.getString(0);
		String sentence = (String) tuple.getValue(0);
		String out = sentence + "!";
		if (out.startsWith("e")) {
			collector.emitDirect(list.get(0),new Values(out));
		}else {
			collector.emitDirect(list2.get(0),new Values(out));
		}

	}

	@Override
	public void declareOutputFields(OutputFieldsDeclarer declarer) {
		declarer.declare(true,new Fields("excl_sentence"));
	}

    @Override
    public void prepare(Map stormConf, TopologyContext context) {
    	list =context.getComponentTasks("print");
    	list2=context.getComponentTasks("print2");
    }
}

在构建topology的时候

使用directGrouping

		builder.setSpout("spout", new RandomSpout());
		builder.setBolt("exclaim", new ExclaimBasicBolt(),3).shuffleGrouping("spout");
		builder.setBolt("print", new PrintBolt(),3).directGrouping("exclaim");
		builder.setBolt("print2", new PrintBolt2(),3).directGrouping("exclaim");

PrintBolt2与PrintBolt类似

只是打印的时候打印出 System.err.println(this+"  i am two   String recieved: " + rec);

OK这下运行的时候我们就能看到

mystorm.PrintBolt2@238ac8bf   String recieved: ted:I'm excited!
mystorm.PrintBolt2@238ac8bf   String recieved: john:I'm sad!
mystorm.PrintBolt2@238ac8bf   String recieved: marry:I'm angry!
mystorm.PrintBolt2@238ac8bf   String recieved: ted:I'm excited!
mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!
mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!
mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!
mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!
mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!
mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!
mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!
mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!
mystorm.PrintBolt@611b7a20  i am two   String recieved: edi:I'm happy!
mystorm.PrintBolt2@238ac8bf   String recieved: marry:I'm angry!
mystorm.PrintBolt2@238ac8bf   String recieved: ted:I'm excited!
mystorm.PrintBolt2@238ac8bf   String recieved: marry:I'm angry!

所有e开头的句子 都跑到Print2这个Bolt的某个task里面了。

本节的整体代码见

package mystorm;

public class ExclaimBasicTopo {
	public static void main(String[] args) throws Exception {
		TopologyBuilder builder = new TopologyBuilder();

		builder.setSpout("spout", new RandomSpout());
		builder.setBolt("exclaim", new ExclaimBasicBolt(),3).shuffleGrouping("spout");
		builder.setBolt("print", new PrintBolt(),3).shuffleGrouping("exclaim");

		Config conf = new Config();
		conf.setDebug(false);

		if (args != null && args.length > 0) {
			conf.setNumWorkers(3);
			StormSubmitter.submitTopology(args[0], conf, builder.createTopology());
		} else {
			LocalCluster cluster = new LocalCluster();
			cluster.submitTopology("test", conf, builder.createTopology());
		}
	}
}

package mystorm;

public class RandomSpout extends BaseRichSpout {

	private SpoutOutputCollector collector;
	private Random rand;
	private int index;
	private static String[] sentences = new String[] {
		"edi:I'm happy", "marry:I'm angry", "john:I'm sad", "ted:I'm excited", "laden:I'm dangerous"};

	@Override
	public void open(Map conf, TopologyContext context,SpoutOutputCollector collector) {
		this.collector = collector;
		this.rand = new Random();
	}

	@Override
	public void nextTuple() {
		if (index<10*sentences.length) {
			String toSay = sentences[rand.nextInt(sentences.length)];
			this.collector.emit(new Values(toSay));
			index++;
		}else {
			try {
				Thread.sleep(1000);
				System.out.println("我停了一秒");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

	@Override
	public void declareOutputFields(OutputFieldsDeclarer declarer) {
		declarer.declare(new Fields("sentence"));
	}

}

package mystorm;

public class ExclaimBasicBolt extends BaseBasicBolt {

	@Override
	public void execute(Tuple tuple, BasicOutputCollector collector) {
		//String sentence = tuple.getString(0);
		String sentence = (String) tuple.getValue(0);
		String out = sentence + "!";
		collector.emit(new Values(out));
	}

	@Override
	public void declareOutputFields(OutputFieldsDeclarer declarer) {
		declarer.declare(new Fields("excl_sentence"));
	}

}

package mystorm;

public class PrintBolt extends BaseBasicBolt {

	@Override
	public void execute(Tuple tuple, BasicOutputCollector collector) {
		String rec = tuple.getString(0);
		System.err.println(this+"   String recieved: " + rec);
	}
	@Override
	public void declareOutputFields(OutputFieldsDeclarer declarer) {
		// do nothing
	}

}

Strom数据流分组解析的更多相关文章

  1. 使用Java正则表达式的分组解析身份证的年月日

    根据Java的Pattern和Matcher类通过分组解析出身份证的年月日: public class GetDateInIdCard { public static void main(String ...

  2. 022_STM32中断优先级分组解析

    (0)STM32有十六个优先级 (一)STM32分组为:组0-4 (二)分组配置在寄存器SCB->AIRCR中: (三)解析第二点 1. 组0就是4位都用来设置成响应优先级,2^4=16位都是响 ...

  3. wordcount数据流过程解析

    (1)执行hadoopFile()操作,其中有生成HadoopRDD的new 方法.然后执行map方法.pair => pair._2.toString,只对Value值进行操作.在textFi ...

  4. 大数据处理框架之Strom:认识storm

    Storm是分布式实时计算系统,用于数据的实时分析.持续计算,分布式RPC等. (备注:5种常见的大数据处理框架:· 仅批处理框架:Apache Hadoop:· 仅流处理框架:Apache Stor ...

  5. 简单聊聊Storm的流分组策略

    简单聊聊Storm的流分组策略 首先我要强调的是,Storm的分组策略对结果有着直接的影响,不同的分组的结果一定是不一样的.其次,不同的分组策略对资源的利用也是有着非常大的不同,本文主要讲一讲loca ...

  6. kcp-go源码解析

    概念 ARQ:自动重传请求(Automatic Repeat-reQuest,ARQ)是OSI模型中数据链路层的错误纠正协议之一.RTO:Retransmission TimeOutFEC:Forwa ...

  7. cdnbest如何让用户访问走最近最快的线路(分组线路)

    用户访问网站有时网络有互通的问题,cdnbest的分组解析可以细分线路,让用户访问自动走最优线路,线路不细分都加默认里,访问的节点是随机分配的 下面我们讲下如何设置: 比如你有电信,移动,和国外的节点 ...

  8. 【原】Storm基本概念

    Storm入门教程 1. Storm基础 Storm Storm主要特点 Storm基本概念 Topologies Streams Spouts Bolts Stream groupings Reli ...

  9. Android WebRTC 音视频开发总结

    www.cnblogs.com/lingyunhu/p/3621057.html 前面介绍了WebRTCDemo的基本结构,本节主要介绍WebRTC音视频服务端的处理,,转载请说明出处(博客园RTC. ...

随机推荐

  1. [SDOI 2008]Cave 洞穴勘测

    Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好 ...

  2. [ZJOI2015]幻想乡战略游戏

    Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来, ...

  3. VK-Cup 2017 qualification 1

    VK-Cup,cf里面只有切成俄文才能看到,题目也都是俄文的(百度翻译成英文和中文). 两人组队参赛的,赛期1天,乐多赛赛制(和时间基本无关,交上去挂了扣分).这次是第一场资格赛. 这次又和ditol ...

  4. bzoj 4025: 二分图

    Description 神犇有一个n个节点的图.因为神犇是神犇,所以在T时间内一些边会出现后消失.神犇要求出每一时间段内这个图是否是二分图.这么简单的问题神犇当然会做了,于是他想考考你. 解题报告: ...

  5. 【UOJ UNR #1】火车管理

    来自FallDream的博客,未经允许,请勿转载,谢谢. 题面 考虑用可持久化线段树直接维护每个点在不同时刻,第一辆车的编号. 这样3操作就变成了区间赋值,1操作变成区间和 2操作的话,只需要查询一下 ...

  6. bzoj1233[Usaco2009Open]干草堆tower 单调队列优化dp

    1233: [Usaco2009Open]干草堆tower Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 983  Solved: 464[Submi ...

  7. bzoj4559[JLoi2016]成绩比较 容斥+拉格朗日插值法

    4559: [JLoi2016]成绩比较 Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 261  Solved: 165[Submit][Status ...

  8. AD域中添加了一个策略导致的问题

    AD域中添加了一个策略,导致浏览器报了一个错,点击下拉菜单选不了.查了资料说是添加信任站点就好,结果信任站点是置灰的,服务器有这个问题,本地是好的. 解决办法:本地服务器有个ie增强设置,关掉就好.

  9. 一日一练-JS toString 和valueOf 方法的联系与区别

    子曰:类型转换中toString 和valueOf 的联系与区别分析 首先是看看ES5 的规范是如何进行说明的 在这里有几个基础知识点需要了解一下: [[Class]] [[Class]] 属于Obj ...

  10. JVM回收方法区内存

    很多人认为方法区(或者HotSpot虚拟机中的永久代)是没有垃圾收集的,Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区进行垃圾收集的“性价比”一般比较低:在堆中,尤其是 ...