Storm简单使用——一举入门

转发请注明出处:http://blog.csdn.net/qq_28945021/article/details/52912142

主体

刚开始学习Storm发现这个流式处理框架还是比较容易理解与使用的。也许是我刚开始学习吧。这篇博客可能没有太多干货,只是记录下来供自己以后翻看。
整个Storm程序可分为几个部分:

  1. spout:作为Storm的开始模块。
  2. bolt:作为每一个任务的处理模块。

spout

作为Storm的开始,spout负责读取数据并不断地给bolt以供处理。因此spout的两个核心方法便是:open()——用于读取数据并放入集合(SpoutOutputCollector)中以供传输。nextTuple()——用于数据预处理及传输到bolt。

其中open方法的签名为:
public void open(Map conf, TopologyContext context, SpoutOutputCollector collector)

conf是Storm的配置文件,里面配置了数据源等信息。想WordCount的例子中就配置了word文件的路径。可在open方法中读取出并读取数据。

可从TopologyContext 获取taskNum,taskId,任务Id等等一系列参数,以此可以做的事就很多了——包括分配不同bolt,包括监督任务数等等。

collector是一个数据传输管道,它能让我们发布交给bolts处理的数据。

这段代码是WordCount例子中的一个简单spout:

 /**
     * 我们将创建一个文件并维持一个collector对象
     * 第一个被调用的spouts方法
     * @param conf:在定义topology对象时创建
     * @param TopologyContext:包含所有拓扑数据
     * @param SpoutOutputCollector:它能让我们发布交给bolts处理的数据。
     */
    @Override
    public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
        try {
            this.context=context;
            this.fileReader = new FileReader(conf.get("wordsFile").toString()); //从conf中读取出wordsFile(文件位置)
        } catch (FileNotFoundException e) {
            throw new RuntimeException("Error reading file ["+conf.get("wordFile")+"]");
        }
        this.collector=collector;
    }

nextTuple的方法签名为

public void nextTuple()

spouts会不断调用这个方法直到传输完或者我们通过代码控制停止时停止。值得注意的是,spout停止并不代表着Storm停止运行。仅仅代表不再有新的数据传入程序而已。我的小例子中使用了一个静态的flag来控制nextTuple的停止。

/**
     * 这个方法用来分发文件中的文本行
     */
    @Override
    public void nextTuple() {
        /**
         * 这个方法会不断被调用直到文件被读取完,我们等待并返回
         */
        if(completed){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //什么也不做
            }
            return;
        }
        String str;
        //创建Reader
        BufferedReader reader = new BufferedReader(fileReader);
        //读所有文本行
        try {
            while((str=reader.readLine())!=null){
                //按行发布一条消息
                this.collector.emit(new Values(str),str);
            }
        } catch (IOException e) {
            throw new RuntimeException("Error reading tuple",e);
        }finally{
            completed=true;     //这里控制要么发完要么出错停止调用
        }
    }

spout还有三个十分重要的方法:ack,fail,declareOutputFields

ack方法代表之后的bolt运行成功,fail代表失败。这是通过锚记得到的,因此bolt中必须调用这两个方法,否则无法监督程序运行状态。在这两个方法中也能进行操作。例如:在fail方法中将失败方法重新计算。

declareOutputFields配置由该spout传出的数据将传到哪个域,默认default域,如果配置了域则只会传到该域bolt中否则为随机配置以达到每个bolt数据均衡。

bolt

作为Storm的计算模块,bolt模块含有三个主要方法:prepare——初始化方法,签名部分与spout的open一致,由此就知道了,Storm支持在运行中添加新的输入数据。execute(Tuple input)——核心计算方法。其中Tuple是Storm对集合的一个重写容器类。也是从spout或其他bolt传来的数据。由于perpare中含有collector,bolt不仅可以用来进行计算后输出,也可传给下一个bolt。cleanup()——bolt运行结束后运行的方法,通常最后一个bolt才使用。可将数据放入接下来的消息队列也好,持久化数据也好。

prepare:与spout的open方法大致相同,不赘述。

execute(Tuple input):正如上文所说,一定要调用ack方法,而fail方法会在失败后自动调用。

/**
     * 最主要!
     * 这里处理从spouts或其他地方传入的Tuple
     */
    @Override
    public void execute(Tuple input) {
        String sentence = input.getString(0);   //因为我们传的Tuple只有一个元素:line
        String words[] = sentence.split(" ");
        for(String word:words){
            word=word.trim();
            if(!word.isEmpty()){
                word=word.toLowerCase();
                //发布这个单词
                collector.emit(new Values("word"));
            }
        }
        //对元组做出应答
        collector.ack(input);
    }

cleanup:这里只是简单的缓存到内存

/**
     * 这个spout结束时(集群关闭的时候),我们会显示单词数量(执行cleanup方法)
     */
    @Override
    public void cleanup() {
        System.out.println("-- 单词数 【"+name+"-"+id+"】 --");
        for(Map.Entry<String,Integer> entry : counters.entrySet()){
            System.out.println(entry.getKey()+": "+entry.getValue());
        }
    }

同样的,bolt也含有declareOutputFields方法用于指定该bolt之后要传到的域。

知道这些,简单的Storm操作就能够满足了。接下来的进阶——包括使用消息队列中间件,自定义DRPC,看看之后有时间再来几篇博客吧。

以下是所有代码:(Main类的注释详细,可以看看)

package test;

import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.topology.TopologyBuilder;
import bolts.WordCounter;
import bolts.WordNormalizer;
import grouping.ModuleGrouping;
import spouts.WordReader;

public class TopologyMain {
    public static void main(String[] args) throws InterruptedException {
        //1.TopologyBuilder将用来创建拓扑,它决定Storm如何安排各节点,以及它们交换数据的方式。
        //在spout和bolts之间通过shuffleGrouping方法连接。
        TopologyBuilder builder = new TopologyBuilder();
        builder.setSpout("word-reader", new WordReader());
        builder.setBolt("word-normalizer", new WordNormalizer())
        .customGrouping("word-reader", new ModuleGrouping())
        .shuffleGrouping("word-reader");
        builder.setBolt("word-counter", new WordCounter()).shuffleGrouping("word-normalizer");

        //2.下一步,创建一个包含拓扑配置的Config对象,它会在运行时与集群配置合并,并通过prepare方法发送给所有节点。
        Config conf=new Config();
        conf.put("wordsFile", "D:\\software\\大数据\\Strom\\testStrom1.txt");
        conf.setDebug(true);    //由于是在开发阶段,设置debug属性为true,Strom会打印节点间交换的所有消息,以及其它有助于理解拓扑运行方式的调试数据。

        //3.要用一个LocalCluster对象运行这个拓扑。在生产环境中,拓扑会持续运行,不过对于这个例子而言,你只要运行它几秒钟就能看到结果。
        conf.setMaxTaskParallelism(1);

        LocalCluster cluster =new LocalCluster();
        cluster.submitTopology("Getting-Started-Topologie",conf,builder.createTopology());
        Thread.sleep(2000);
        cluster.shutdown();
    }
}
package spouts;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;

import backtype.storm.spout.SpoutOutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IRichSpout;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Values;

public class WordReader implements IRichSpout{
    private SpoutOutputCollector collector;
    private FileReader fileReader;
    private boolean completed = false;
    private TopologyContext context;
    public boolean isDistributed() {return false;}

    @Override
    public void ack(Object msgId) {
        System.out.println("OK"+msgId);
    }
    @Override
    public void close() {}
    @Override
    public void fail(Object msgId) {
        System.out.println("Fail"+msgId);
    }
    /**
     * 这个方法用来分发文件中的文本行
     */
    @Override
    public void nextTuple() {
        /**
         * 这个方法会不断被调用直到文件被读取完,我们等待并返回
         */
        if(completed){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //什么也不做
            }
            return;
        }
        String str;
        //创建Reader
        BufferedReader reader = new BufferedReader(fileReader);
        //读所有文本行
        try {
            while((str=reader.readLine())!=null){
                //按行发布一条消息
                this.collector.emit(new Values(str),str);
            }
        } catch (IOException e) {
            throw new RuntimeException("Error reading tuple",e);
        }finally{
            completed=true;     //这里控制要么发完要么出错停止调用
        }
    }
    /**
     * 我们将创建一个文件并维持一个collector对象
     * 第一个被调用的spouts方法
     * @param conf:在定义topology对象时创建
     * @param TopologyContext:包含所有拓扑数据
     * @param SpoutOutputCollector:它能让我们发布交给bolts处理的数据。
     */
    @Override
    public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
        try {
            this.context=context;
            this.fileReader = new FileReader(conf.get("wordsFile").toString()); //从conf中读取出wordsFile(文件位置)
        } catch (FileNotFoundException e) {
            throw new RuntimeException("Error reading file ["+conf.get("wordFile")+"]");
        }
        this.collector=collector;
    }
    /**
     * 声明输入域"word"
     * @param declarer
     */
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("line"));
    }

    @Override
    public void activate() {

    }

    @Override
    public void deactivate() {

    }

    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }




}
package bolts;

import java.util.Map;

import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IRichBolt;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;
/**
 * 第一个bolt,WordNormalizer,负责得到并标准化每行文本。它把文本行切分成单词,大写转化成小写,去掉头尾空白符。
 * @author wrm
 *
 */
public class WordNormalizer implements IRichBolt{
    private OutputCollector collector;
    /**
     * 声明bolt出参
     * 这里我们声明bolt将发布一个名为“word”的域
     */
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("word"));
    }

    /**
     * 最主要!
     * 这里处理从spouts或其他地方传入的Tuple
     */
    @Override
    public void execute(Tuple input) {
        String sentence = input.getString(0);   //因为我们传的Tuple只有一个元素:line
        String words[] = sentence.split(" ");
        for(String word:words){
            word=word.trim();
            if(!word.isEmpty()){
                word=word.toLowerCase();
                //发布这个单词
                collector.emit(new Values("word"));
            }
        }
        //对元组做出应答
        collector.ack(input);
    }

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



    @Override
    public void cleanup() {

    }

    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }




}
package bolts;

import java.util.HashMap;
import java.util.Map;

import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IRichBolt;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Tuple;
/**
 * 这个例子的bolt什么也没发布,它把数据保存在map里,但是在真实的场景中可以把数据保存到数据库。
 * @author wrm
 *
 */
public class WordCounter implements IRichBolt{
    Integer id;
    String name;
    Map<String,Integer> counters;
    private OutputCollector collector;
    //初始化方法
    @Override
    public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
        this.counters = new HashMap<String, Integer>();
        this.collector = collector;
        this.name = context.getThisComponentId();
        this.id = context.getThisTaskId();
    }
    /**
     * 为每个单词计数
     */
    @Override
    public void execute(Tuple input) {
        String str = input.getString(0);
        //如果单词不存在于Map,我们就从新创建一个
        if(!counters.containsKey(str)){
            counters.put(str, 1);
        }else{
            Integer c = counters.remove(str)+1;
            counters.put(str, c);
        }
        //对元组进行应答
        collector.ack(input);
    }
    /**
     * 这个spout结束时(集群关闭的时候),我们会显示单词数量(执行cleanup方法)
     */
    @Override
    public void cleanup() {
        System.out.println("-- 单词数 【"+name+"-"+id+"】 --");
        for(Map.Entry<String,Integer> entry : counters.entrySet()){
            System.out.println(entry.getKey()+": "+entry.getValue());
        }
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {

    }
    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }



}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值