Storm入门程序wordCount

1.1、storm是什么

        storm是twitter公司开源贡献给apache的一款实时流式处理的一个开源软件,主要用于解决数据的实时计算以及实时的处理等方面的问题

1.2、storm的特点

        Storm是一个开源的分布式实时计算系统,可以简单、可靠的处理大量的数据流。Storm有很多使用场景:如实时分析,在线机器学习,持续计算,分布式RPC,ETL等等。Storm支持水平扩展,具有高容错性,保证每个消息都会得到处理,而且处理速度很快(在一个小集群中,每个结点每秒可以处理数以百万计的消息)。Storm的部署和运维都很便捷,而且更为重要的是可以使用任意编程语言来开发应用。
Storm有如下特点:

  • 编程模型简单

        在大数据处理方面相信大家对hadoop已经耳熟能详,基于Google Map/Reduce来实现的Hadoop为开发者提供了map、reduce原语,使并行批处理程序变得非常地简单和优美。同样,Storm也为大数据的实时计算提供了一些简单优美的原语,这大大降低了开发并行实时处理的任务的复杂性,帮助你快速、高效的开发应用。

  • 可扩展

        在Storm集群中真正运行topology的主要有三个实体:工作进程、线程和任务。Storm集群中的每台机器上都可以运行多个工作进程,每个工作进程又可创建多个线程,每个线程可以执行多个任务,任务是真正进行数据处理的实体,我们开发的spout、bolt就是作为一个或者多个任务的方式执行的。
因此,计算任务在多个线程、进程和服务器之间并行进行,支持灵活的水平扩展。

  • 高可靠性

        Storm可以保证spout发出的每条消息都能被“完全处理”,这也是直接区别于其他实时系统的地方,如S4。
请注意,spout发出的消息后续可能会触发产生成千上万条消息,可以形象的理解为一棵消息树,其中spout发出的消息为树根,Storm会跟踪这棵消息树的处理情况,只有当这棵消息树中的所有消息都被处理了,Storm才会认为spout发出的这个消息已经被“完全处理”。如果这棵消息树中的任何一个消息处理失败了,或者整棵消息树在限定的时间内没有“完全处理”,那么spout发出的消息就会重发。
        考虑到尽可能减少对内存的消耗,Storm并不会跟踪消息树中的每个消息,而是采用了一些特殊的策略,它把消息树当作一个整体来跟踪,对消息树中所有消息的唯一id进行异或计算,通过是否为零来判定spout发出的消息是否被“完全处理”,这极大的节约了内存和简化了判定逻辑,后面会对这种机制进行详细介绍。
        这种模式,每发送一个消息,都会同步发送一个ack/fail,对于网络的带宽会有一定的消耗,如果对于可靠性要求不高,可通过使用不同的emit接口关闭该模式。
       上面所说的,Storm保证了每个消息至少被处理一次,但是对于有些计算场合,会严格要求每个消息只被处理一次,幸而Storm的0.7.0引入了事务性拓扑,解决了这个问题,后面会有详述。

  • 高容错性

         如果在消息处理过程中出了一些异常,Storm会重新安排这个出问题的处理单元。Storm保证一个处理单元永远运行(除非你显式杀掉这个处理单元)。
当然,如果处理单元中存储了中间状态,那么当处理单元重新被Storm启动的时候,需要应用自己处理中间状态的恢复。

  • 支持多种编程语言

        除了用java实现spout和bolt,你还可以使用任何你熟悉的编程语言来完成这项工作,这一切得益于Storm所谓的多语言协议。多语言协议是Storm内部的一种特殊协议,允许spout或者bolt使用标准输入和标准输出来进行消息传递,传递的消息为单行文本或者是json编码的多行。
         Storm支持多语言编程主要是通过ShellBolt, ShellSpout和ShellProcess这些类来实现的,这些类都实现了IBolt 和 ISpout接口,以及让shell通过java的ProcessBuilder类来执行脚本或者程序的协议。
        可以看到,采用这种方式,每个tuple在处理的时候都需要进行json的编解码,因此在吞吐量上会有较大影响。

  • 支持本地模式

         Storm有一种“本地模式”,也就是在进程中模拟一个Storm集群的所有功能,以本地模式运行topology跟在集群上运行topology类似,这对于我们开发和测试来说非常有用。

  • 高效

 

 

2、storm的架构模型

 

 

  1. Nimbus:负责资源分配和任务调度。新版本中的nimbus节点可以有多个,做主备
  2. Supervisor:负责接受nimbus分配的任务,启动和停止属于自己管理的worker进程。
  3. Worker:运行具体处理组件逻辑的进程。
  4. Task:worker中每一个spout/bolt的线程称为一个task. 在storm0.8之后,task不再与物理线程对应,同一个spout/bolt的task可能会共享一个物理线程,该线程称为executor。最新版本Jstorm已经废除了task的概念

3 storm的编程模型

    DataSource:外部数据源
    Spout:接受外部数据源的组件,将外部数据源转化成Storm内部的数据,以Tuple为基本的传输单元下发给Bolt
    Bolt:接受Spout发送的数据,或上游的bolt的发送的数据。根据业务逻辑进行处理。发送给下一个Bolt或者是存储到某种介质上。介质可以是mongodb或mysql,或者其他。
    Tuple:Storm内部中数据传输的基本单元,里面封装了一个List对象,用来保存数据。
   

4 storm的入门程序

实现单次计数的统计

1 pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>bigdata</artifactId>
        <groupId>cn.feilix.bigdata</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>storm</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.apache.storm</groupId>
            <artifactId>storm-core</artifactId>
            <version>1.1.1</version>
            <!--
                     test
                     provided  编译的时候需要,打包的时候不要
                     compile
            默认
                     -->
            <!-- 本地模式注释掉这行 -->
            <!--<scope>provided</scope>-->
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

TopologyMain.java

package com.felix.wordcount;

import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.generated.AlreadyAliveException;
import org.apache.storm.generated.AuthorizationException;
import org.apache.storm.generated.InvalidTopologyException;
import org.apache.storm.generated.StormTopology;
import org.apache.storm.topology.TopologyBuilder;

import java.util.Map;

public class TopologyMain {

    public static void main(String[] args) throws InvalidTopologyException, AuthorizationException, AlreadyAliveException {

        //将我们的spout与bolt组织起来成为一个topology提交运行
        //通过TopologyBuilder 来组织我们的spout与bolt
        TopologyBuilder builder = new TopologyBuilder();

        builder.setSpout("RandomSpout",new RandomSpout());

        builder.setBolt("SplitBolt",new SplitBolt()).shuffleGrouping("RandomSpout");

        builder.setBolt("CountBolt",new CountBolt()).shuffleGrouping("SplitBolt");

        StormTopology topology = builder.createTopology();
        Config config = new Config();
        if(args!=null && args.length>0){
            //集群提交模式,打包到集群上面去的时候,用这种方式进行提交
            StormSubmitter.submitTopology(args[0],config,topology);

        }else{
            //本地运行模式  注意吧provide注释掉
            LocalCluster cluster = new LocalCluster();
            cluster.submitTopology("wordCount",config,topology);

        }

    }


}
 

 

RandomSpout.java

package com.felix.wordcount;

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;
import java.util.Random;

/**
 *
 */
public class RandomSpout extends BaseRichSpout {
    private SpoutOutputCollector collector;
    private  String[]  arrays;
    private Random random;

    /**
     * 只执行一次
     * @param conf
     * @param context
     * @param collector
     */
    @Override
    public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
        random = new Random();
        arrays = new String[]{"hello world","hadoop hive","hello kitty","sqoop hadoop"};
        this.collector = collector;
    }

    /**
     * 这个方法会反复不断的一直被执行,在这个方法里面,将我们的数据往下游发送,通过 SpoutOutputCollector
     * 将我们的数据往下游发送
     */
    @Override
    public void nextTuple() {
        //通过调用emit方法,将我们的数据往外发送,需要接受一个list类型的参数
        //values继承了arrayList   就是一个list
        //这里面包的就是我们需要往下游发送的数据r

        int i = random.nextInt(arrays.length);
        String line = arrays[i];

        Values values = new Values(line);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //往下游发送
        collector.emit(values);


    }

    /**
     * 为我们往下游发送的单词申明一个字符串
     * 下游获取单词的时候,可以直接通过这个字符串获取
     * @param declarer
     */
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {

        Fields fields = new Fields("hello");//相当于给数据加一个标签
        declarer.declare(fields);

    }

}
 

 

SplitBolt.java

package com.felix.wordcount;

import org.apache.storm.topology.BasicOutputCollector;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseBasicBolt;
import org.apache.storm.tuple.Fields;
import org.apache.storm.tuple.Tuple;
import org.apache.storm.tuple.Values;

/**
 *  * bolt也需要继承一个类,表示我们是一个规范的bolt组件
 * bolt用于继承BaseBasicBolt 不要继承BaseRichBolt
 */
public class SplitBolt extends BaseBasicBolt {


    /**
     * 这个方法也会反复不断的被调用,只要有上游发送的数据,这个方法就会执行
     *
     * @param tuple :上游发送的数据,都包在这个tuple里面了,我们可以从tuple当中获取上游发送的数据
     * @param collector:往下游发送数据的
     */
    @Override
    public void execute(Tuple tuple, BasicOutputCollector collector) {

        Object o = tuple.getValueByField("hello");//根据标签获取上游传过来的值
        String v = o.toString();
        String[] split = v.split(" ");//切分单词
        for (String word: split) {

            Values values = new Values(word, 1);

            collector.emit(values);

        }

    }

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

CountBolt.java

package com.felix.wordcount;

import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.BasicOutputCollector;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseBasicBolt;
import org.apache.storm.tuple.Tuple;

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

public class CountBolt extends BaseBasicBolt {

    private static ConcurrentHashMap<String,Integer> map ;


    /**
     * 只执行一次
     * @param stormConf
     * @param context
     */
    @Override
    public void prepare(Map stormConf, TopologyContext context) {

        map = new ConcurrentHashMap<>();

    }

    @Override
    public void execute(Tuple input, BasicOutputCollector collector) {

        String word = input.getValueByField("word").toString();
        String nums = input.getValueByField("nums").toString();
        Integer num = Integer.parseInt(nums);

        if(map.containsKey(word)){
            //已经存在 加1
            map.put(word,map.get(word)+num);

        }else{
            //不存在
            //放进去
            map.put(word,1);
        }

        //打印map
        System.out.println(map);

    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {

    }
}
 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章