利用Storm实现word count例子与Storm启动流程分析
main函数
public static void main(String[] args) throws AlreadyAliveException, InvalidTopologyException, AuthorizationException, InterruptedException {
//准备一个TopologyBuilder
TopologyBuilder builder=new TopologyBuilder();
builder.setSpout("spout",new RandomSentenceSpout(),Integer.valueOf(5));
builder.setBolt("split",new SplitSentenceBolt(),Integer.valueOf(8)).shuffleGrouping("spout");
builder.setBolt("count",new WordCountBolt(),Integer.valueOf(12)).fieldsGrouping("split",new Fields("word"));
//创建一个configuration,用来制定当前topology需要的数量
Config conf=new Config();
conf.setNumWorkers(2);
//提交任务
//storm有两种运行模式:
//集群模式——在集群上运行
//本地模式——当前编辑器中运行
StormSubmitter.submitTopology("word-count",conf,builder.createTopology());
}
WordCountBolt(),Integer.valueOf(12)).fieldsGrouping(“split”,new Fields(“word”)); 申明分组是按照“word”字段,而用fieldGrouping进行分组,是因为要让同一个字段同时到一个task中,才能够统计得到完成的结果。
Spout
public class RandomSentenceSpout extends BaseRichSpout{
private static final long serialVersionUID=56343879980567L;
SpoutOutputCollector _collector;//用来收集Spout输出的tuple
Random _rand;
//该方法调用一次,主要由storm框架传入SpoutOutputCollector
@Override
public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
this._collector=collector;
this._rand=new Random();
}
//storm框架在while(true)调用nextTuple方法
@Override
public void nextTuple() {
Utils.sleep(100L);
String[] sentences= {"keep moving","an apple a day keeps the docotr away","just do it"};
String sentence=sentences[this._rand.nextInt(sentences.length)];
this._collector.emit(new Values(sentence));
}
//消息源可以发射多条消息流stream,多条消息流可以理解为多种类型的数据
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word"));
}
Split_Bolt
public class SplitSentenceBolt extends BaseRichBolt{
private static final long serialVersionUID = 1L;
OutputCollector collector;
//该方法只会被调用一次,用来初始化
public void prepare(Map stormConf,TopologyContext context,OutputCollector collector) {
this.collector=collector;
}
/*
* 接受的参数是RandomSentenceSpout发出的句子,即input的内容是句子execute方法,将句子切割形成的单词发出
*/
public void execute(Tuple input) {
String sentence=input.getString(0);
String[] words=sentence.split(" ");
for(String word:words) {
collector.emit(new Values(word,1));
}
}
//消息源可以发射多条消息流stream,多条消息流可以理解为多种类型的数据
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word","num"));//这里申明的两个字段的index分别对应着collector.emit(new Values(word,1)); 中的word和1
}
Count_Bolt
public class WordCountBolt extends BaseRichBolt{
private static final long serialVersionUID=765657677676642L;
private Map<String,Integer> counters=new HashMap<String,Integer>();//用来保存最后计算的结果key=单词,value=单词个数
OutputCollector collector;
//该方法只会被调用一次,用来初始化
public void prepare(Map stormConf,TopologyContext context,OutputCollector collector) {
this.collector=collector;
}
/*
* 将collector中的元素存放在成员变量counters(Map)中,
* 如果counters(Map)中已经存在该元素,getValue并对Value进行累加操作。
*/
public void execute(Tuple input) {
String str=input.getString(0);
Integer num=input.getInteger(1);
input.getFields();
if(!counters.containsKey(str)) {
counters.put(str,num);
}else {
Integer c=counters.get(str)+num;
counters.put(str,c);
}
System.out.println(str+" "+counters.get(str));
}
public void declareOutputFields(OutputFieldsDeclarer declarer) {
//不输出
}
Storm启动流程分析
1、客户端运行storm nimbus时,会调用storm的python脚本,该脚本中为每个命令编写一个方法,每个方法都可以生成一条相应的java命令。命令格式如下:
java -server xxx.ClassName -args
nimbus---->Running:/export/servers/jdk/bin/java -server backtype.storm.daemon.nimbus
supervisor--->Running:/export/servers/jdk/bin/java -server backtype.storm.daemon.supervisor
2、nimbus启动之后,接受客户端提交任务,命令格式如下:
java -server xxx.ClassName -args
storm jar xxx.jar xx驱动类 参数
Running:/export/servers/jdk/bin/java -client -D storm.jar=/export/servers/storm/examples/storm-starters-topologies-0.9.6.jar
storm.starter.WordCountTopology wordcount-28
该命令会执行storm-starters-topologies-0.9.6.jar中的storm-starter-topologies-0.9.6.jar的main方法,main方法中会执行以下代码:
StormSubmitter.submitTopology("word-count",conf,builder.createTopology());
builder.createTopology()会将Spout对象和Bolt对象序列化。然后将用户的jar上传到nimbus物理节点的/export/data/workdir/nimbus/inbox目录下,并且按规则改名,即:添加一个UUID字符串。在nimbus物理节点的/export/data/storm/workdir/nimbus/stormdist目录下,有当前正在运行的topology的jar包和配置文件,序列化对象文件。
3、接受到任务之后,会将任务进行分配,分配会产生一个assignment对象,该对象会保存到ZooKeeper中,目录是/storm/assignments,该目录只保存正在运行的topology任务。
4、supervisor通过watch机制,感知到nimbus在ZooKeeper上的任务分配信息,从ZooKeeper上拉取任务信息,分辨出属于自己的任务。
ResourceWorkerSlot[hostname=192.168.1.106,nameSize=0,cpu=0,tasks=[1,2,3,4,5,6,7,8],jvn=<null>,nodeId=61ce10a7-4c47-9fb3-c21f43a331ba,port=6900]
5、supervisor根据自己的任务信息,启动自己的worker,并分配一个端口。
'/export/servers/jdk/bin/java''-server''-Xmx768m' export/data/storm/workdir/supervisor/stormdist/wordcount1-3-1461483066/stormjar.jar
'backtype.storm.daemon.worker''wordcount1-3-1461683066''a69bb8fc-e08e-b51f-e539b066f90b''6701'
'9fac2805-7d2b-4e40-aabc-1c85c9856d64'
6、worker启动之后,连接ZooKeeper,拉取任务
ResourceWorkerSlot[hostname=192.168.1.106,nameSize=0,cpu=0,tasks=[1,2,3,4,5,6,7,8],jvn=<null>,nodeId=61ce10a7-4c47-9fb3-c21f43a331ba,port=6900]
假设任务信息:
1---->Spout----type:Spout
2---->Bolt----type:Bolt
3---->Bolt----type:Bolt
worker通过反序列化,得到之前被序列化的Spout和Bolt对象。
7、worker根据任务类型,分别执行Spout任务或者Bolt任务。Spout的声明周期为:open->nextTuple->declareOutputFields。Bolt的生命周期为:prepare->execute->declareOutputFields。
本文详细解析了使用Storm实现Word Count的例子,并深入分析了Storm的启动流程,包括客户端提交任务、Nimbus调度、Supervisor执行、Worker运行Spout和Bolt等关键步骤。
721

被折叠的 条评论
为什么被折叠?



