1.拓扑程序中的基本概念
- Stream:流数据,持续不断产生的数据流。
- Spout:数据源,类似于kafka Streaming中的
Source
组件,负责从外部的存储系统获取一条条记录,并且会将这些记录封装为一个Tuple(元组)
对象。Spout将封装好的Tuple发送给下游的Bolt组件,进行加工处理。 常用Spout类型IRichSpout(最多一次处理),IBaseSpout(最少一次处理)。 - Bolt: 处理器,对接口到的Tuple进行加工处理(产生新的Tuple),继续将新产生的Tuple发射交由下游的Bolt进行处理,常用Bolt类型:IRichBolt,IBaseBolt。
- Tuple: Storm流数据中的一条记录,Tuple本质是一个可以存放任何类型的List集合Tuple=List(1,true,“zs”), Tuple只能赋值一次(read only)。
2.应用开发
整体结构分析
2.1 引入相关依赖
<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-core</artifactId>
<version>2.0.0</version>
<!-- 依赖的作用范围 scope依靠外部的容器或者应用提供 -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-client</artifactId>
<version>2.0.0</version>
<scope>provided</scope>
</dependency>
2.2 编写Spout
package quickstart;
import org.apache.storm.spout.SpoutOutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichSpout;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Values;
import org.apache.storm.utils.Utils;
import java.util.Map;
import java.util.Random;
/**
* 负责生产一行行的英文短语
*/
public class LinesSpout extends BaseRichSpout {
// 模拟数据 英文短语
private String[] lines = null;
private SpoutOutputCollector collector = null;
/**
* 初始化方法
* @param conf
* @param context
* @param collector
*/
public void open(Map<String, Object> conf, TopologyContext context, SpoutOutputCollector collector) {
lines = new String[]{"Hello Storm","Hello Kafka","Hello Spark"};
this.collector = collector;
}
/**
* 创建元组方法
*/
public void nextTuple() {
// 每隔5秒 新产生一个tuple 并且将它发送给下游的处理器
Utils.sleep(5000);
// 随机数:0 - 2
int num = new Random().nextInt(lines.length);
// 随机获取一行英文短语
String line = lines[num];
// 将随机产生的数据封装为1个Tuple元组
// 将封装好的tuple发送给下游的处理器bolt
collector.emit(new Values(line));
}
/**
* 说明输出元组信息
* @param declarer
*/
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("line"));
}
}
2.3 编写LineSplitBolt
package quickstart;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;
import java.util.Map;
/**
* 流数据的处理器
* line ---> word
*/
public class LineSplitBolt extends BaseRichBolt {
private OutputCollector collector = null;
/**
* 准备方法
* @param topoConf
* @param context
* @param collector
*/
public void prepare(Map<String, Object> topoConf, TopologyContext context, OutputCollector collector) {
this.collector = collector;
}
/**
* 执行方法
* @param input
*/
public void execute(Tuple input) {
String line = input.getStringByField("line");
String[] words = line.split(" ");
for (String word : words) {
// 新创建的元组 交给下游的处理器 统计和计算
collector.emit(new Values(word));
}
}
/**
*
* @param declarer
*/
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word"));
}
}
2.4 编写WordCountBolt
package quickstart;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Tuple;
import java.util.HashMap;
import java.util.Map;
/**
* 统计单词出现的次数
* word --> word count
*/
public class WordCountBolt extends BaseRichBolt {
private HashMap<String, Long> wordCount = null;
public void prepare(Map<String, Object> topoConf, TopologyContext context, OutputCollector collector) {
wordCount = new HashMap<String, Long>();
}
/**
* 单词计数案例:
* wordcountBolt最终的处理器
* @param input
*/
public void execute(Tuple input) {
String word = input.getStringByField("word");
// 累计单词出现次数(计算状态)
Long num = wordCount.getOrDefault(word, 0L);
num++;
wordCount.put(word, num);
System.out.println(word + "\t" + num);
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
}
}
2.5 初始化类WordCountApplication
package quickstart;
import org.apache.storm.Config;
import org.apache.storm.StormSubmitter;
import org.apache.storm.generated.AlreadyAliveException;
import org.apache.storm.generated.AuthorizationException;
import org.apache.storm.generated.InvalidTopologyException;
import org.apache.storm.topology.TopologyBuilder;
import org.apache.storm.tuple.Fields;
/**
* 编制拓扑应用程序
*/
public class WordCountApplication {
public static void main(String[] args) throws InvalidTopologyException, AuthorizationException, AlreadyAliveException {
//1. 构建topology的DAG(有向无环图)
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("s1",new LinesSpout());
// 参数三:设置计算单元并行度 默认1
builder.setBolt("b1",new LineSplitBolt(),2)
// 将spout生产的元组tuple均等分配给b1组件的多个并行任务
.shuffleGrouping("s1");
builder.setBolt("b2",new WordCountBolt(),3)
// 将指定的field相同的数据 归为1组
.fieldsGrouping("b1",new Fields("word"));
//2. 将topology提交运行
// 参数1:拓扑任务名
Config config = new Config();
// 当前拓扑运行时 需要占用两个槽位(2个worker进程)
config.setNumWorkers(2);
config.setNumAckers(0); // storm可靠性处理相关
StormSubmitter.submitTopology("storm-wordcount",config,builder.createTopology());
}
}
2.6 提交任务
# 将storm的拓扑应用 打包,并上传到storm集群,通过以下命令提交任务
[root@node1 apache-storm-2.0.0]# storm jar /root/storm-demo-1.0-SNAPSHOT.jar quickstart.WordCountApplication
2.7 查看拓扑任务的状态
[root@node1 apache-storm-2.0.0]# storm list
2.8 Strom UI 的拓扑摘要详解
2.9 结束拓扑任务
主节点上进行操作
[root@node1 apache-storm-2.0.0]# storm kill storm-wordcount(拓扑任务的名字)