通过这个入门指南,你将学会如何创建storm拓扑(topology)和部署拓扑到storm集群。主要使用Java语言,少许例子使用Python说明Storm的多语言特性。
- storm jar all-my-code.jar backtype.storm.MyTopology arg1 arg2
arg1
和
arg2
参数的
backtype.storm.MyTopology
类,这个类的主要功能是定义拓扑,并提交拓扑到nimbus,
storm jar
负责连接nimbus和上传jar包。
Storm核心的抽象概念是“流”。流是一个×××的连续元组(tuple)。Storm提供用分布式和可靠的方式转换一个流到一个新流的原语(primitive)。例如,你可以转换一个tweets流到一个trending topics流。
spout是流的源头。例如,一个spout可能从Kestrel队列读取元组并以流的方式发射它们,一个spout可能连接Twitter API并发射一个tweets流。
- public class DoubleAndTripleBolt implements IRichBolt {
- private OutputCollectorBase _collector;
- @Override
- public void prepare(Map conf, TopologyContext context, OutputCollectorBase collector) {
- _collector = collector;
- }
- @Override
- public void execute(Tuple input) {
- int val = input.getInteger(0);
- _collector.emit(input, new Values(val*2, val*3));
- _collector.ack(input);
- }
- @Override
- public void cleanup() {
- }
- @Override
- public void declareOutputFields(OutputFieldsDeclarer declarer) {
- declarer.declare(new Fields("double", "triple"));
- }
- }
declareOutputFields
为这个组件声明输出字段
["double", "triple"]
,剩下的bolt将在后面的章节解释。
ExclamationTopology
的 定义,代码如下所示:
- TopologyBuilder builder = new TopologyBuilder();
- builder.setSpout("words", new TestWordSpout(), 10);
- builder.setBolt("exclaim1", new ExclamationBolt(), 3)
- .shuffleGrouping("words");
- builder.setBolt("exclaim2", new ExclamationBolt(), 2)
- .shuffleGrouping("exclaim1");
setSpout
和方法
setBolt
来定义节点。这些方法需要三个入参:用户指定节点ID,业务逻辑处理对象,节点的并行数。这个例子,我们给spout指定的id是“words”,给bolt指定的id分别是“exclaim1”和“exclaim2”。
setBolt
方法返回一个
InputDeclarer对象,此对象用于定义该bolt的输入流。组件“
excliam1”声明它要
读取所有由组件“
words”发射出来的元组,并使用随机分组策略(
shuffle grouping)分发到各线程,组件
“excliam2”声明它要读取所有由组件“excliam1”发射出来的元组,并使用随机分组策略分发到各线程。“shuffle grouping”是指元组从输入任务被随机的分发到bolt任务。组件间的数据分发策略还有很多,我将用少量章节解释它们。
- builder.setBolt("exclaim2", new ExclamationBolt(), 5)
- .shuffleGrouping("words")
- .shuffleGrouping("exclaim1");
让我们深入探究拓扑中spout和bolt的实现。Spout负责发送新消息到拓扑。拓扑中的TestWordSpout每隔0.1秒就从["nathan", "mike", "jackson", "golda", "bertels"]列表中随机选择一个单词,并把该单词作为一元元组发射出去。TestWordSpout类的nextTuple()方法的实现如下所示:
- public void nextTuple() {
- Utils.sleep(100);
- final String[] words = new String[] {"nathan", "mike", "jackson", "golda", "bertels"};
- final Random rand = new Random();
- final String word = words[rand.nextInt(words.length)];
- _collector.emit(new Values(word));
- }
- public static class ExclamationBolt implements IRichBolt {
- OutputCollector _collector;
- public void prepare(Map conf, TopologyContext context, OutputCollector collector) {
- _collector = collector;
- }
- public void execute(Tuple tuple) {
- _collector.emit(tuple, new Values(tuple.getString(0) + "!!!"));
- _collector.ack(tuple);
- }
- public void cleanup() {
- }
- public void declareOutputFields(OutputFieldsDeclarer declarer) {
- declarer.declare(new Fields("word"));
- }
- }
prepare方法提供给bolt一个Outputcollector用来发射tuple。Bolt可以在任意时候发射tuple – 可以在prepare、execute、cleanup方法中发射, 或者甚至在另一个线程中异步发射。prepare方法只是简单地把OutputCollector作为一个类成员变量保存,以供execute方法以后使用。
- Config conf = new Config();
- conf.setDebug(true);
- conf.setNumWorkers(2);
- LocalCluster cluster = new LocalCluster();
- cluster.submitTopology("test", conf, builder.createTopology());
- Utils.sleep(10000);
- cluster.killTopology("test");
- cluster.shutdown();
通过告诉storm在任务集合之间如何发送元组,“stream grouping”回答了这个问题。在深入研究不同种类的流分组之前,我们先看一下storm-starter项目中的另一个拓扑,WordCountTopology从spout读取句子,WordCountBolt输出单词的出现次数,代码如下所示:
- TopologyBuilder builder = new TopologyBuilder();
- builder.setSpout("sentences", new RandomSentenceSpout(), 5);
- builder.setBolt("split", new SplitSentence(), 8)
- .shuffleGrouping("sentences");
- builder.setBolt("count", new WordCount(), 12)
- .fieldsGrouping("split", new Fields("word"));
一个更有趣的流分组种类是“fields grouping“。SplitSentence和WordCount之间使用的是字段分组(fields grouping)。对于WordCount功能,同一单词流向同一任务是关键。否则,如果多个任务都能获取同一单词,那么由于他们获得的信息不完整,他们将发射不正确的计数结果。字段分组让我们可以按字段的子集对流分组,这使得子段子集的相等值会流向同一任务。由于WordCount使用基于字段“word”的字段分组方式订阅SplitSentence的输出流,所以相同的单词总是流向同一任务,最终bolt输出正确的结果。
- public static class SplitSentence extends ShellBolt implements IRichBolt {
- public SplitSentence() {
- super("python", "splitsentence.py");
- }
- public void declareOutputFields(OutputFieldsDeclarer declarer) {
- declarer.declare(new Fields("word"));
- }
- }
- import storm
- class SplitSentenceBolt(storm.BasicBolt):
- def process(self, tup):
- words = tup.values[0].split(" ")
- for word in words:
- storm.emit([word])
- SplitSentenceBolt().run()
转载于:https://blog.51cto.com/chenlx/739531