【Storm篇】Storm的基本概念及使用

第一部分:Storm简介
一、特征
Storm进程常驻内存,数据不经过磁盘,在内存中处理。(通过kill 方式结束进程)
架构:

Nimbus、supervisor、worker
分别类似于:master、follower、process
其编程模型:
DAG以及spout、bolt
数据传输:ZMQ(twitter早期产品)
Netty

Storm是可维护的,通过StormUI图形化监控接口。

流式处理(异步 与 同步)
异步:客户端提交数据进行结算,并不会等待数据计算结果
这里写图片描述
中间的MQ可为kafaka,storage可为DB,

同步:客户端提交数据请求之后,立刻取得计算结果并返回给客户端

Drpc
这里写图片描述

Storm:纯流式处理
Spark Streaming:微批处理
MapReduce:批处理
三者之间的关系
Storm:进程、线程常驻内存运行,数据不进入磁盘,数据通过网络传递。
MapReduce:为TB、PB级别数据设计的批处理计算框架。

这里写图片描述

Storm:纯流式处理
专门为流式处理设计
数据传输模式更为简单,很多地方也更为高效
并不是不能做批处理,它也可以来做微批处理,来提高吞吐
Spark Streaming:微批处理
将RDD做的很小来用小的批处理来接近流式处理
基于内存和DAG可以把处理任务做的很快
这里写图片描述

二、storm 计算模型
基本概念:
Topology – DAG有向无环图的实现
Tuple:Stream中最小数据组成单元
Stream – 数据流(spout与bolt以及bolt之间的管道)

Spout – 数据源

扫描二维码关注公众号,回复: 59589 查看本文章

1.从指定外部的数据源读取元组(Tuple)发送到拓扑(Topology)中
2.一个Spout可以发送多个数据流(Stream)
3.Spout中最核心的方法是nextTuple,该方法会被Storm线程不断调用、主动从数据源拉取数据,再通过emit方法将数据生成元组(Tuple)发送给之后的Bolt计算

Bolt – 数据流处理组件

1.拓扑中数据处理均有Bolt完成。对于简单的任务或者数据流转换,单个Bolt可以简单实现;更加复杂场景往往需要多个Bolt分多个步骤完成
2.一个Bolt可以发送多个数据流(Stream)
3.Bolt中最核心的方法是execute方法,该方法负责接收到一个元组(Tuple)数据、真正实现核心的业务逻辑

以下图解说明storm的计算过程:
这里写图片描述
备注:水龙头就是spout端,发送数据流,到bolt端。bolt可以有多个。不同的分发策略对应着不同的线程处理方式。详见下方的Storm Grouping

如下以简单的wordcount为例
这里写图片描述

storm:从spout端把收集的数据通过tuple最小的数据单元发出,经过bolt主要的逻辑处理。如果后续还有操作,还可以设置bolt。

三、Storm Grouping – 数据流分组(即数据分发策略)

1.Shuffle Grouping
随机分组,随机派发stream里面的tuple,保证每个bolt task接收到的tuple数目大致相同。
轮询,平均分配
2.Fields Grouping
按字段分组,比如,按”user-id”这个字段来分组,那么具有同样”user-id”的 tuple 会被分到相同的Bolt里的一个task, 而不同的”user-id”则可能会被分配到不同的task
3.All Grouping
广播发送,对于每一个tuple,所有的bolts都会收到
4.Global Grouping
全局分组,把tuple分配给task id最低的task 。
5.None Grouping
不分组,这个分组的意思是说stream不关心到底怎样分组。目前这种分组和Shuffle grouping是一样的效果。 有一点不同的是storm会把使用none grouping的这个bolt放到这个bolt的订阅者同一个线程里面去执行(未来Storm如果可能的话会这样设计)。
6.Direct Grouping
指向型分组, 这是一种比较特别的分组方法,用这种分组意味着消息(tuple)的发送者指定由消息接收者的哪个task处理这个消息。只有被声明为 Direct Stream 的消息流可以声明这种分组方法。而且这种消息tuple必须使用 emitDirect 方法来发射。消息处理者可以通过 TopologyContext 来获取处理它的消息的task的id (OutputCollector.emit方法也会返回task的id)
7.Local or shuffle grouping
本地或随机分组。如果目标bolt有一个或者多个task与源bolt的task在同一个工作进程中,tuple将会被随机发送给这些同进程中的tasks。否则,和普通的Shuffle Grouping行为一致
8.customGrouping
自定义,相当于mapreduce那里自己去实现一个partition一样。

使用较多的前2个。

Demo实现求和以及wordcount
以下为简单的累加的实现:
WSpout.java

/**
 * 
 备注:此为storm计算累加的简单案例的spout的类
 */
public class WSpout extends BaseRichSpout {

    Map map;

    TopologyContext context;

    SpoutOutputCollector collector;

    int i = 0;

    /**
     * 初始设置配置
     */
    @Override
    public void open(Map map, TopologyContext context, SpoutOutputCollector collector) {
        this.map = map;
        this.context = context;
        this.collector = collector;
    }

    /**
     * 被线程循环调用
     */
    @Override
    public void nextTuple() {
        i++;
        List num = new Values(i);
        this.collector.emit(num);

        System.err.println("storm======" + i);

        Utils.sleep(1000);

    }

    /**
     * 申明 申明发送的数据 bolt 通过申明来拿到数据
     */
    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {

        declarer.declare(new Fields("n"));
    }
}

Wbolt.java

public class Wbolt extends BaseRichBolt {
    Map map;
    TopologyContext contex;
    OutputCollector collector;
    int sum = 0;

    @Override
    public void prepare(Map map, TopologyContext context, OutputCollector collector) {
        this.collector = collector;
        this.contex = context;
        this.map = map;
    }

    @Override
    public void execute(Tuple input) {
        int i = input.getIntegerByField("n");
        sum += i;

        System.err.println(sum);
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        // TODO:后续无操作则不进行处理
    }

Test.java

/**
 * 异步的方式,所谓异步也就是线程对CPU资源抢占的过程。谁快谁先拿到资源。
 * 如果同步了,那单线程线性执行流程,第一个完成第二个完成。这样CPU资源可能浪费,有时候还会出现阻塞的现象。 
 */
public class Test {
    public static void main(String[] args) {
        // 使用构建者模式构建来构建当前的storm的拓扑结构
        TopologyBuilder tb = new TopologyBuilder();
        tb.setSpout("wsspout", new WSpout());
        tb.setBolt("wsbolt", new Wbolt()).shuffleGrouping("wsspout");

        // 设置storm集群运行方式
        LocalCluster loc = new LocalCluster();
        loc.submitTopology("wsum", new Config(), tb.createTopology());
    }
}

以下为wordcount实例:
WcSpout.java

public class WcSpout extends BaseRichSpout {

    Map map;

    TopologyContext context;

    SpoutOutputCollector collcoter;

    String[] text = { "chen shi xing kong", "tian chen", "chen shi" };

    Random random = new Random();

    @Override
    public void open(Map map, TopologyContext context, SpoutOutputCollector collcoter) {
        this.map = map;
        this.context = context;
        this.collcoter = collcoter;
    }

    @Override
    public void nextTuple() {
        List line = new Values(text[random.nextInt(text.length)]);
        this.collcoter.emit(line);
        System.out.println("spout emit -----" + line);
        Utils.sleep(1000);

    }

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

WsplitBolt.java

public class WsplitBolt extends BaseRichBolt {

    OutputCollector collector;

    @Override
    public void prepare(Map map, TopologyContext context, OutputCollector collector) {

        this.collector = collector;

    }

    @Override
    public void execute(Tuple input) {
        String line = input.getString(0);
        String[] words = line.split(" ");
        for (String word : words) {
            List wd = new Values(word);
            this.collector.emit(wd);
        }

    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {

        declarer.declare(new Fields("w"));
    }

WcountBolt.java

public class WcountBolt extends BaseRichBolt {

    Map map = new HashMap<String, Integer>(); // key:单词 value:个数

    @Override
    public void prepare(Map arg0, TopologyContext arg1, OutputCollector arg2) {

    }

    @Override
    public void execute(Tuple input) {
        String word = input.getString(0);

        // 定义初始值
        int count = 1;
        // 若单词已出现,则获取该单词出现的次数,并让count累加
        if (map.containsKey(word)) {
            count = (int) map.get(word) + 1;
        }
        map.put(word, count);
        // 展示单词及其出现的次数

        System.err.println("word=" + word + "-------" + "num=" + count);

    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer arg0) {

    }

猜你喜欢

转载自blog.csdn.net/chenshi_2753/article/details/80041924