本文来自网易云社区

作者:汪建伟

  • 举个栗子

1 实现的目标

设计一个系统,来实现对一个文本里面的单词出现的频率进行统计。

2 设计Topology结构:

这是一个简单的例子,topology也非常简单。整个topology如下:

整个topology分为三个部分:

WordReader:数据源,负责发送sentence

WordNormalizer:负责将sentence切分

Wordcounter:负责对单词的频率进行累加

3 代码实现

1. 构建maven环境,添加storm依赖

	<repositories>  
      <!-- Repository where we can found the storm dependencies  -->  
      <repository>  
          <id>clojars.org</id>  
          <url>http://clojars.org/repo</url>  
      </repository>  
</repositories>  
<dependencies>  
      <dependency>   
        <groupId>storm</groupId>  
        <artifactId>storm</artifactId>  
        <version>0.7.1</version>  
     </dependency>  
</dependencies>

2. 定义Topology

	public class TopologyMain {  
    public static void main(String[] args) throws InterruptedException {  
           
        //Topology definition  
        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(),1)  
            .fieldsGrouping("word-normalizer", new Fields("word"));  
          
        //Configuration  
        Config conf = new Config();  
        conf.put("wordsFile", args[0]);  
        conf.setDebug(false);  
        //Topology run  
        conf.put(Config.TOPOLOGY_MAX_SPOUT_PENDING, 1);  
        LocalCluster cluster = new LocalCluster();  
        cluster.submitTopology("Getting-Started-Toplogie", conf, builder.createTopology());  
        Thread.sleep(1000);  
        cluster.shutdown();  
    }  
}

3. 实现WordReader Spout

	public class WordReader extends BaseRichSpout {  
  
    private SpoutOutputCollector collector;  
    private FileReader fileReader;  
    private boolean completed = false;  
    public void ack(Object msgId) {  
        System.out.println("OK:"+msgId);  
    }  
    public void close() {}  
    public void fail(Object msgId) {  
        System.out.println("FAIL:"+msgId);  
    }  
  
    public void nextTuple() {  
        if(completed){  
            try {  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
            }  
            return;  
        }  
        String str;  
        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;  
        }  
    }  
  
    public void open(Map conf, TopologyContext context,  
                     SpoutOutputCollector collector) {  
        try {  
            this.fileReader = new FileReader(conf.get("wordsFile").toString());  
        } catch (FileNotFoundException e) {  
            throw new RuntimeException("Error reading file ["+conf.get("wordFile")+"]");  
        }  
        this.collector = collector;  
    }  
  
    public void declareOutputFields(OutputFieldsDeclarer declarer) {  
        declarer.declare(new Fields("line"));  
    }  
}

第一个被调用的spout方法都是public void open(Map conf, TopologyContext context, SpoutOutputCollector collector)。它接收如下参数:配置对象,在定义topology对象是创建;TopologyContext对象,包含所有拓扑数据;还有SpoutOutputCollector对象,它能让我们发布交给bolts处理的数据。

4. 实现WordNormalizer bolt

	public class WordNormalizer extends BaseBasicBolt {  
  
    public void cleanup() {}  
  
    public void execute(Tuple input, BasicOutputCollector collector) {  
        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));  
            }  
        }  
    }  
      
    public void declareOutputFields(OutputFieldsDeclarer declarer) {  
        declarer.declare(new Fields("word"));  
    }  
}

bolt最重要的方法是void execute(Tuple input),每次接收到元组时都会被调用一次,还会再发布若干个元组。

5. 实现WordCounter bolt

	public class WordCounter extends BaseBasicBolt {  
  
    Integer id;  
    String name;  
    Map counters;  
  
    @Override  
    public void cleanup() {  
        System.out.println("-- Word Counter ["+name+"-"+id+"] --");  
        for(Map.Entry entry : counters.entrySet()){  
            System.out.println(entry.getKey()+": "+entry.getValue());  
        }  
    }  
  
    @Override  
    public void prepare(Map stormConf, TopologyContext context) {  
        this.counters = new HashMap();  
        this.name = context.getThisComponentId();  
        this.id = context.getThisTaskId();  
    }  
  
    @Override  
    public void declareOutputFields(OutputFieldsDeclarer declarer) {}  
  
    @Override  
    public void execute(Tuple input, BasicOutputCollector collector) {  
        String str = input.getString(0);  
        if(!counters.containsKey(str)){  
            counters.put(str, 1);  
        }else{  
            Integer c = counters.get(str) + 1;  
            counters.put(str, c);  
        }  
    }  
}

6. 使用本地模式运行Topology

在这个目录下面创建一个文件,/src/main/resources/words.txt,一个单词一行,然后用下面的命令运行这个拓扑:mvn exec:java -Dexec.main -Dexec.args=”src/main/resources/words.txt”。

如果你的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 在这个例子中,每类节点只有一个实例。

  • 附-Storm记录级容错的基本原理

首先来看一下什么叫做记录级容错?storm允许用户在spout中发射一个新的源tuple时为其指定一个message id, 这个message id可以是任意的object对象。多个源tuple可以共用一个message id,表示这多个源 tuple对用户来说是同一个消息单元。storm中记录级容错的意思是说,storm会告知用户每一个消息单元是否在指定时间内被完全处理了。那什么叫做完全处理呢,就是该message id绑定的源tuple及由该源tuple后续生成的tuple经过了topology中每一个应该到达的bolt的处理。举个例子。在图4-1中,在spout由message 1绑定的tuple1和tuple2经过了bolt1和bolt2的处理生成两个新的tuple,并最终都流向了bolt3。当这个过程完成处理完时,称message 1被完全处理了。

在storm的topology中有一个系统级组件,叫做acker。这个acker的任务就是追踪从spout中流出来的每一个message id绑定的若干tuple的处理路径,如果在用户设置的最大超时时间内这些tuple没有被完全处理,那么acker就会告知spout该消息处理失败了,相反则会告知spout该消息处理成功了。在刚才的描述中,我们提到了”记录tuple的处理路径”,如果曾经尝试过这么做的同学可以仔细地思考一下这件事的复杂程度。但是storm中却是使用了一种非常巧妙的方法做到了。在说明这个方法之前,我们来复习一个数学定理。

A xor A = 0.

A xor B…xor B xor A = 0,其中每一个操作数出现且仅出现两次。

storm中使用的巧妙方法就是基于这个定理。具体过程是这样的:在spout中系统会为用户指定的message id生成一个对应的64位整数,作为一个root id。root id会传递给acker及后续的bolt作为该消息单元的唯一标识。同时无论是spout还是bolt每次新生成一个tuple的时候,都会赋予该tuple一个64位的整数的id。Spout发射完某个message id对应的源tuple之后,会告知acker自己发射的root id及生成的那些源tuple的id。而bolt呢,每次接受到一个输入tuple处理完之后,也会告知acker自己处理的输入tuple的id及新生成的那些tuple的id。Acker只需要对这些id做一个简单的异或运算,就能判断出该root id对应的消息单元是否处理完成了。下面通过一个图示来说明这个过程。

上图 spout中绑定message 1生成了两个源tuple,id分别是0010和1011.

上图 bolt1处理tuple 0010时生成了一个新的tuple,id为0110.

上图 bolt2处理tuple 1011时生成了一个新的tuple,id为0111.

上图 bolt3中接收到tuple 0110和tuple 0111,没有生成新的tuple.

容错过程存在一个可能出错的地方,那就是,如果生成的tuple id并不是完全各异的,acker可能会在消息单元完全处理完成之前就错误的计算为0。这个错误在理论上的确是存在的,但是在实际中其概率是极低极低的,完全可以忽略。

相关阅读:流式处理框架storm浅析(上篇)

网易云免费体验馆,0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 如何实现最佳的跨平台游戏体验呢?

流式处理框架storm浅析(下篇)的更多相关文章

  1. 流式处理框架storm浅析(上篇)

    本文来自网易云社区 作者:汪建伟 前言 前一段时间参与哨兵流式监控功能设计,调研了两个可以做流式计算的框架:storm和spark streaming,我负责storm的调研工作.断断续续花了一周的时 ...

  2. 实时流式计算框架Storm 0.9.0发布通知(中文版)

    Storm0.9.0发布通知中文翻译版(2013/12/10 by 富士通邵贤军 有错误一定告诉我 shaoxianjun@hotmail.com^_^) 我们很高兴宣布Storm 0.9.0已经成功 ...

  3. 分布式流式处理框架:storm简介 + Storm术语解释

    简介: Storm是一个免费开源.分布式.高容错的实时计算系统.它与其他大数据解决方案的不同之处在于它的处理方式.Hadoop 在本质上是一个批处理系统,数据被引入 Hadoop 文件系统 (HDFS ...

  4. Storm:分布式流式计算框架

    Storm是一个分布式的.高容错的实时计算系统.Storm适用的场景: Storm可以用来用来处理源源不断的消息,并将处理之后的结果保存到持久化介质中. 由于Storm的处理组件都是分布式的,而且处理 ...

  5. 【流处理】Kafka Stream-Spark Streaming-Storm流式计算框架比较选型

    Kafka Stream-Spark Streaming-Storm流式计算框架比较选型 elasticsearch-head Elasticsearch-sql client NLPchina/el ...

  6. Faust——python分布式流式处理框架

    摘要 Faust是用python开发的一个分布式流式处理框架.在一个机器学习应用中,机器学习算法可能被用于数据流实时处理的各个环节,而不是仅仅在推理阶段,算法也不仅仅局限于常见的分类回归算法,而是会根 ...

  7. Storm:最火的流式处理框架

    伴随着信息科技日新月异的发展,信息呈现出爆发式的膨胀,人们获取信息的途径也更加多样.更加便捷,同时对于信息的时效性要求也越来越高.举个搜索场景中的例子,当一个卖家发布了一条宝贝信息时,他希望的当然是这 ...

  8. 浅谈Storm流式处理框架(转)

    Hadoop的高吞吐,海量数据处理的能力使得人们可以方便地处理海量数据.但是,Hadoop的缺点也和它的优点同样鲜明——延迟大,响应缓慢,运维复杂. 有需求也就有创造,在Hadoop基本奠定了大数据霸 ...

  9. [转载] Storm:最火的流式处理框架

    转载自http://www.cnblogs.com/langtianya/p/5199529.html 伴随着信息科技日新月异的发展,信息呈现出爆发式的膨胀,人们获取信息的途径也更加多样.更加便捷,同 ...

随机推荐

  1. 778. Swim in Rising Water

    ▶ 给定方阵 grid,其元素的值为 D0n-1,代表网格中该点处的高度.现在网格中开始积水,时刻 t 的时候所有值不大于 t 的格点被水淹没,当两个相邻格点(上下左右四个方向)的值都不超过 t 的时 ...

  2. 2.ehcache.xml简介

    转自:https://www.cnblogs.com/crazylqy/p/4238148.html ehcache.xml文件是用来定义Ehcache的配置信息的,更准确的来说它是定义CacheMa ...

  3. Nginx rewrite使用

    转自: https://www.cnblogs.com/czlun/articles/7010604.html

  4. j2ee常用jar包

    [b]activation.jar:[/b]与javaMail有关的jar包,使用javaMail时应与mail.jar一起加入到lib中去,具体负责mail的数据源和类型等 [b]ajaxtags- ...

  5. Git--时光穿梭机之删除文件06

    在Git中,删除也是一个修改操作,我们实际操作一下吧,先添加一个文件test.txt到Git并且提交 $ git add test.txt $ git commit -m "add test ...

  6. Eclipse Class Decompiler——Java反编译插件(转)

    Eclipse Class Decompiler是一款Eclipse插件,整合了多种反编译器,和Eclipse Class Viewer无缝集成,能够很方便的使用插件查看类库源码,进行Debug调试. ...

  7. 2015年传智播客JavaEE 第168期就业班视频教程17-登录功能业务逻辑实现(代码)

    点击红色在业务层接口EmpEbi创建方法login 按F4弹出类继承层次视图 这些快捷键是条件反射了. 业务层做MD5数据加密,不能放在表现层也不能放在数据层必须放在业务层.它属于业务操作. 数据层的 ...

  8. python selenium 测试环境的搭建及python mysql的连接

    又来一篇傻瓜教程啦,防止在学习的小伙伴们走弯路. 1.python 环境搭建 python官网:https://www.python.org/downloads/  选择最新版本python下载(如果 ...

  9. basicHttpBinding

    表示一个绑定,Windows Communication Foundation (WCF) 服务可以使用此绑定配置和公开能够与基于 ASMX 的 Web 服务和客户端通信的终结点以及符合 WS-I B ...

  10. shiro 和spring集合 实现登录时输入验证码并校验(七)

    编写实现验证码的主体实现类:CaptchaCode import java.util.UUID; import javax.servlet.http.HttpServletRequest; impor ...