storm(day01)

1.Storm概述
Storm是一个开源的分布式实时计算系统,可以简单、可靠的处理大量的数据流。实时计算可以实时获取数据进行运算,得到计算结果,在很多实时性要求比较高的场景下有大量的应用.例如:微博热门话题榜单、电商网站实时推荐、地图路况信息。

官网

https://storm.apache.org/

下载

https://storm.apache.org/downloads.html
https://archive.apache.org/dist/storm/apache-storm-0.9.3/

实时计算和离线计算有较大的不同,实时计算强调实时性,数据不断流入,实时运算后结果实时反馈,实时计算没有数据积累的过程,有开始没有结束,如果不人为停止会一直运行下去。
Storm是一个开源的分布式实时计算系统,可以简单、可靠的处理大量的数据流。
storm支持水平扩展,具有高容错性,保证每个消息都会得到处理,而且处理速度很快(在一个小集群中,每个结点每秒可以处理数以百万计的消息)。
Storm的部署和运维都很便捷,而且更为重要的是可以使用多种编程语言来开发应用。

2 Storm核心组件
storm结构称为topology(拓扑),由stream(数据流),spout(喷嘴-数据流的生成者),bolt(阀门-数据流运算者)组成(参考图:Storm组成结构)。

Spout负责连接数据源,接收数据,转换为tuple向后发送,只负责转化数据,不负责数据处理。

Bolt负责接收数据,进行运算,运算过后可以继续向后发送tuple,给其他零个或多个Bolt。这样利用spout和Bolt就可以组件起复杂的数据处理流网络,实现负责的分布式实时运算。I整个由spout和Bolt组成的复杂的数据流处理网络称之为topology

在这里插入图片描述
不同于Hadoop中的job,Storm中的topology会一直运行下去,除非进程被杀死或取消部署。

Storm入门
1.Storm单机模式
Storm提供了单机开发模式,即使没有storm集群也可以在本地java环境中进行开发测试。具体开发步骤∶
创建java项目
导入Storm相关开发包

单词统计案例
在这里插入图片描述

package cn.com.storm.base;

import org.apache.storm.spout.SpoutOutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichSpout;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Values;

import java.util.Map;

public class SentenceSpout extends BaseRichSpout {
    private String[] sentences={
         "ee oo pp",
         "ff ff ll",
         "oo pp ll",
         "11 gg mm",
         "vv dd dd"
    };
    private SpoutOutputCollector collector=null;
    /*
    * 初始化的方法,当前spout被初始化时调用
    * conf:配置对象,包含配置信息,配置信息合并自集群配置和当前拓扑的配置
    * context:上下文对象,代表当前spout运行的环境,可以通过该对象获取任务组件 拓扑 相关的信息
    * collector:用来发送tuple的对象,可以任意位置发送tuple,这个方法线程安全,通常保存在类的内部方便使用
    *
    * */
    @Override
    public void open(Map<String, Object> conf, TopologyContext context, SpoutOutputCollector collector) {
        this.collector=collector;
    }

    /*storm会不停的调用此方法,来要求spout发送一个tuple
    * 如果有tuple则通过collector发送,如果没有也不要阻塞这个方法,直接返回即可。
    * storm的底层在用一个死循环不停的调用nextTuple ack fail方法
    * 如果真的没有tuple要发送,在返回之前最好睡上一毫秒,以便于不至于浪费过多cpu。
    */
    int index=0;
    @Override
    public void nextTuple() {
        if(index< sentences.length){
            collector.emit(new Values(sentences[index]));
            index++;
        }else{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    /*
    * 声明当前spout发送的tuple的结构
    * declarer:通过此对象声明当前组件发送的tuple的结构
     * */
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("sentence"));
    }
}

package cn.com.storm.base;

import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;

import java.util.Map;

public class SplitSentenceBolt extends BaseRichBolt {

    private OutputCollector collector=null;


    /*
     * 初始化的方法,当前spout被初始化时调用
     * conf:配置对象,包含配置信息,配置信息合并自集群配置和当前拓扑的配置
     * context:上下文对象,代表当前spout运行的环境,可以通过该对象获取任务组件 拓扑 相关的信息
     * collector:用来发送tuple的对象,可以任意位置发送tuple,这个方法线程安全,通常保存在类的内部方便使用
     *
     * */
    @Override
    public void prepare(Map<String, Object> conf, TopologyContext context, OutputCollector collector) {
        this.collector=collector;
    }

    /**
    *针对输入的每一个tuple都触发这个方法,对tuple进行处理。
    * input:当前要处理的tuple,可以通过tuple获取到组件流任务相关信息。
    * */
    @Override
    public void execute(Tuple input) {
        String sentence = input.getStringByField("sentence");
        String[] words = sentence.split(" ");
        for (String word:words) {
            collector.emit(new Values(word));
        }
    }

    /*
     * 声明当前spout发送的tuple的结构
     * declarer:通过此对象声明当前组件发送的tuple的结构
     * */
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("word"));
    }
}

package cn.com.storm.base;

import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;

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

public class WordCountBolt extends BaseRichBolt {
    private OutputCollector collector=null;
    @Override
    public void prepare(Map<String, Object> conf, TopologyContext context, OutputCollector collector) {
        this.collector=collector;
    }

    private Map<String,Integer> map=new HashMap<>();

    @Override
    public void execute(Tuple input) {
        String word = input.getStringByField("word");
        map.put(word,map.containsKey(word)?map.get(word)+1:1);
        collector.emit(new Values(word,map.get(word)));
    }

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

package cn.com.storm.base;

import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseRichBolt;
import org.apache.storm.tuple.Tuple;

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

public class ReportBolt extends BaseRichBolt {
    private OutputCollector collector=null;
    @Override
    public void prepare(Map<String, Object> conf, TopologyContext context, OutputCollector collector) {
        this.collector=collector;
    }

    Map<String,Integer> map=new HashMap<>();
    @Override
    public void execute(Tuple input) {
        String word = input.getStringByField("word");
        int count = input.getIntegerByField("count");
        map.put(word,count);
        System.out.println(word+"====="+count);
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {

    }

    /*
    *输出,销毁
    * */
    @Override
    public void cleanup() {
        System.out.println("==========");
        for(Map.Entry<String,Integer> entry:map.entrySet()){
            String word = entry.getKey();
            Integer count = entry.getValue();
            System.out.println(word+"=========="+count);
        }
        System.out.println("==========");
    }
}

package cn.com.storm.base;

import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.generated.StormTopology;
import org.apache.storm.topology.TopologyBuilder;
import org.apache.storm.tuple.Fields;


public class WcTopology {
    public static void main(String[] args) throws Exception{
        //创建组件
        SentenceSpout spout=new SentenceSpout();
        SplitSentenceBolt splitSentenceBolt=new SplitSentenceBolt();
        WordCountBolt wordCountBolt=new WordCountBolt();
        ReportBolt reportBolt=new ReportBolt();

        //创建拓扑构建者
        TopologyBuilder builder=new TopologyBuilder();

        //声明拓扑结构
        builder.setSpout("Sentence_Spout",spout);
        builder.setBolt("Split_Sentence_Bolt",splitSentenceBolt)
                .shuffleGrouping("Sentence_Spout");
        builder.setBolt("Word_Count_Bolt",wordCountBolt)
                .fieldsGrouping("Split_Sentence_Bolt",new Fields("word"));
        builder.setBolt("Report_Bolt",reportBolt)
                .globalGrouping("Word_Count_Bolt");


        //创建拓扑
        StormTopology topology=builder.createTopology();

        //提交拓扑到集群中运行---生产服务器集群
//        Config conf=new Config();
//        StormSubmitter.submitTopology("Wc_Topology",conf,topology);


        //本地模拟
        LocalCluster cluster=new LocalCluster();
        Config conf=new Config();
        cluster.submitTopology("Wc_Topology",conf,topology);

        Thread.sleep(3000);
        cluster.killTopology("Wc_Topology");
        cluster.shutdown();

    }
}

执行记录
在这里插入图片描述

Storm的并发控制
1.Storm中的并发级别
Storm作为一个分布式实时计算系统,是可以通过在不同级别并发来提升程序的效率的。storm的并发体现在如下的四个级别:
i. Node -服务器级别
配置在storm集群中的一个服务器,会执行Topology的一部分运算,一个storm集群中包含一个或者多个Node
ii. worker-进程级别
指一个Node上相互独立运作的JVM进程,每个Node可以配置运行一个或多个worker。一个Topology会分配到一个或者多个worker上运行。
ii. Executor-线程级别
指一个worker的jvm中运行的java线程。一个Worker中可能同时运行着多个Executor。
iv. Task -任务
task是spout和bolt的实例,他们的nextTuple()和execute()方法会被executors线程调用执行。多个task可以指派给同一个executer来执行。除非是明确指定,Storm默认会给每个executor分配一个task。

a.storm的默认并发控制
storm的默认并发度为1。也即,如果不明确指定,默认情况下,一个Topology会被分配到一个Node的一个worker中,一个Worker中可能运行多个executor,但每个executor只负责一个task。
在这里插入图片描述
b. Storm的并发控制
i.增加worker
可以通过API和修改配置两种方式修改分配给topology的woker数量。

Config conf=new Config();
        conf.setNumWorkers(2);

注意,增加worker的代码,在单机测试模式下无效。
ii.增加Executor

builder.setSpout(spout_id, spout,2)
builder.setBolt(bolt_id, bolt, 2)

**注意,这种做法为Spout或Bolt增加线程数量,而默认每个线程都运行一个task,所以增加线程如果不指定task数量,默认每个线程一个task。
ili.增加Task

builder.setSpout(---) -setNumTasks(2);
builder.setBolt(--).setNumTasks (task_nurm) ;

**注意,如果手动设置过task的数量,,task的总数量就是给定的值,而不管线程有几个,这些task会随机分配在这些个线程内部

案例1
在这里插入图片描述
案例2
在这里插入图片描述
在这里插入图片描述

3.数据流分组方式
在storm中,由于组件存在多个实例实现并发,所以上游的task向下游的task发送数据时,往往存在多个下游目的地task,此时如何分发数据就成了问题。
在storm中提供了7种数据流分组方式,指定数据如何在多个目的地task中分配。

a. Shuffle Grouping(随机分组)
随机分发数据流中的tuple给bolt中的各个task ,每个task接收到的tuple数量相同。
b. Fields Grouping(按字段分组)
根据指定字段的值进行分组。指定字段具有相同值的tuple会路由到同一个bolt中的task中。
c. All Grouping(全复制分组)
所有的tuple复制后分发给后续bolt的所有的task。
d. Globle Grouping(全局分组)
这种分组方式将所有的tuple路由到唯一一个task上,storm按照最小task id来选取接受数据的task。
这种分组方式下配置bolt和task的并发度没有意义。
这种方式会导致所有tuple都发送到一个JⅣM实例上,可能会引起strom集群中某个ⅣM或者服务器出现性能瓶颈或崩溃。
e. None Grouping(不分组)
在功能上和随机分组相同,为将来预留。
f. Direct Grouping(指向型分组)
数据源会通过emitDirect()方法来判断一个tuple应该由哪个strom组件来接受。只能在声明了是指向型数据流上使用。
g. Local or shuffle Grouping(本地或随机分组)
和随机分组类似,但是,这种数据流分组方式将会优先将tuple分发给同一个worker内的下一个bolt的 task,如果同一个worker中不存在下一个bolt的task,则随机分组方式分发数据。
这种方式可以减少网络传输,从而提高topology的性能。

**另外可以自定义数据流分组方式
写类实现customStreamGrouping接口

代码:
/
*自定义数据流分组方式经author park

*/
public ciass MyStreamGrouping implements CustomStreamGrouping {
/**
*·运行时调用,用来初始化分组信息con艺合X无:topology上下文对象后*E工些am:锈分组敌讴流房性
*七在rgetTasks:所有待选task的标识符列表.
*/
@override
public void prepaie (WorkerTopologyContext context,Globa1StreamId stream,
List<Integer>targetTasks){
/*
*核心方法,进行task选择 taaId:发送tuple的组件id.ralnes :tnple的值
*返回值要发往哪个task*/
@override
public List<Integer> chooseTasks (int taskId,List<Object> values){
return null:
}

Storm的可靠性保证
1Storm可靠性保证概述
Storm是分布式实时处理系统,需要在运算时保证数据处理的可靠。分布式数据处理的可靠性,具有三个级别∶

1)至多一次
可能会丢,但不会多
2)至少—次
可能会多,但不会丢
3)恰好一次、
不对丢,也不会多
storm提供了完整的三个级别的可靠性保障。

2.至多一次
Storm不做任何可靠性相关的配置时,默认就是此可靠性级别。
也即,可能因网络不稳定程序有异常等的情况下,造成数据丢失,会丢,但不会多。
3.至少一次
需要在数据发生丢失时,检测到数据丢失,重发数据。
Storm提供了ack-fail机制来实现重发,从而实现至少一次的语义。

a.至少一次语义在Spout中的实现
i.缓存发送过的数据
spout需要缓存发送过的数据,以便于在后续数据处理失败时,可以找到缓存的数据进行重发。
发送过的数据需要一直缓存,直到该数据在storm中完成了全部处理,才可以删除。
ii.实现ack()fail()方法
spout需要实现ack()fail(方法
当某条数据处理成功时,storm会自动调用spout中的ack()方法,开发者可以在这个方法中执行数据处理成功时的逻辑,例如,删除缓存的该数据。
当某条数据处理失败时,storm会自动调用spout中的fail(方法,开发者可以在这个方法中执行数据处理失败时的逻辑,主要是重发该数据。

b.至少—次语义在Bolt中的实现
i、在发送子tuple时锚定父tuple
所谓锚定,就是在bolt中发送子tuple时,将子tuple派生自父tuple的关系信息维系在collector中,以便于后续子孙tuple出错时,可以根据关系信息,找到最初的祖先tuple进行重发。

在这里插入图片描述

ii.在处理tuple成功或失败后告知storm
所谓的告知,就是在bolt处理tuple成功或失败时,告知collector , collector会收集这个信息,当整个拓扑对tuple都成功处理时,collector调用spout的ack方法,而任何一个处理失败,告知到collector后,collector调用spout的fail方法,进行重发

案例:改造之前的入门案例,在spout中处理至少—次的语义实现
spout修改

@Override
    public void nextTuple() {
        if(index< sentences.length){
            collector.emit(new Values(sentences[index]),index);
            index++;
        }else{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
/*
    *当storm完整了对某个tuple的处理时,会调用此方法进行通知通常在这个方法,
    *来删除之前缓存的该tuple的数据
    *msgId:之前保存的用来标识tuple的编号
    * */

    @Override
    public void ack(Object msgId) {
        System.out.println("处理数据成功...."+sentences[(int) msgId]);
    }

    /*
    * 当storm对某个tuple的处理失败时,会调用此方法进行通知通常在这个方法,重发该数据
    * msgId:之前保存的用来标识tuple的编号
    * */
    @Override
    public void fail(Object msgId) {
        System.out.println("处理数据失败...."+sentences[(int) msgId]);
        collector.emit(new Values(sentences[(int) msgId],msgId));
    }

bolt修改部分

@Override
    public void execute(Tuple input) {
        try {
            String sentence = input.getStringByField("sentence");

            //自己搞得bug
           /* if(sentence!=null||!sentence.equals("")){
                throw new RuntimeException("自己搞的bug");
            }*/
            String[] words = sentence.split(" ");
            for (String word:words) {
                //input锚定
                collector.emit(input,new Values(word));
            }
            collector.ack(input);
        }catch (Exception e){
            e.printStackTrace();
            collector.fail(input);
        }


    }

其他bolt类修改部分同上

4恰好一次

所有的数据要按照批发送批带编号,且编号是递增的
Bolt分为Processor阶段和Commit阶段的Bolt , Processor阶段的Bolt对批的处理随意并发,保证效率,而Commit阶段的Bolt要求批严格按照顺序处理,保证一起且一次的语义。

在这里插入图片描述

Storm也提供了恰好一次的语义支持,为了实现这样的语义,Storm提供了一套新的api -TransactionTopology来实现。

a.Transactionspout


需要写一个类继承BaseTransactionalSpout<metadata>在其中要求我们返回coordinater和emitter两个对象

b. TransactionBolt

需要写一个类继承BaseBatchBolt

Coordinator :
负责组织批的信息,交由Emitter来真正的发送批
和后续的组件的Coordinator通信,确认数据是否发送成功,如果失败则重发数据
Emitter:
根据Coordinator给的批的信息,真正发送批的数据

在这里插入图片描述

代码

package base4;

import java.io.Serializable;

public class SentenceMetaData implements Serializable {
    private int begin=0;
    private int end =0;

    public SentenceMetaData() {
    }

    public SentenceMetaData(int begin, int end) {
        this.begin = begin;
        this.end = end;
    }

    public int getBegin() {
        return begin;
    }

    public void setBegin(int begin) {
        this.begin = begin;
    }

    public int getEnd() {
        return end;
    }

    public void setEnd(int end) {
        this.end = end;
    }
}

package base4;

public class SentenceDB {
    public SentenceDB() {
    }

    private static String[] sentences={
            "ee oo pp",
            "ff ff ll",
            "oo pp ll",
            "11 gg mm",
            "vv dd dd",
            "hao hi nu",
            "hi ni ji"
    };

    public static String[] getSentences() {
        return sentences;
    }
}

package base4;

import backtype.storm.coordination.BatchOutputCollector;
import backtype.storm.transactional.ITransactionalSpout;
import backtype.storm.transactional.TransactionAttempt;
import backtype.storm.tuple.Values;

import java.math.BigInteger;

public class SentenceEmitter implements ITransactionalSpout.Emitter<SentenceMetaData> {




    /*
    *接收到Coordinator交过来的元数据信息和当前批的编号需要在这个方法中真的组织一个批发送
    *要保证在任何时候对于同一个批的编号同一个元数据发送的批的tuple必须一致,以保证在重发时一致
需要时可已将发送的批的数据进行缓存以保证数据一致发送的tuple的第一个字段一定要保留给事务编号
*
     *
    * */
    @Override
    public void emitBatch(TransactionAttempt tx, SentenceMetaData md, BatchOutputCollector collector) {
        int begin = md.getBegin();
        int end = md.getEnd();
        for(int i=begin; i<end;i++){
        String sentence = SentenceDB.getSentences()[i];
        collector.emit(new Values(tx,sentence));
    }

    }

    /*
    * 当某一个批完成整个处理之后,会来调用此方法,传入批的编号表明批已经被处理完成
    * 如果之前缓存过批的数据可以在这个方法中将传入的批的编号的缓存的数据进行清理操作


     * */
    @Override
    public void cleanupBefore(BigInteger txid) {

    }


//    在当前Emitter被释放之前调用的方法
//通常可以在这个方法中释放资源
    @Override
    public void close() {

    }
}

package base4;

import backtype.storm.transactional.ITransactionalSpout;

import java.math.BigInteger;

public class SentenceCoordinator implements ITransactionalSpout.Coordinator<SentenceMetaData> {

    private int batchSize=3;
    private boolean hasMore=true;

    /*
    * 当isReady()返回ture时,storm会来调用此方法要求返回一个批的元数据信息
    *这个元数据信息中应该包含足够的信息来组建一个批,并且要保证,之后任何时候需要时通过这个
     *元数据应该可以重新建立一个相同的批,以便于重发
     * 返回的metadata对象需要存储到zk中,所以必须实现序列化反序列化
     * * */
    @Override
    public SentenceMetaData initializeTransaction(BigInteger txid, SentenceMetaData prevMetadata) {

        int begin=prevMetadata==null?0:prevMetadata.getEnd();
        int end=begin+batchSize< SentenceDB.getSentences().length?
                begin+batchSize: SentenceDB.getSentences().length;
        hasMore=end!= SentenceDB.getSentences().length;
        return new SentenceMetaData(begin,end);
    }

    /*
    storm循环调用此方法,询问是否有一个新的批要发
    *返回true表示有,返回false表示没有
    * */
    @Override
    public boolean isReady() {
        return true;
//        return false;
    }

    /*
    * 会在当前Coordinator退出之前调用,用来释放资源
     * */
    @Override
    public void close() {

    }
}

package base4;

import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseTransactionalSpout;
import backtype.storm.tuple.Fields;

import java.util.Map;

public class SentenceSpout extends BaseTransactionalSpout<SentenceMetaData> {
    @Override
    public Coordinator<SentenceMetaData> getCoordinator(Map conf, TopologyContext context) {
        return new SentenceCoordinator();
    }

    @Override
    public Emitter<SentenceMetaData> getEmitter(Map conf, TopologyContext context) {

        return new SentenceEmitter();
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        //预留事务id
        declarer.declare(new Fields("txid","sentence"));
    }
}

package base4;

import backtype.storm.coordination.BatchOutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseBatchBolt;
import backtype.storm.transactional.TransactionAttempt;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;

import java.util.Map;

public class SplitSentenceBolt extends BaseBatchBolt<TransactionAttempt> {

    private BatchOutputCollector collector=null;
    private TransactionAttempt txid=null;
    @Override
    public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector, TransactionAttempt txid) {
        this.collector=collector;
        this.txid=txid;

    }

    @Override
    public void execute(Tuple tuple) {
        String sentence = tuple.getStringByField("sentence");
        String[] words = sentence.split(" ");
        for (String word:words) {
            collector.emit(new Values(txid,word));
        }
    }

    @Override
    public void finishBatch() {

    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("txid","word"));

    }
}

package base4;

import backtype.storm.coordination.BatchOutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseBatchBolt;
import backtype.storm.transactional.TransactionAttempt;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;

import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class WordCountBolt extends BaseBatchBolt<TransactionAttempt>  {
//public class WordCountBolt extends BaseBatchBolt<TransactionAttempt> implements ICombiner<> {//去重的效果 实现这个接口

    private BatchOutputCollector collector=null;
    private TransactionAttempt txid=null;
    @Override
    public void prepare(Map conf, TopologyContext context, BatchOutputCollector collector, TransactionAttempt txid) {
        this.collector=collector;
        this.txid=txid;

    }

    private static Map<String,Integer> map = new ConcurrentHashMap<>();
    private static Set<String> set=new LinkedHashSet<>();
    @Override
    public void execute(Tuple tuple) {
        String word = tuple.getStringByField("word");
        map.put(word,map.containsKey(word)?map.get(word)+1:1);
//        collector.emit(new Values(word,map.get(word)));
        set.add(word);
    }


    private static int lastId=0;
    //bug
    ///lastId时静态的,以下方法需加锁-----难实现(线程所好解决,进程锁难解决)
    // ---lastId存到redis----集群模式下
    @Override
    public void finishBatch() {
        if(txid.getTransactionId().intValue() > lastId) {

            for (String word : set) {
                collector.emit(new Values(txid, word, map.get(word)));
            }
            lastId=txid.getTransactionId().intValue();
        }else {
            System.err.println("发现重发数据。。抛弃。。");

        }

    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("txid","word","count"));
    }
}

package base4;

import backtype.storm.coordination.BatchOutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.topology.base.BaseBatchBolt;
import backtype.storm.transactional.TransactionAttempt;
import backtype.storm.tuple.Tuple;

import java.util.Map;

public class ReportBolt extends BaseBatchBolt<TransactionAttempt> {

    private TransactionAttempt txid=null;
    @Override
    public void prepare(Map map, TopologyContext topologyContext, BatchOutputCollector batchOutputCollector, TransactionAttempt txid) {
        this.txid=txid;
    }

    @Override
    public void execute(Tuple tuple) {
        String word = tuple.getStringByField( "word" );
        int count = tuple.getIntegerByField( "count");
        System.out.println("--["+txid.getTransactionId()+"--["+word+"~"+count+"]--");

    }

    @Override
    public void finishBatch() {

    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {

    }
}

package base4;

import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.generated.StormTopology;
import backtype.storm.transactional.TransactionalTopologyBuilder;
import backtype.storm.tuple.Fields;

public class SentenceTopology {
    public static void main(String[] args) throws Exception {
        //1.创建组件
        SentenceSpout sentenceSpout = new SentenceSpout();
        SplitSentenceBolt splitsentenceBolt = new SplitSentenceBolt();
        WordCountBolt wordCountBolt = new WordCountBolt();
        ReportBolt reportBolt = new ReportBolt();

        //2.创建拓扑构建者
        TransactionalTopologyBuilder builder
                =new TransactionalTopologyBuilder("WC_Topology","Sentence_Spout",sentenceSpout);

        builder.setBolt("Split_Sentence_Bolt", splitsentenceBolt).shuffleGrouping("Sentence_Spout");
        //setCommitterBolt  实现去重
        builder.setCommitterBolt("Word_Count_Bolt", wordCountBolt).fieldsGrouping("Split_Sentence_Bolt",new Fields("word"));
        //        builder.setBolt("Word_Count_Bolt", wordCountBolt).fieldsGrouping("Split_Sentence_Bolt",new Fields("word"));
        builder.setBolt( "Report_Bolt", reportBolt).globalGrouping("Word_Count_Bolt");

        //3.创建拓扑
        StormTopology topology = builder.buildTopology();

        //4.提交拓扑到集群中运行
//        Config conf = new Config();
//        StormSubmitter.submitTopology("WC.Topology",conf,topology);


        //5.本地模拟运行
        LocalCluster cluster = new LocalCluster();
        Config conf = new Config();
        cluster.submitTopology("WC_Topology",conf,topology);




    }
}

=======================
安装配置storm集群
提交拓扑到集群

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值