storm解析及wordcount简单实例
来源:互联网 发布:linux device is busy 编辑:程序博客网 时间:2024/06/06 16:44
storm:分布式实时流计算框架
storm分为单机模式和集群模式。
在集群模式下:
Storm由一个主节点和多个工作节点组成。主节点运行了一个名为“Nimbus”的守护进程,用于分配代码、布置任务及故障检测。每个工作节点都运行了一个名为“Supervisor”的守护进程,用于监听工作,开始并终止工作进程。Nimbus和Supervisor都能快速失败,而且是无状态的,这样一来它们就变得十分健壮,两者的协调工作是由Apache ZooKeeper来完成的。如图:
这也就意味着你可以用kill -9来杀死nimbus和supervisor进程, 然后再重启它们, 它们可以继续工作
更重要的是, nimbus和supervisor的fail或restart不会影响worker的工作, 不象Hadoop, Job tracker的fail会导致job失败
Storm是以Stream流为核心的,可定义为无限的tuple序列。
什么是tuple?
命名的value序列, 可以理解成Key/value序列, 每个value可以是任何类型, 动态类型不需要事先声明。
Tuple在传输中需要序列化和反序列化, storm集成了普通类型的序列化模块, 用户可以自定义特殊类型的序列化逻辑 。
Spouts, 流的源头 : 在一个topology中产生源数据流的组件。
Spout是Storm里面特有的名词, Stream的源头. 通常是从外部数据源读取tuples, 并emit到topology.
—-Spout可以同时emit多个tuple stream, 通过OutputFieldsDeclarer中的declareStream method来定义
—-Spout需要实现IRichSpout接口, 其中最重要的方法是nextTuple, 通常情况下,spout会从外部数据源中读取数据,然后转换为topology内部的源数据。Spout是一个主动的角色,其接口中有个nextTuple()函数,storm框架会不停地调用此函数,用户只要在其中生成源数据即可。
例:
@Override //接收外部数据转换为topology内部的源数据处理 public void nextTuple() { String[] sentests = new String[]{"jahdkj djhdk dsjdl", "jdkasjfl jfhk dhaku","dbhajdh frhy3ui hfkhf", "hekdh ekfhak dkuhwk","ruyw flk ejr"}; int randIndex = (int) (Math.random() * sentests.length); String sentence = sentests[randIndex]; collector.emit(new Values(sentence)); //写出去的数据 }
Bolts, 流的处理节点 :在一个topology中接受数据然后执行处理的组件。
对于Bolt, 用户可以定义任意的处理逻辑, 最重要的方法是execute, 输入为tuple, 输出为emit 0或多个tuples到OutputCollector.
Bolt支持多个输入流和emit多个输出流, 输出流和spout一样, 通过OutputFieldsDeclarer中的declareStream method来定义; 对于输入流, 如果想subscribe上层节点的多个输出streaming, 需要显式的通过stream_id去订阅, 如果不明确指定stream_id, 默认会订阅default stream.
也就是说Bolt可执行过滤、函数操作、合并、写数据库等操作。Bolt是一个被动的角色其接口中有一个execute(Tuple input)函数,在接受到消息后会调用此函数,用户可以在其中执行自己想要的操作。例:
public class WordCountBolt extends BaseRichBolt { private static final long serialVersionUID = -3262356130322390839L; private OutputCollector collector; @SuppressWarnings("rawtypes") @Override public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { this.collector = collector; } @Override public void execute(Tuple input) { String sentence = input.getStringByField("sentence"); String[] words = sentence.split("\\s+"); //以空格为单位 for (String word : words) { collector.emit(new Values(word, 1)); } } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("word", "num")); }
整个可以看作下图:
Topologies, 拓扑
可以理解为类似MapReduce job
区别在于, MR job执行完就结束, 而Topology会一直存在. 因为MR流动的是代码, 而Storm流动的数据.
所以Storm不可能替代MR, 因为对于海量数据, 数据的流动是不合理的
另一个区别, Topology对工作流有更好的支持, 而MR job往往只能完成一个map/reduce的过程, 而对于复杂的操作, 需要多个MR job才能完成.
而Topology的定义更加灵活, 可以简单的使用一个topology支持比较复杂的工作流场景。
这里具一个例子:
//创建一个topology拓扑结构的对象TopologyBuilder builder = new TopologyBuilder();//数据来源 设置topology中产生源数据流的组件对象builder.setSpout("spout", new WordCountSpout(), 3); //数据拆分 设置topology中接收数据然后执行处理组件对象builder.setBolt("split", new WordCountBolt(),3).shuffleGrouping("spout"); //数据合并 设置topology中接收数据然后执行处理组件对象builder.setBolt("count", new WordCountCount(), 3).fieldsGrouping("split", new Fields("word"));
Topology有一个spout, 两个Bolt. setSpout和setBolt的参数都是一样, 分别为id(在Topology中的唯一标识); 处理逻辑(对于Spout就是数据产生function); 并发线程数(task数) 。
比较特别的是, setBolt方法会返回一个InputDeclarer对象, 并且该对象是用来定义Bolt输入的, 比如上面.shuffleGrouping(“spout”), 用spout(spout)的输出流作为输入。
这里我们可以看到这里有一个Streaming grouping:即消息的paitition方法
Stream grouping 定义了一个流在Bolt任务间该被 如何切分。这里有Storm提供的6个Stream Grouping类型。
(1)随机分组(Shuffle grouping):随机分发tuple到Bolt的任务,保证每个任务获得相等数量的tuple.
(2)字段分组(Fields grouping):根据指定字段分割数据流,并分组。例如:根据 user-id 字段,相同 user-id的元祖总是分发到同一个任务,不同user-id的元祖可能分发到不同的任务。
(3)全部分组(All grouping):tuple被复制到bolt的所有任务。这种类型需要谨慎使用。
(4)全局分组(global grouping):全部流都分配到bolt的同一个任务。明确地说,是分配给ID最小的那个task。
(5)无分组(None grouping):你不需要关心流是如何分组。目前,无分组等效于随机分组。但最终,Storm将把无分组的Bolts放到Bolts或Spouts订阅他们的同一线程去执行(如果可能)。
(6)直接分组(Direct grouping):这是一个特别的分组类型。元祖生产者决定tuple由哪个元祖处理者任务接收。
于是我们可以总结Storm工作流程
第一步:客户端提交拓扑到Nimbus。
第二步:Nimbus针对该拓扑建立本地的目录根据topology的配置计算task,分配task,在zookeeper上建立assignments节点存储task和supervisor机器节点中woker的对应关系。
第三步:在zookeeper上创建taskbeats节点来监控task的心跳,启动topology。
第四步:Supervisor去zookeeper上获取分配的tasks,启动多个woker进行,每个woker生成task,一个task一个线程;根据topology信息初始化建立task之间的连接;Task和Task之间是通过ZeroMQ管理的;后整个拓扑运行起来。
Storm的术语包括Stream、Spout、Bolt、Task、Worker、Stream Grouping和Topology。
Stream是被处理的数据。
Sprout是数据源。
Bolt处理数据。
Task是运行于Spout或Bolt中的线程。
Worker是运行这些线程的进程。
Stream Grouping规定了Bolt接收什么东西作为输入数据。 Topology是由Stream Grouping连接起来的Spout和Bolt节点网络。
WordCount实例:
package com.yc.hadoop.storm;import java.util.Arrays;import org.apache.storm.Config;import org.apache.storm.LocalCluster;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 WordCountStorm { public static void main(String[] args) throws AlreadyAliveException, InvalidTopologyException, AuthorizationException { //创建一个topology拓扑结构的对象 TopologyBuilder builder = new TopologyBuilder(); //数据来源 设置topology中产生源数据流的组件对象 builder.setSpout("spout", new WordCountSpout(), 3); //数据拆分 设置topology中接收数据然后执行处理组件对象 builder.setBolt("split", new WordCountBolt(), 3).shuffleGrouping("spout"); //数据合并 设置topology中接收数据然后执行处理组件对象 builder.setBolt("count", new WordCountCount(), 3).fieldsGrouping("split", new Fields("word")); Config conf = new Config(); //storm配置 //conf.setDebug(true); /*conf.put(Config.NIMBUS_SEEDS, Arrays.asList("master")); conf.put(Config.STORM_ZOOKEEPER_SERVERS, Arrays.asList("slave01", "slave02", "slave03")); */ //本地模式 LocalCluster lc = new LocalCluster(); lc.submitTopology("wordcount", conf, builder.createTopology());//提交作业 //远程集群模式 /*System.setProperty("storm.jar", "E:\\workspaces\\hadoop42-012-storm01\\target\\hadoop42-012-storm01-0.0.1-SNAPSHOT.jar"); StormSubmitter.submitTopology("wordcount4", conf, builder.createTopology());*/ }}import java.util.Map;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;public class WordCountSpout extends BaseRichSpout { /** * */ private static final long serialVersionUID = -2882826287312192427L; private SpoutOutputCollector collector; @SuppressWarnings("rawtypes") @Override //只会调用一次 public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) { this.collector=collector; } @Override //接收外部数据转换为topology内部的源数据处理 public void nextTuple() { String[] sentests = new String[]{"jahdkj djhdk dsjdl", "jdkasjfl jfhk dhaku","dbhajdh frhy3ui hfkhf", "hekdh ekfhak dkuhwk","ruyw flk ejr"}; int randIndex = (int) (Math.random() * sentests.length); String sentence = sentests[randIndex]; collector.emit(new Values(sentence)); //写出去的数据 } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("sentence")); //写出数据的key } }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;public class WordCountBolt extends BaseRichBolt { private static final long serialVersionUID = -3262356130322390839L; private OutputCollector collector; @SuppressWarnings("rawtypes") @Override public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) { this.collector = collector; } @Override public void execute(Tuple input) { String sentence = input.getStringByField("sentence"); String[] words = sentence.split("\\s+"); //以空格为单位 for (String word : words) { collector.emit(new Values(word, 1)); } } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare(new Fields("word", "num")); }}import java.util.HashMap;import java.util.Map;import org.apache.storm.task.TopologyContext;import org.apache.storm.topology.BasicOutputCollector;import org.apache.storm.topology.OutputFieldsDeclarer;import org.apache.storm.topology.base.BaseBasicBolt;import org.apache.storm.tuple.Tuple;public class WordCountCount extends BaseBasicBolt{ private static final long serialVersionUID = -4479736188433530257L; Map<String,Integer> wordCountMap; @SuppressWarnings("rawtypes") @Override public void prepare(Map stormConf, TopologyContext context) { super.prepare(stormConf, context); wordCountMap = new HashMap<String,Integer>(); } @Override public void execute(Tuple input, BasicOutputCollector collector) { String word = input.getStringByField("word"); int num = input.getIntegerByField("num"); if(!wordCountMap.containsKey(word)){ wordCountMap.put(word, num); }else{ num += wordCountMap.get(word); wordCountMap.put(word, num); } System.out.println(word + "出现的个数是:" + num); } @Override public void declareOutputFields(OutputFieldsDeclarer declarer) { }}
运行结果:
(注:如果采用集群模式可利用maven打包,在相应的运行的机器下查看work日志)
总结:我们可以把storm过程看做一个mapreduce,但是真正的MR运行结束后job就终止了,但是storm会一直运行下去,这体现了他的实时性。根据数据的改变会有相应的反应。
- storm解析及wordcount简单实例
- storm wordcount实例
- storm入门简介及WordCount代码解析(一)
- hadoop简单实例-WordCount
- Storm-9.1.0 WordCount jar 实例
- hadoop MapReduce实例解析(WordCount)
- storm 实战及实例讲解
- Hadoop示例程序WordCount详解及实例
- Hadoop示例程序WordCount详解及实例
- Hadoop示例程序WordCount详解及实例 .
- Hadoop示例程序WordCount详解及实例
- Hadoop示例程序WordCount详解及实例
- Hadoop示例程序WordCount详解及实例
- Hadoop示例程序WordCount详解及实例
- Hadoop示例程序WordCount详解及实例
- Hadoop示例程序WordCount详解及实例
- 运行hadoop中的实例wordcount及问题
- storm运行wordcount例子
- Android-JNI开发-环境配置(eclipse)
- java.security.AccessControlException: access denied (java.lang.RuntimePermission accessDeclaredMembe
- 0814Link
- GitHub 排名前 100 的安卓、iOS项目简介
- POJ 1088 滑雪
- storm解析及wordcount简单实例
- Java多线程
- git命令合并分支代码
- 关于数据在内存中存储的运算
- js显示实时时间[样式:年/月/日 上下午 时:分:秒 星期几](仅需一行代码)
- Linux下Anaconda的安装及系统自带python和Anaconda切换
- Python-OpenCV图像算术运算
- ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot exec
- 关于iOS 静态库.a 合并通用库