storm-基础-安装与例子

本地模式

至少完如下配置,每一行前都有空格,每个:后都有空格

 storm.zookeeper.servers:
    ¦- "ub"           
 storm.zookeeper.port: 2181
 storm.zookeeper.root: "/storm"
 storm.local.dir: "/home/sean/ext/tmp/storm"
 nimbus.seeds: ["ub"]  

启动nimbus、supervisor、页面管理

nohup storm nimbus &
nohup storm supervisor &
nohup storm ui &

标准的配置文件:
https://github.com/apache/storm/blob/v1.0.0/conf/defaults.yaml

开发测试

参考:
https://github.com/runfriends/GettingStartedWithStorm-cn/blob/master/chapter2/Hello World Storm.md#hello-world

可以找出Twitter上的热门话题
一个spout读取文本
第一个bolt标准化单词
第二个bolt为单词技数

Spout
实现IRichSpout接口
从文件中按行读取文本,并把文本行提供给第一个bolt
一个spout发布一个定义域列表。这个架构允许你使用不同的bolts从同一个spout流读取数据,它们的输出也可作为其它bolts的定义域,以此类推。

public class WordRead extends BaseRichSpout {

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

    @Override
    public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
        try {
            this.context = context;
            this.fileReader = new FileReader(conf.get("wordsFile").toString());
        } catch (FileNotFoundException e) {
            throw new RuntimeException("Error reading file ["+conf.get("wordFile")+"]");
        }
        this.collector = collector;
    }

    //这个方法做的惟一一件事情就是分发文件中的文本行
    @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(Exception e){
            throw new RuntimeException("Error reading tuple",e);
        }finally{
            completed = true;
        }
    }

    @Override
    public void fail(Object msgId) {
        System.out.println("FAIL:"+msgId);
    }

    //声明一个输入域"word"
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("line"));
    }
}

调用的第一个方法是open(Map conf, TopologyContext context, SpoutOutputCollector collector)
输入参数:配置对象、TopologyContext对象包含拓扑数据,SpoutOutputCollector对象发布交给bolts处理的数据;
通过nextTuple向bolts发送待处理的数据,这个方法要读取文件并逐行发布数据;
Values是一个ArrarList实现,它的元素就是传入构造器的参数;

nextTuple()会在同一个循环内被ack()和fail()周期性的调用。没有任务时它必须释放对线程的控制,其它方法才有机会得以执行。因此nextTuple的第一行就要检查是否已处理完成。如果完成了,为了降低处理器负载,会在返回前休眠一毫秒。如果任务完成了,文件中的每一行都已被读出并分发了。
tuple是一个列表,可以使任何对象,默认情况Storm会序列化字符串、字节数组、ArrayList、HashMap和HashSet等类型;

Bolts
bolt最重要的方法是void execute(Tuple input),每次接收到元组时都会被调用一次,还会再发布若干个元组;
当调用nextTuple或execute方法时可能会发布0、1或若干个元组.
一个bolt WordNormalizer标准化每行文本,切分单词,转化大小写,去空白符;
首先声明bolt参数

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

这里我们声明bolt将发布一个名为“word”的域
实现public void execute(Tuple input)处理输入元组

public void execute(Tuple input){
    String sentence=input.getString(0);
    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);
}

第一行从元组读取值。值可以按位置或名称读取;
调用collector发布对象,每次调用ack()确认处理了一个元组
完整代码如下

    public class WordNormalizer implements IRichBolt{
        private OutputCollector collector;
        public void cleanup(){}
        /**
          * *bolt*从单词文件接收到文本行,并标准化它。
          * 文本行会全部转化成小写,并切分它,从中得到所有单词。
         */
        public void execute(Tuple input){
            String sentence = input.getString(0);
            String[] words = sentence.split(" ");
            for(String word : words){
                word = word.trim();
                if(!word.isEmpty()){
                    word=word.toLowerCase();
                    //发布这个单词
                    List a = new ArrayList();
                    a.add(input);
                    collector.emit(a,new Values(word));
                }
            }
            //对元组做出应答
            collector.ack(input);
        }
        public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
            this.collector=collector;
        }

        /**
          * 这个*bolt*只会发布“word”域
          */
        public void declareOutputFields(OutputFieldsDeclarer declarer) {
            declarer.declare(new Fields("word"));
        }
    }

如果收到This is the Storm book会发送5个元组;
下一个bolt WordCounter统计单词,拓扑接收调用cleanup(),显示每个单词的数量;

public class WordCounter implements IRichBolt{
    Integer id;
    String name;
    Map<String,Integer> counters;
    private OutputCollector collector;

    /**
      * 这个spout结束时(集群关闭的时候),我们会显示单词数量
      */
    @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 execute(Tuple input) {
        String str=input.getString(0);
        /**
         * 如果单词尚不存在于map,我们就创建一个,如果已在,我们就为它加1
         */
        if(!counters.containsKey(str)){
            conters.put(str,1);
        }else{
            Integer c = counters.get(str) + 1;
            counters.put(str,c);
        }
        //对元组做出应答
        collector.ack(input);
    }

    /**
     * 初始化
     */
    @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 declareOutputFields(OutputFieldsDeclarer declarer) {}
}

execute方法使用一个map收集单词并计数。拓扑结束时,将调用clearup()方法打印计数器map;
通常情况下,当拓扑关闭时,你应当使用cleanup()方法关闭活动的连接和其它资源;

主类
主类中创建拓扑和一个本地集群对象,以便于在本地测试和调试;
LocalCluster可以通过Config对象,让你尝试不同的集群配置。比如,当使用不同数量的工作进程测试你的拓扑时,如果不小心使用了某个全局变量或类变量,你就能够发现错误。
所有拓扑节点的各个进程必须能够独立运行,而不依赖共享数据(也就是没有全局变量或类变量),因为当拓扑运行在真实的集群环境时,这些进程可能会运行在不同的机器上。
TopologyBuilder将用来创建拓扑,它决定Storm如何安排各节点,以及它们交换数据的方式

TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("word-reader", new WordReader());
builder.setBolt("word-normalizer", new WordNormalizer()).shuffleGrouping("word-reader");
builder.setBolt("word-counter", new WordCounter())..shuffleGrouping("word-normalizer");

在spout和bolts之间通过shuffleGrouping方法连接;
这种分组方式决定了Storm会以随机分配方式从源节点向目标节点发送消息;
下一步,创建一个包含拓扑配置的Config对象,它会在运行时与集群配置合并,并通过prepare方法发送给所有节点。

Config conf = new Config();
conf.put("wordsFile", args[0]);
conf.setDebug(true);

由spout读取的文件的文件名,赋值给wordFile属性。
由于是在开发阶段,设置debug属性为true,Strom会打印节点间交换的所有消息,以及其它有助于理解拓扑运行方式的调试数据。
用一个LocalCluster对象运行这个拓扑

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

调用createTopology和submitTopology,运行拓扑,休眠两秒钟(拓扑在另外的线程运行),然后关闭集群。

public class TopologyMain {
    public static void main(String[] args) throws InterruptedException {
    //定义拓扑
        TopologyBuilder builder = new TopologyBuilder());
        builder.setSpout("word-reader", new WordReader());
        builder.setBolt("word-normalizer", new WordNormalizer()).shuffleGrouping("word-reader");
        builder.setBolt("word-counter", new WordCounter(),2).fieldsGrouping("word-normalizer", new Fields("word"));

    //配置
        Config conf = new Config();
        conf.put("wordsFile", args[0]);
        conf.setDebug(false);

    //运行拓扑
         conf.put(Config.TOPOLOGY_MAX_SPOUT_PENDING, 1);
        LocalCluster cluster = new LocalCluster();
        cluster.submitTopology("Getting-Started-Topologie", conf, builder.createTopology();
        Thread.sleep(1000);
        cluster.shutdown();
    }
}

观察运行情况
内容

 Storm test are great is an Storm simple application but very powerful really Storm is great 

如果文档非常大,可以创建多个实例

 builder.setBolt("word-counter", new WordCounter(),2).shuffleGrouping("word-normalizer");

相同的单词发送给同一个WordCounter实例
把shuffleGrouping("word-normalizer")换成fieldsGrouping("word-normalizer", new Fields("word"))

posted @ 2016-11-25 10:35  zhangshihai1232  阅读(113)  评论(0)    收藏  举报