storm 开发系列一 第一个程序

这篇博客详细介绍了如何在本地环境中搭建并创建一个基本的Storm程序。通过使用Maven创建hello_storm项目,然后将App.java重命名为HelloTopology.java,并在此文件中编写包含spout和bolt的静态类,简化了Example中的TestWordSpout,移除了日志代码并调整了私有变量的命名约定。

本文将在本地开发环境创建一个storm程序,力求简单。

首先用mvn创建一个简单的工程hello_storm

mvn archetype:generate -DgroupId=org.csfreebird -DartifactId=hello_storm -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

编辑pom.xml,添加dependency

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.csfreebird</groupId>
  <artifactId>hello_storm</artifactId>
  <version>0.9.5</version>
  <packaging>jar</packaging>
  <name>hello_storm</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>org.apache.storm</groupId>
      <artifactId>storm-core</artifactId>
      <version>${project.version}</version>
      <!-- keep storm out of the jar-with-dependencies -->
      <scope>provided</scope>
    </dependency>
  </dependencies>
</project>

provided 表示storm-core的jar包只作为编译和测试时使用,在集群环境下运行时完全依赖集群环境的storm-core的jar包。


然后重命名App.java为HelloTopology.java文件,开始编码。模仿之前的Example, 这里将所有的spout/bolt类都作为静态类定义,就放在HelloTopology.java文件。

功能如下



编写HelloTopology.java代码,spout代码来自于TestWordSpout,去掉了log的代码,改变了_引导的成员变量命名方法

package org.csfreebird;

import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.StormSubmitter;
import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.testing.TestWordSpout;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.TopologyBuilder;
import backtype.storm.topology.base.BaseRichBolt;
import backtype.storm.topology.base.BaseRichSpout;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;
import backtype.storm.utils.Utils;
import backtype.storm.spout.SpoutOutputCollector;
import java.util.Map;
import java.util.TreeMap;
import java.util.Random;

public class HelloTopology {
    
    public static class HelloSpout extends BaseRichSpout {
	
	boolean isDistributed;
	SpoutOutputCollector collector;

	public HelloSpout() {
	    this(true);
	}

	public HelloSpout(boolean isDistributed) {
	    this.isDistributed = isDistributed;
	}
        
	public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
	    this.collector = collector;
	}
    
	public void close() {
	}
        
	public void nextTuple() {
	    Utils.sleep(100);
	    final String[] words = new String[] {"china", "usa", "japan", "russia", "england"};
	    final Random rand = new Random();
	    final String word = words[rand.nextInt(words.length)];
	    this.collector.emit(new Values(word));
	}
    
	public void ack(Object msgId) {
	}

	public void fail(Object msgId) {
	}
    
	public void declareOutputFields(OutputFieldsDeclarer declarer) {
	    declarer.declare(new Fields("word"));
	}

	@Override
	public Map<String, Object> getComponentConfiguration() {
	    if(!this.isDistributed) {
		Map<String, Object> ret = new TreeMap<String, Object>();
		ret.put(Config.TOPOLOGY_MAX_TASK_PARALLELISM, 1);
		return ret;
	    } else {
		return null;
	    }
	}
    }

    public static class HelloBolt extends BaseRichBolt {
	OutputCollector collector;

	@Override
	public void prepare(Map conf, TopologyContext context, OutputCollector collector) {
	    this.collector = collector;
	}

	@Override
	public void execute(Tuple tuple) {
	    this.collector.emit(tuple, new Values("hello," + tuple.getString(0)));
	    this.collector.ack(tuple);
	}

	@Override
	public void declareOutputFields(OutputFieldsDeclarer declarer) {
	    declarer.declare(new Fields("word"));
	}
    }
    
    public static void main(String[] args) throws Exception {
	
	TopologyBuilder builder = new TopologyBuilder();
	builder.setSpout("a", new HelloSpout(), 10);
	builder.setBolt("b", new HelloBolt(), 5).shuffleGrouping("a");

	Config conf = new Config();
	conf.setDebug(true);

	if (args != null && args.length > 0) {
	    conf.setNumWorkers(3);
	    StormSubmitter.submitTopologyWithProgressBar(args[0], conf, builder.createTopology());
	} else {
	    String test_id = "hello_test";
	    LocalCluster cluster = new LocalCluster();
	    cluster.submitTopology(test_id, conf, builder.createTopology());
	    Utils.sleep(10000);
	    cluster.killTopology(test_id);
	    cluster.shutdown();
	}
    }   
}

编译成功

mvn clean compile

为了能够在本地模式运行,需要在pom.xml中添加如下:

  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.2.1</version>
        <executions>
          <execution>
            <goals>
              <goal>exec</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <executable>java</executable>
          <includeProjectDependencies>true</includeProjectDependencies>
          <includePluginDependencies>false</includePluginDependencies>
          <classpathScope>compile</classpathScope>
          <mainClass>${storm.topology}</mainClass>
        </configuration>
      </plugin>
    </plugins>
  </build>

然后运行命令

mvn compile exec:java -Dstorm.topology=org.csfreebird.HelloTopology  




第一个Storm应用--数单词数量(个spout读取文本,第一个bolt用来标准化单词,第二个bolt为单词计数) Storm运行模式: 1.本地模式(Local Mode): 即Topology(相当于个任务,后续会详细讲解) 运行在本地机器的单JVM上,这个模式主要用来开发、调试。 2.远程模式(Remote Mode):在这个模式,我们把我们的Topology提交到集群,在这个模式中,Storm的所有组件都是线程安全的, 因为它们都会运行在不同的Jvm或物理机器上,这个模式就是正式的生产模式。 二、写个HelloWord Storm 我们现在创建这么个应用,统计文本文件中的单词个数,详细学习过Hadoop的朋友都应该写过。 那么我们需要具体创建这样个Topology,用个spout负责读取文本文件,用第一个bolt来解析成单词,用第二个bolt来对解析出的单词计数, 整体结构流程所示:Word Storage (words.txt) --> Spout(WordReader.java) --> Bolt(WordNormalizer.java) --> Bolt(WordCounter.java) 可以从这里下载源码:https://github.com/storm-book/examples-ch02-getting_started/zipball/master 三、写个可运行的Demo很简单,我们只需要三步: 1.创建个Spout读取数据(数据源) Spout作为数据源,它实现了IRichSpout接口,功能是读取个文本文件并把它的每行内容发送给bolt。 2.创建bolt处理数据 创建两个bolt来处理Spout发射出的数据 Spout已经成功读取文件并把每行作为个tuple(在Storm数据以tuple的形式传递)发射过来,我们这里需要创建两个bolt分别来负责解析每行和对单词计数。 Bolt中最重要的是execute方法,每当个tuple传过来时它便会被调用。 3.创建个Topology提交到集群 4.运行结果分析 如果你的words.txt文件有如下内容: Storm test are great is an Storm simple application but very powerful really Storm is great 你应该会在日志中看到类似下面的内容: is: 2 application: 1 but: 1 great: 1 test: 1 simple: 1 Storm: 3 really: 1 are: 1 great: 1 an: 1 powerful: 1 very: 1 在这个例子中,每类节点只有个实例。但是如果你有个非常大的日志文件呢?你能够很轻松的改变系统中的节点数量实现并行工作。这个时候,你就要创建两个WordCounter实例。 builder.setBolt("word-counter", new WordCounter(),2).shuffleGrouping("word-normalizer"); 程序返回时,你将看到: — 单词数 【word-counter-2】 — application: 1 is: 1 great: 1 are: 1 powerful: 1 Storm: 3 — 单词数 [word-counter-3] — really: 1 is: 1 but: 1 great: 1 test: 1 simple: 1 an: 1 very: 1 棒极了!修改并行度实在是太容易了(当然对于实际情况来说,每个实例都会运行在单独的机器上)。 不过似乎有个问题:单词is和great分别在每个WordCounter各计数次。怎么会这样? 当你调用shuffleGrouping时,就决定了Storm会以随机分配的方式向你的bolt实例发送消息。 在这个例子中,理想的做法是相同的单词问题发送给同个WordCounter实例。 你把shuffleGrouping(“word-normalizer”)换成fieldsGrouping(“word-normalizer”, new Fields(“word”))就能达到目的。 试试,重新运行程序,确认结果。 你将在后续章节学习更多分组方式和消息流类型。 参考文章 http://blog.csdn.net/suifeng3051/article/details/38369689 http://ifeve.com/getting-started-with-storm-2/
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值