storm的编程模型
如图:
dataSource:数据源,我们需要处理的数据的来源,可以在任意的地方。
spout:接受外部数据源的组件。连接我们的数据源,从数据源那里获取数据,然后往下游发送,主要功能就是获取数据
bolt:数据处理逻辑单元,处理数据,按照你需要的逻辑进行加工处理,然后将数据转换成你想要的
tuple:我们的数据都是封装在tuple里面,各个组件之间的数据的传递都是通过tuple来的
JAVA api
1、导入jar包
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.storm/storm-core -->
<dependency>
<groupId>org.apache.storm</groupId>
<artifactId>storm-core</artifactId>
<version>1.1.1</version>
<!-- provided 表示我们的jar包引用范围,开发的时候需要他,打包的时候不需要 -->
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
2、开发我们的spout
public class WordsSpout extends BaseRichSpout{
private SpoutOutputCollector collector;
//定义一个数组,存储一些数据,然后随机的获取一下数据当中的数据,发送出去
private String[] lines ;
private Random random ;
/**
* 初始化的方法,一些准备工作都在这里做进行初始化,只在启动的时候,调用一次
*/
@Override
public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
this.collector = collector;
lines = new String[]{"hello world","hadoop storm","sqoop azkaban"};
random = new Random();
}
/**
* 这个方法会一直不断地运行源源不断的获取数据,往下游发送
*/
@Override
public void nextTuple() {
int i = random.nextInt(lines.length);
String line = lines[i];
//通过collector调用emit方法,将我们的数据往下游发送
collector.emit(new Values(line));
}
/**
* 为我们往下游发送的数据申明一个变量值,下游可以通过这个变量值获取我们上游发送来的数据
*/
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//调用declare来申明我们所有发出去的字符串的一个变量,下游可以通过这个变量进行取值
declarer.declare(new Fields("lines"));
}
}
3、开发我们的splitBolt
public class SplitBolt extends BaseBasicBolt{
/**
* execute方法会反复的执行
*/
@Override
public void execute(Tuple input, BasicOutputCollector collector) {
//获取上游发送的数据
String lines = input.getStringByField("lines");
//切分上游的数据,然后准备往下游发送
String[] split = lines.split(" ");
for (String word : split) {
// word 1
//往下游发送了两个字段,一个是word 一个是1
collector.emit(new Values(word,1));
}
}
/**
* 给下游发送的数据申明一个变量,下游可以通过这个变量获取数据
*/
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word","nums"));
}
}
4、开发我们的countbolt
public class CountBolt extends BaseBasicBolt {
//加上static关键字可以解决我们并发的问题
private static Map<String,Integer> map = new HashMap<String,Integer>();
/**
* 初始化调用,一些准备工作都放在这里执行
*/
@Override
public void prepare(Map stormConf, TopologyContext context) {
// map = new HashMap<String,Integer>();
}
@Override
public void execute(Tuple input, BasicOutputCollector collector) {
String word = input.getStringByField("word");
Integer nums = input.getIntegerByField("nums");
/**
* 使用一个map将我们的数据存储起来,哪个单词出现了多少次
*/
if(map.containsKey(word)){
map.put(word,map.get(word)+nums);
}else{
map.put(word,nums);
}
System.out.println(map.toString());
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
}
}
5、开发main方法
public class WordCountMain {
/**
* 主程序的入口,将我们的spout与bolt组织起来
*/
public static void main(String[] args) throws InvalidTopologyException, AuthorizationException, AlreadyAliveException {
//通过TopologyBuilder来组装我们的spout与bolt
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("wordSpout",new WordsSpout(),6);
builder.setBolt("splitBolt",new SplitBolt(),6).globalGrouping("wordSpout");
builder.setBolt("countBolt",new CountBolt(),8).globalGrouping("splitBolt");
/**
* storm程序的运行有两种方式,一种是集群方式运行,一种是本地模式运行
*/
Config config = new Config();
config.setNumWorkers(3);
//传递了参数进来了,我认为是集群模式运行
if(null != args && args.length > 0){
//关闭程序的调试模式
config.setDebug(false);
StormTopology topology = builder.createTopology();
StormSubmitter.submitTopology(args[0],config,topology);
}else{
//本地运行模式
LocalCluster cluster = new LocalCluster();
cluster.submitTopology("localStorm",config,builder.createTopology());
}
}
}