1,分布式单词计数的流程

首先要有数据源,在SentenceSpout中定义了一个字符串数组sentences来模拟数据源。字符串数组中的每句话作为一个tuple发射。其实,SplitBolt接收SentenceSpout发射的tuple,它将每句话分割成每个单词,并将每个单词作为tuple发射。再次,WordCountBolt接收SplitBolt发送的tuple,它将接收到的每一个单词统计计数,并将 <单词:出现次数> 作为tuple发射。最后,ReportBolt接收WordCountBolt发送的tuple,将统计的结果存入HashMap中,并打印出结果。

流程图如下:

2,Topology的组成类

ISpout、IComponent、IBolt三个接口定义了一些最基本的方法,BaseRichSpout、BaseRichBolt是接口的实现类,自定义的Spout与Bolt通过继承实现类来完成工作。

aaarticlea/png;base64,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" alt="" />

详细解释参考代码里面的注释。参考《Storm分布式实时计算模式》第一章中的例子。

1, SentenceSpou.java分析:

private String[] sentences = { "my dog has fleas", "i like cold beverages",
"the dog ate my homework", "don't have a cow man",
"i don't think i like fleas" };

定义了待发射的数据源。Spout从该字符串数组一次取一个字符串生成tuple进行发射。

32     public void open(@SuppressWarnings("rawtypes") Map conf,
33 TopologyContext context, SpoutOutputCollector collector) {
34 // TODO Auto-generated method stub
35 this.collector = collector;
36 }

open函数,在ISpout接口中定义,所有的Spout组件在初始化时调用这个方法。在open()中初始化了发射器。

55     public void declareOutputFields(OutputFieldsDeclarer declarer) {
56 // TODO Auto-generated method stub
57 declarer.declare(new Fields("sentence"));// 标记SentenceSpout发送的tuple的键为
58 // sentence
59 }

declareOutputFields函数标记了该Spout发射的tuple的(字段值)键值。下游的Bolt可以通过该键值来接收它发出的tuple

41     public void nextTuple() {
42 // TODO Auto-generated method stub
43 // 以字符串数组sentences 中的每个字符串 作为参数 构造tuple
44 this.collector.emit(new Values(sentences[index]));// 通过emit方法将构造好的tuple发送出去
45 index++;
46 if (index >= sentences.length) {
47 index = 0;
48 }
49 Utils.sleep(100);
50 }

nextTuple()是所有Spout的核心方法。Storm通过调用这个方法向collector发射tuple。Values.java 继承了ArrayList,new Values(...)构造了一个List对象,并将之作为emit的参数通过collector发射出去。

这里的发射规则是:每次发射其中一个字符串,阻塞100ms。当发射完整个字符串数组时,将索引(index)重新置0。可以继续发射。除非显示终止Topology,否则它不会停止。

SentenceSpou.java代码如下:

 1 package org.apache.storm.storm_core;
2
3 import java.util.Map;
4
5 import backtype.storm.spout.SpoutOutputCollector;
6 import backtype.storm.task.TopologyContext;
7 import backtype.storm.topology.OutputFieldsDeclarer;
8 import backtype.storm.topology.base.BaseRichSpout;
9 import backtype.storm.tuple.Fields;
10 import backtype.storm.tuple.Values;
11 import backtype.storm.utils.Utils;
12
13 public class SentenceSpout extends BaseRichSpout {
14 /**
15 *
16 */
17 private static final long serialVersionUID = 3444934973982660864L;
18 private SpoutOutputCollector collector;// 用来向其他Spout发射tuple
19 private String[] sentences = { "my dog has fleas", "i like cold beverages",
20 "the dog ate my homework", "don't have a cow man",
21 "i don't think i like fleas" };
22
23 private int index = 0;
24
25 /*
26 * open() 方法在所有的Spout组件初始化时被调用
27 *
28 * @param Map conf storm 配置信息
29 *
30 * @context TopologyContext topology 组件信息
31 */
32 public void open(@SuppressWarnings("rawtypes") Map conf,
33 TopologyContext context, SpoutOutputCollector collector) {
34 // TODO Auto-generated method stub
35 this.collector = collector;
36 }
37
38 /*
39 * Values.java extends ArrayList Storm 调用该方法向输出的collector发射tuple
40 */
41 public void nextTuple() {
42 // TODO Auto-generated method stub
43 // 以字符串数组sentences 中的每个字符串 作为参数 构造tuple
44 this.collector.emit(new Values(sentences[index]));// 通过emit方法将构造好的tuple发送出去
45 index++;
46 if (index >= sentences.length) {
47 index = 0;
48 }
49 Utils.sleep(100);
50 }
51
52 /*
53 * SentenceSpout 发送的tuple它是一个包含键值对的List,该方法声明了List中包含的键值对的键为 sentence
54 */
55 public void declareOutputFields(OutputFieldsDeclarer declarer) {
56 // TODO Auto-generated method stub
57 declarer.declare(new Fields("sentence"));// 标记SentenceSpout发送的tuple的键为
58 // sentence
59 }
60 }

SplitBolt.java代码如下:

 1 package org.apache.storm.storm_core;
2
3 import java.util.Map;
4
5 import backtype.storm.task.OutputCollector;
6 import backtype.storm.task.TopologyContext;
7 import backtype.storm.topology.OutputFieldsDeclarer;
8 import backtype.storm.topology.base.BaseRichBolt;
9 import backtype.storm.tuple.Fields;
10 import backtype.storm.tuple.Tuple;
11 import backtype.storm.tuple.Values;
12
13 public class SplitSentenceBolt extends BaseRichBolt {
14 /**
15 *
16 */
17 private static final long serialVersionUID = -2107029392155190729L;
18 private OutputCollector collector;// 用来向其他Spout发射tuple的发射器
19
20 /*
21 * (non-Javadoc) prepare方法类似于open方法,prepare在bolt初始化时被调用
22 */
23 public void prepare(Map stormConf, TopologyContext context,
24 OutputCollector collector) {
25 // TODO Auto-generated method stub
26 this.collector = collector;// 发射器初始化
27
28 }
29
30 public void execute(Tuple input) {
31 // TODO Auto-generated method stub
32 // 接收从SentenceSpout的发射器发射过来的tuple,因为SentenceSpout中声明的tuple字段为sentence,故getStringByField方法的参数为sentence
33 String sentence = input.getStringByField("sentence");// 该tuple是一个包含
34 // 键为sentence
35 // 值为字符串
36 // 的列表List<Map<sentence,String>>
37 String[] words = sentence.split(" ");// 将字符串分解成一个个的单词
38 for (String word : words)
39 this.collector.emit(new Values(word));// 将每个单词构造成tuple并发送给下一个Spout
40 }
41
42 public void declareOutputFields(OutputFieldsDeclarer declarer) {
43 // TODO Auto-generated method stub
44 declarer.declare(new Fields("word"));// 定义SplitSentenceBolt发送的tuple的字段("键值")为 word
45 }
46 }

WordCountBolt.java

 package org.apache.storm.storm_core;

 import java.util.HashMap;
import java.util.Map; import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichBolt;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values; public class WordCountBolt extends BaseRichBolt{ private OutputCollector collector;
private HashMap<String, Long>counts = null;//统计每个单词出现的次数,放到HashMap中保存起来 public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
// TODO Auto-generated method stub
this.collector = collector;
this.counts = new HashMap<String, Long>();//初始化HashMap,因为prepare会被自动调用的
} public void execute(Tuple input) {
// TODO Auto-generated method stub
String word = input.getStringByField("word");
Long count = this.counts.get(word);
if(count == null)//HashMap中没有word这个单词
count = 0L;
count++;
this.counts.put(word, count);//更新该单词在HashMap中的统计次数
//此处发射的tuple包含了两个元素:单词和计数,它每次发送的是一个长度为2的List,
//可理解为:List.add(new HashMap("word",word)); List.add(new HashMap(("count",count));
this.collector.emit(new Values(word, count));//第一个元素的键为 "word",值为该单词(a string),第二个键为 "count",值为单词的计数
} public void declareOutputFields(OutputFieldsDeclarer declarer) {
// TODO Auto-generated method stub
declarer.declare(new Fields("word", "count"));
}
}

ReportBolt.java如下:

package org.apache.storm.storm_core;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichBolt;
import backtype.storm.tuple.Tuple; public class ReportBolt extends BaseRichBolt{
/**
*
*/
private static final long serialVersionUID = 4921144902730095910L;
// private OutputCollector collector; ReportBolt不需要发射tuple了
private HashMap<String, Long> counts = null; public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
// TODO Auto-generated method stub
this.counts = new HashMap<String, Long>();
} public void execute(Tuple input) {
// TODO Auto-generated method stub
String word = input.getStringByField("word");
Long count = input.getLongByField("count");
this.counts.put(word, count);
} public void declareOutputFields(OutputFieldsDeclarer declarer) {
// TODO Auto-generated method stub
//不需要发出任何数据流
} //Topology在storm集群中运行时,cleanup方法是不可靠的,并不能保证它一定会执行
public void cleanup(){
System.out.println("------ print counts ------");
List<String> keys = new ArrayList<String>();
keys.addAll(counts.keySet());//将HashMap中所有的键都添加到一个集合中
Collections.sort(keys);//对键(单词)进行排序
for(String key : keys)//输出排好序的每个单词的出现次数
System.out.println(key + " : " + this.counts.get(key));
System.out.println("--------bye----------");
}
}

WordCountTopology.java如下:

 1 package org.apache.storm.storm_core;
2
3 import backtype.storm.Config;
4 import backtype.storm.LocalCluster;
5 import backtype.storm.topology.TopologyBuilder;
6 import backtype.storm.tuple.Fields;
7 import backtype.storm.utils.Utils;
8
9 public class WordCountTopology {
10 private static final String SENTENCE_SPOUT_ID = "sentence-spout";
11 private static final String SPLIT_BOLT_ID = "split-bolt";
12 private static final String COUNT_BOLT_ID = "count-bolt";
13 private static final String REPORT_BOLT_ID = "report-bolt";
14 private static final String TOPOLOGY_NAME = "word-count-topology";
15
16 public static void main(String[] args) throws Exception{
17 SentenceSpout spout = new SentenceSpout();
18 SplitSentenceBolt splitBolt = new SplitSentenceBolt();
19 WordCountBolt countBolt = new WordCountBolt();
20 ReportBolt reportBolt = new ReportBolt();
21
22 TopologyBuilder builder = new TopologyBuilder();
23 builder.setSpout(SENTENCE_SPOUT_ID, spout);
24 builder.setBolt(SPLIT_BOLT_ID, splitBolt).shuffleGrouping(SENTENCE_SPOUT_ID);
25 builder.setBolt(COUNT_BOLT_ID, countBolt).fieldsGrouping(SPLIT_BOLT_ID, new Fields("word"));
26 builder.setBolt(REPORT_BOLT_ID, reportBolt).globalGrouping(COUNT_BOLT_ID);
27
28 Config config = new Config();
29 LocalCluster cluster = new LocalCluster();
30
31 cluster.submitTopology(TOPOLOGY_NAME, config, builder.createTopology());
32 Utils.sleep(1000);
33 cluster.killTopology(TOPOLOGY_NAME);
34 cluster.shutdown();
35
36 }
37 }

Storm WordCount Topology学习的更多相关文章

  1. 2 storm的topology提交执行

    本博文的主要内容有 .storm单机模式,打包,放到storm集群 .Storm的并发机制图 .Storm的相关概念 .附PPT 打包,放到storm集群去.我这里,是单机模式下的storm. wee ...

  2. 3、SpringBoot 集成Storm wordcount

    WordCountBolt public class WordCountBolt extends BaseBasicBolt { private Map<String,Integer> c ...

  3. 关于Storm 中Topology的并发度的理解

    来自:https://storm.apache.org/documentation/Understanding-the-parallelism-of-a-Storm-topology.html htt ...

  4. Storm编程入门API系列之Storm的Topology默认Workers、默认executors和默认tasks数目

    关于,storm的启动我这里不多说了. 见博客 storm的3节点集群详细启动步骤(非HA和HA)(图文详解) 建立stormDemo项目 Group Id :  zhouls.bigdata Art ...

  5. Storm编程入门API系列之Storm的Topology多个Workers数目控制实现

    前期博客 Storm编程入门API系列之Storm的Topology默认Workers.默认executors和默认tasks数目 继续编写 StormTopologyMoreWorker.java ...

  6. Storm编程入门API系列之Storm的Topology多个Executors数目控制实现

    前期博客 Storm编程入门API系列之Storm的Topology默认Workers.默认executors和默认tasks数目 Storm编程入门API系列之Storm的Topology多个Wor ...

  7. Storm编程入门API系列之Storm的Topology多个tasks数目控制实现

    前期博客 Storm编程入门API系列之Storm的Topology默认Workers.默认executors和默认tasks数目 Storm编程入门API系列之Storm的Topology多个Wor ...

  8. Storm提交Topology报错:Found multiple defaults.yaml resources.

    Storm提交Topology运行方式分为本地和集群运行两种,其中集群运行需要将程序打包并把jar包复制到集群,通过以下方式执行: bin/storm jar /opt/run/storm-demo- ...

  9. Twitter Storm中Topology的状态

    Twitter Storm中Topology的状态 状态转换如下,Topology 的持久化状态包括: active, inactive, killed, rebalancing 四个状态. 代码上看 ...

随机推荐

  1. Oracle 数据表误删恢复 Flashback

    1. 前提条件. recyclebin 参数打开. 验证参数是否打开: SHOW PARAMETER RECYCLEBIN 2. 如果参数没有打开的话 需要打开,并且重启一下数据库方法为 alter ...

  2. PSexec以及xcopy的简单使用

    1. 远程执行命令. 有时候不想远程但是想执行一些命令, 比较简单的方法是: 下载systeminternals 然后解压缩后可以讲目录放到path环境变量中 然后打开命令行工具 输入 如下的命令 p ...

  3. 从网上整理的一些delphi字符串加密解密方法

    function Encode(Str: string): string; var //加密 TmpChr: AnsiChar; i, Len: integer; begin Result := St ...

  4. 为Bootstrap模态对话框添加拖拽移动功能

    请自行下载使用到的Bootstrap库及jQuery库 <!DOCTYPE html> <html> <head lang="en"> < ...

  5. 闭包自由变量引用对象的问题 http://bbs.pythontab.com/thread-4266-1-1.html

  6. Galaxy S10使用几乎零黑边框的OLED显示屏

    2019年的首波安卓旗舰中,目前关于三星Galaxy S10的爆料是最多的,在销量连续萎缩后,外界对手机一哥的“发力之作”充满期待. 据TheElec报道,Galaxy S10正面使用的是一块几乎零黑 ...

  7. Linux学习之/etc/init.d/目录和rc.local脚本

    init.d目录中包含很多系统服务的启动和停止脚本,比较常用的就是网络服务,当你修改了网络配置时,可以自行 sudo /etc/init.d/networking restart  命令来重启网络服务 ...

  8. 关于js特效轮播图练习

    [出现问题] js轮播图,图片未正常轮播. [解决方法] 通过对代码的检查,发现是以下三个原因造成的错误. 1.js代码问题 js代码使用alert(test);,测试修改完毕后,发现依然没有解决错误 ...

  9. Catenyms POJ - 2337(单词+字典序输出路径)

    题意: 就是给出几个单词 看能否组成欧拉回路或路径  当然还是让输出组成的最小字典序的路 解析: 还是把首尾字母看成点   把单词看成边 记录边就好了 这题让我对fleury输出最小字典序又加深了一些 ...

  10. Lights inside a 3d Grid UVA - 11605(概率)

    题意: 给出一个n * m * h的空间 每次任意选择两个点  使得在以这两个点连线为对角线的空间的点的值 取反  (初始为0) 求经过k次操作后最后有多少点的值为1 解析: 遇到坐标分维去看  把三 ...