Storm开发

以Storm开发指南中的一个简单例子开始

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.topology.OutputFieldsDeclarer;
import backtype.storm.topology.TopologyBuilder;
import backtype.storm.topology.base.BaseRichBolt;
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 backtype.storm.topology.base.BaseRichSpout;
import org.apache.log4j.Logger;
import java.util.Map;
import java.util.HashMap;
import java.util.Random;

public class ExclamationTopology {
    
    public static class TestWordSpout extends BaseRichSpout {
       public static Logger LOG = Logger.getLogger(TestWordSpout.class);
       boolean _isDistributed;
       SpoutOutputCollector _collector;

       public TestWordSpout() {
            this(true);
       }

       public TestWordSpout(boolean isDistributed) {
             _isDistributed = isDistributed;
       }
        
       public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
            _collector = collector;
       }
    
       public void close() {
        
        }
        
        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 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(!_isDistributed) {
                 Map<String, Object> ret = new HashMap<String, Object>();
                 ret.put(Config.TOPOLOGY_MAX_TASK_PARALLELISM, 1);
                 return ret;
            } else {
              return null;
          }
        }    
      }

    public static class ExclamationBolt extends BaseRichBolt {
        OutputCollector _collector;

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

        @Override
        public void execute(Tuple tuple) {
            _collector.emit(tuple, new Values(tuple.getString(0) + "!!!"));
            _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("word", new TestWordSpout(), 10);        
        builder.setBolt("exclaim1", new ExclamationBolt(), 3)
                .shuffleGrouping("word");
        builder.setBolt("exclaim2", new ExclamationBolt(), 2)
                .shuffleGrouping("exclaim1");
                
        Config conf = new Config();
        conf.setDebug(true);
        
        if(args!=null && args.length > 0) {
            conf.setNumWorkers(3);
            
            StormSubmitter.submitTopology(args[0], conf, builder.createTopology());
        } else {
        
            LocalCluster cluster = new LocalCluster();
            cluster.submitTopology("test", conf, builder.createTopology());
            Utils.sleep(10000);
            cluster.killTopology("test");
            cluster.shutdown();    
        }
    }
}



几点说明
1.Spout实现
  Strom使用元组作为数据模型,元组就是一组命名的值,元组中的每个字段都可以是任何类型的对象。Storm支持所有基本类型,string和byte数组作为元组字段值。如果要使用自己定义的类型,也只需要为你自己定义的类型实现并且注册一个serializer即可。每个节点还必须要为输出的元组定义字段名称。
  Spout要么继承BaseRichSpout要么实现IRichSpout和IComponent接口,对与实现来说主要是实现以下这些函数(参考TestWordSpout的实现):
  void open(java.util.Map conf,
          TopologyContext context,
          SpoutOutputCollector collector)
     当一个Supervisor初始化该Spout组件时调用,提供Spout运行所必需的环境
     参数
        conf - Storm关于这个Spout的配置
        context - 这个配置被用来获取该Spout任务的信息,包括任务id,组件id,输入输出信息等等
        collector - 用来从这个Spout里发送元组,元组可以在任何时间里发送,包括open和close函数里。collector是线程安全的,应该被作为一
         个实例对象保存到Spout对象里

   void declareOutputFields(OutputFieldsDeclarer declarer)
   定义topology里的Stream的schema
   declarer - 定义输出stream的ids,输出的字段,输出stream是不是直接stream(direct stream)

  java.util.Map<java.lang.String,java.lang.Object> getComponentConfiguration()
    定义该组件的配置

 void     ack(java.lang.Object msgId)
 以msgId消息告诉Storm这个Spout已经成功输出了该元组
 void     activate()
 激活Spout,Spout从deactivate模式转化为activate模式,Spout开始调用nextTuple输出数据。
 void     close()
 关闭Spout
 void     deactivate()
 解除激活Spout,Spout从activate模式转化为deactivate模式,Spout停止调用nextTuple输出数据
 void     fail(java.lang.Object msgId)
 以msgId消息告诉Storm这个Spout输出该元组失败,主要用于将该元组重新放回消息队列,以在一段时间后重发该元组
   void nextTuple()
   调用该函数请求Storm发送元组到Output Collector,这个函数不应该是阻塞的,当没有元组发送时,一般调用sleep,以充分利用CPU

2.Bolt的实

最主要的三个函数是,其余的关于组件接口的函数和Spout的实现是一样的,这里就不说了
void prepare(java.util.Map stormConf,
             TopologyContext context,
             OutputCollector collector)
 和Spout的open函数的作用类似,在Bolt组件初始化的时候调用,提供Bolt所必需的环境

void execute(Tuple input)
    处理单个输入的元组,元组对象包含了从组件/流/任务得来的元数据。元组的值通过Tuple#getValue访问,Bolt并不需要马上处理元组,可以先将数据保存在合适的时间处理。Bolt使用在prepare函数中得到的OutputCollector对象输出元组,必须在这个函数里面确保使用OutputCollector#ack或者OutputCollector#fail告知Storm已经处理成功或者处理失败,否则Storm将无法确定Spout里元组是否已经被处理完成。

void cleanup()
     当Bolt要关闭的时候调用,但是不能保证该函数一定可以被调用,当使用kill -9命令杀死工作进程时该函数就无法调用,一般用于local mode下清理使用

3.Topology构建
  构建相当直接,使用TopologyBuilder构建,如例子中的main函数的代码所示。TopologyBuilder#setSpout设置Topology的Spout,使用TopologyBuilder#setBolt设置Topology的Bolt。
  其中
  public BoltDeclarer setBolt(java.lang.String id,
                              IBasicBolt bolt,
                              java.lang.Number parallelism_hint)
  id-需要消费该组件输出的流的组件用来识别该组件的唯一标识
  bolt-该节点处理数据的Bolt
  parallelism_hint-用来执行该Bolt的任务的数量,每个任务会在集群的某个进程的某个线程里面执行
 
  其中BoltDeclarer中包含了很多元组从一个节点怎么映射到另一个节点的规则,例子中的builder.setBolt("exclaim1", new ExclamationBolt(), 3)
        .shuffleGrouping("words")表示设置exclaim1节点的Bolt为ExclamationBolt,并行度为3,从words节点到exclaim1节点使用随机散发规则。

  public SpoutDeclarer setSpout(java.lang.String id,
                              IRichSpout spout,
                              java.lang.Number parallelism_hint)

   
        id-需要消费该组件输出的流的组件用来识别该组件的唯一标识
        spout-Spout类
        parallelism_hint-用来执行该Bolt的任务的数量,每个任务会在集群的某个进程的某个线程里面执行
发布了58 篇原创文章 · 获赞 3 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/lldustc/article/details/8452987