1 storm入门介绍
1.1 背景-实现实时计算系统需要解决那些问题
如果让我们自己设计一个实时计算系统,我们要解决哪些问题。
(1)低延迟:都说了是实时计算系统了,延迟是一定要低的。
(2)高性能:性能不高就是浪费机器,浪费机器是要受批评的哦。
(3)分布式:系统都是为应用场景而生的,如果你的应用场景、你的数据和计算单机就能搞定,那么不用考虑这些复杂的问题了。我们所说的是单机搞不定的情况。
(4)可扩展:伴随着业务的发展,我们的数据量、计算量可能会越来越大,所以希望这个系统是可扩展的。
(5)容错:这是分布式系统中通用问题。一个节点挂了不能影响我的应用。
(6)通信:设计的系统需要应用程序开发人员考虑各个处理组件的分布、消息的传递吗?如果是,发人员可能会用不好,也不会想去用。
(7)消息不丢失:用户发布的一个宝贝消息不能在实时处理的时候给丢了,对吧?
1.2离线计算是什么
![](http://i.imgur.com/fMtoxNK.png)
1.3流式计算是什么
流式计算:数据实时产生、数据实时传输、数据实时计算、实时展示
代表技术:Flume实时获取数据、Kafka/metaq实时数据存储、Storm/JStorm实时数据计算、Redis实时结果缓存、持久化存储(mysql)。
1.4离线计算和实时计算的区别
最大的区别:实时收集、实时计算、实时展示
离线计算,一次计算很多条数据
实时计算,数据被一条一条的计算
1.5storm的应用场景
Storm处理数据的方式是基于消息的流水线处理, 因此特别适合无状态计算,也就是计算单元的依赖的数据全部在接受的消息中可以找到, 并且最好一个数据流不依赖另外一个数据流。
因此,常常用于:
1)日志分析,从海量日志中分析出特定的数据,并将分析的结果存入外部存储器用来辅佐决策。
2)管道系统, 将一个数据从一个系统传输到另外一个系统, 比如将数据库同步到Hadoop
3)消息转化器, 将接受到的消息按照某种格式进行转化,存储到另外一个系统如消息中间件
4)统计分析器, 从日志或消息中,提炼出某个字段,然后做count或sum计算,最后将统计值存入外部存储器。中间处理过程可能更复杂。
2storm入门介绍
2.1storm与Hadoop对比性分析storm
1、storm是分布式实时数据计算框架,不负责数据的存储;
hadoop包含两个框架(hdfs、MapReduce)
2、storm数据的获取:
Hadoop MapReduce(TextInputFormat\Map\Reduce\TextOutputFormat)由TextInputFormat获取数据
storm 通过Spout去获取数据,数据可以在数据库中、文件中、redis中等在任何的存储地方。
注:只有数据实时产生,并实时获取才能让storm实时计算框架产生最大的效益。如果spout在一行一行的读取存储文件中的数据表示只是在实时的读取数据。
3、storm框架数据的实时处理:
Hadoop MapReduce通关Map\Reduce来计算数据
storm在Bolt中进行相关业务逻辑的计算,如果有多个阶段的业务逻辑需要计算,将Bolt进行串联,Bolt1--->Bolt2--->Bolt3--->Bolt4 ...
4、storm的编程模型
Spout--->Bolt1--->Bolt2--->Bolt3---Bolt4...
获取数据 计算 计算 计算
2.2需求分析(单词统计)
原始数据:i am lilei
i am hanmeimei
i am tom
结果数据:i 3
am 3
lilei 1
hanmeimei 1
tom 1
分析: 1、读取数据,用WordCountSpout组件读取数据
2、对句子进行分割,SplitBolt进行句子分割
3、对单词进行统计并将结果打印出来,CountAndPrintBolt统计单词个数并且打印
流程:WordCountSpout--->SplitBolt--->CountAndPrintBolt
代码:
WordCountSpout:
package com.storm.wordcount;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.lang.StringUtils;
import backtype.storm.spout.SpoutOutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseRichSpout;
import backtype.storm.tuple.Fields;
public class WordCountSpout extends BaseRichSpout {
private static final long serialVersionUID = -1311260628931046774L;
private SpoutOutputCollector collector;
private LineIterator lineIterator;
@SuppressWarnings("rawtypes")
@Override
// 初始化方法
public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
this.collector = collector;
InputStream in = WordCountSpout.class.getResourceAsStream("/test.dat");
if (in != null) {
try {
this.lineIterator = IOUtils.lineIterator(in, "utf-8");
} catch (IOException e) {
e.printStackTrace();
}
}
}
// Storm实时计算的特性就是对数据一条一条的处理
// while(true){
// this.nextTuple();
// }
@Override
public void nextTuple() {
// 每被调用一次就发生一条数据出去
if (lineIterator != null && lineIterator.hasNext()) {
String line = lineIterator.nextLine();
if (StringUtils.isNotEmpty(line)) {
List<Object> cachingValues = new ArrayList<Object>();
cachingValues.add(line);
collector.emit(cachingValues);
// cachingValues.clear();
}
}
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("line"));
}
}
SplitBolt:
package com.storm.wordcount;
import org.apache.commons.lang.StringUtils;
import backtype.storm.topology.BasicOutputCollector;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseBasicBolt;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;
public class SplitBolt extends BaseBasicBolt {
private static final long serialVersionUID = -3325717180080513451L;
@Override
public void execute(Tuple tuple, BasicOutputCollector collector) {
// 1、如何取数据
String pickLine = tuple.getStringByField("line");
if (StringUtils.isNotEmpty(pickLine)) {
// 2、进行分割
String[] splitStrs = pickLine.split(" ");
for (String str : splitStrs) {
// 3.发送数据
// values对象会帮助我们生成一个list
collector.emit(new Values(str, 1));
}
}
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word", "num"));
}
}
CountAndPrintBolt:
package com.storm.wordcount;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import backtype.storm.topology.BasicOutputCollector;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseBasicBolt;
import backtype.storm.tuple.Tuple;
public class CountAndPrintBolt extends BaseBasicBolt {
private static final long serialVersionUID = 3538707571695237510L;
private Map<String, Integer> word2Num = new HashMap<String, Integer>();
@Override
public void execute(Tuple tuple, BasicOutputCollector collector) {
String pickWord = tuple.getStringByField("word");
Integer pickNum = tuple.getIntegerByField("num");
if (StringUtils.isNotEmpty(pickWord) && pickNum != null) {
// 查看单词是否存在
Integer num = word2Num.get(pickWord);
if (num == null || num.intValue() == 0) {
word2Num.put(pickWord, pickNum);
} else {
word2Num.put(pickWord, pickNum.intValue() + num.intValue());
}
// 打印数据
System.out.println(word2Num);
}
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//nothing to do
}
}
WordCountTopologyDriver:
package com.storm.wordcount;
import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.StormSubmitter;
import backtype.storm.generated.AlreadyAliveException;
import backtype.storm.generated.InvalidTopologyException;
import backtype.storm.generated.StormTopology;
import backtype.storm.topology.TopologyBuilder;
public class WordCountTopologyDriver {
public static void main(String[] args) throws AlreadyAliveException, InvalidTopologyException {
// 1、准备任务信息
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("word_count_spout", new WordCountSpout());
builder.setBolt("split_bolt", new SplitBolt()).shuffleGrouping("word_count_spout");
builder.setBolt("count_and_print_bolt", new CountAndPrintBolt()).shuffleGrouping("split_bolt");
// 2、提交任务
Config stormConf = new Config();
StormTopology topology = builder.createTopology();
// 2.1提交本地模式
// LocalCluster localCluster = new LocalCluster();
// localCluster.submitTopology("wordcount", stormConf, topology);
//2.2集群模式
StormSubmitter.submitTopology("wordcount", stormConf, topology);
}
}