Storm实战之WordCount

来源:互联网 发布:域名反向解析 编辑:程序博客网 时间:2024/05/21 11:27
在storm环境部署完毕,并正确启动之后,现在就可以真正进入storm开发了,按照惯例,以wordcount作为开始。
这个例子很简单,核心组件包括:一个spout,两个bolt,一个Topology。

spout从一个路径读取文件,然后readLine,向bolt发射,一个文件处理完毕后,重命名,以不再重复处理。

第一个bolt将从spout接收到的字符串按空格split,产生word,发射给下一个bolt。

第二个bolt接收到word后,统计、计数,放到HashMap容器中。

1,定义一个spout,作用是源源不断滴向bolt发射字符串。

[Java] 纯文本查看 复制代码
01import java.io.File;
02import java.io.IOException;
03import java.util.Collection;
04import java.util.List;
05import java.util.Map;
06 
07import org.apache.commons.io.FileUtils;
08import org.apache.commons.io.filefilter.FileFilterUtils;
09 
10import backtype.storm.spout.SpoutOutputCollector;
11import backtype.storm.task.TopologyContext;
12import backtype.storm.topology.OutputFieldsDeclarer;
13import backtype.storm.topology.base.BaseRichSpout;
14import backtype.storm.tuple.Fields;
15import backtype.storm.tuple.Values;
16 
17public class WordReader extends BaseRichSpout {
18    private static final long serialVersionUID = 2197521792014017918L;
19    private String inputPath;
20    private SpoutOutputCollector collector;
21 
22    @Override
23    @SuppressWarnings(\"rawtypes\")
24    public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
25        this.collector = collector;
26        inputPath = (String) conf.get(\"INPUT_PATH\");
27    }
28 
29    @Override
30    public void nextTuple() {
31        Collection<File> files = FileUtils.listFiles(new File(inputPath),
32                FileFilterUtils.notFileFilter(FileFilterUtils.suffixFileFilter(\".bak\")), null);
33        for (File f : files) {
34            try {
35                List<String> lines = FileUtils.readLines(f, \"UTF-8\");
36                for (String line : lines) {
37                    collector.emit(new Values(line));
38                }
39                FileUtils.moveFile(f, new File(f.getPath() + System.currentTimeMillis() + \".bak\"));
40            catch (IOException e) {
41                e.printStackTrace();
42            }
43        }
44    }
45 
46    @Override
47    public void declareOutputFields(OutputFieldsDeclarer declarer) {
48        declarer.declare(new Fields(\"line\"));
49    }
50 
51}



2,定义一个bolt,作用是接收spout发过来的字符串,并分割成word,发射给下一个bolt。

[Java] 纯文本查看 复制代码
01import org.apache.commons.lang.StringUtils;
02 
03import backtype.storm.topology.BasicOutputCollector;
04import backtype.storm.topology.OutputFieldsDeclarer;
05import backtype.storm.topology.base.BaseBasicBolt;
06import backtype.storm.tuple.Fields;
07import backtype.storm.tuple.Tuple;
08import backtype.storm.tuple.Values;
09 
10public class WordSpliter extends BaseBasicBolt {
11 
12    private static final long serialVersionUID = -5653803832498574866L;
13 
14    @Override
15    public void execute(Tuple input, BasicOutputCollector collector) {
16        String line = input.getString(0);
17        String[] words = line.split(\" \");
18        for (String word : words) {
19            word = word.trim();
20            if (StringUtils.isNotBlank(word)) {
21                word = word.toLowerCase();
22                collector.emit(new Values(word));
23            }
24        }
25    }
26 
27    @Override
28    public void declareOutputFields(OutputFieldsDeclarer declarer) {
29        declarer.declare(new Fields(\"word\"));
30 
31    }
32 
33}



3,定义一个bolt,接收word,并统计。

[Java] 纯文本查看 复制代码
01import java.util.HashMap;
02import java.util.Map;
03import java.util.Map.Entry;
04 
05import backtype.storm.task.TopologyContext;
06import backtype.storm.topology.BasicOutputCollector;
07import backtype.storm.topology.OutputFieldsDeclarer;
08import backtype.storm.topology.base.BaseBasicBolt;
09import backtype.storm.tuple.Tuple;
10 
11public class WordCounter extends BaseBasicBolt {
12    private static final long serialVersionUID = 5683648523524179434L;
13    private HashMap<String, Integer> counters = new HashMap<String, Integer>();
14    private volatile boolean edit = false;
15 
16    @Override
17    @SuppressWarnings(\"rawtypes\")
18    public void prepare(Map stormConf, TopologyContext context) {
19        final long timeOffset = Long.parseLong(stormConf.get(\"TIME_OFFSET\").toString());
20        new Thread(new Runnable() {
21            @Override
22            public void run() {
23                while (true) {
24                    if (edit) {
25                        for (Entry<String, Integer> entry : counters.entrySet()) {
26                            System.out.println(entry.getKey() + \" : \" + entry.getValue());
27                        }
28                        System.out.println(\"WordCounter---------------------------------------\");
29                        edit = false;
30                    }
31                    try {
32                        Thread.sleep(timeOffset * 1000);
33                    catch (InterruptedException e) {
34                        e.printStackTrace();
35                    }
36                }
37            }
38        }).start();
39    }
40 
41    @Override
42    public void execute(Tuple input, BasicOutputCollector collector) {
43        String str = input.getString(0);
44        if (!counters.containsKey(str)) {
45            counters.put(str, 1);
46        else {
47            Integer c = counters.get(str) + 1;
48            counters.put(str, c);
49        }
50        edit = true;
51        System.out.println(\"WordCounter+++++++++++++++++++++++++++++++++++++++++++\");
52    }
53 
54    @Override
55    public void declareOutputFields(OutputFieldsDeclarer declarer) {
56 
57    }
58}



注意WordCounter类的prepare方法,里面定义了一个Thread,持续监控容器的变化(word个数增加或者新增word)。

4,定义一个Topology,提交作业。

[Java] 纯文本查看 复制代码
01public class WordCountTopo {
02    public static void main(String[] args) {
03        if (args.length != 2) {
04            System.err.println(\"Usage: inputPaht timeOffset\");
05            System.err.println(\"such as : java -jar WordCount.jar D://input/ 2\");
06            System.exit(2);
07        }
08        TopologyBuilder builder = new TopologyBuilder();
09        builder.setSpout(\"word-reader\", new WordReader());
10        builder.setBolt(\"word-spilter\", new WordSpliter()).shuffleGrouping(\"word-reader\");
11        builder.setBolt(\"word-counter\", new WordCounter()).shuffleGrouping(\"word-spilter\");
12        String inputPaht = args[0];
13        String timeOffset = args[1];
14        Config conf = new Config();
15        conf.put(\"INPUT_PATH\", inputPaht);
16        conf.put(\"TIME_OFFSET\", timeOffset);
17        conf.setDebug(false);
18        LocalCluster cluster = new LocalCluster();
19        cluster.submitTopology(\"WordCount\", conf, builder.createTopology());
20    }
21}



5,代码完成后,导出jar(导出时不要指定Main class),然后上传至storm集群,通过命令./storm jar com.x.x.WordCountTopo /data/tianzhen/input 2来提交作业。

Topo启动,spout、bolt执行过程:

 


Thread监控的统计结果:


 


源文件处理之后被重命名为*.bak。

和Hadoop不同,在任务执行完之后,Topo不会停止,spout会一直监控数据源,不停地往bolt发射数据。
所以现在如果源数据发生变化,应该能够立马体现出来。我往path下再放一个文本文件,结果:


 


可见,结果立刻更新了,storm的实时性就体现在这里。
0 0