本文来自网易云社区

作者:汪建伟

  • 举个栗子

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. SQL中INNER JOIN的用法

    SQL join 用于根据两个或多个表中的列之间的关系,从这些表中查询数据. Join 和 Key 有时为了得到完整的结果,我们需要从两个或更多的表中获取结果.我们就需要执行 join. 数据库中的表 ...

  2. Eclipse apk 签名

    Eclipse apk 签名 project>right mouse click>android tool>Export signed Application package cre ...

  3. Spring Boot实践——统一异常处理

    注解说明 @ControllerAdvice,是Spring3.2提供的新注解,从名字上可以看出大体意思是控制器增强.让我们先看看@ControllerAdvice的实现: /** * Special ...

  4. c# 后台调用接口接收传过来的json

    public string GetRequestTest(string url) { HttpWebRequest httpWebRequest = (HttpWebRequest)WebReques ...

  5. //todo 的用处

    在代码中添加 //todo 以后要做的事 可以暂时打上标记,以后再来处理. 光有这个没什么用,关键是IDE要支持,比如VS2017,只要按下 ctrl+w,t 就可以在输出窗口中显示出所有 todo的 ...

  6. eclipse双击变量高亮显示开关

    在eclipse/myeclipse中如果不小心把变量的高亮显示弄丢了.可真是件愁人的事,不过看到这你就不用愁了 windows->   preferences-> java-> E ...

  7. Python运维开发基础06-语法基础

    上节作业回顾 (讲解+温习120分钟) #!/usr/bin/env python3 # -*- coding:utf-8 -*- # author:Mr.chen # 添加商家入口和用户入口并实现物 ...

  8. Mybatis自动生成xml文件、dao接口、实体类

    Mybatis可以通过逆向工程,实现自动生成xml文件.dao接口.实体类 以下使用的是Intellij Idea进行自动生成 一.首先,要在pom.xml中导入插件,在<build>中加 ...

  9. ORA-01795: maximum number of expressions in a list is 1000

     今天发现查询Oracle用In查询In的元素不可以超过1000个还需要分成多个1000查询记录博客备忘!! Load Test的时候发现这么如下这个错误.... ORA-01795: maximum ...

  10. 33-wxpython多个frame之间的信息共享

    https://blog.csdn.net/xyisv/article/details/78576932 https://blog.csdn.net/tianmaxingkong_/article/d ...