Hadoop生态圈(十一):Storm

目录

1 Storm概述

1.1 离线计算是什么?

1.2 流式计算是什么?

1.3 Storm是什么?

1.4 Storm与Hadoop的区别

1.5 Storm应用场景及行业案例

1.5.1 运用场景

1.5.2 典型案列

2 Storm基础知识

2.1 Storm编程模型

2.2 Storm核心组件

2.3 实时流计算常见框架图

3 Storm集群搭建

3.1 环境准备

3.2 Storm集群搭建

3.3 启动集群

3.4 Storm命令行操作

4 常用API

4.1 API简介

4.1.1 Component组件(Spout和Bolt)

4.1.2 Spout(水龙头)

4.1.3 Bolt(转接头)

4.1.4 spout的tail特性

4.2 网站日志处理案例

4.2.1 环境准备

4.2.2 需求1

4.2.3 需求2:动态增加日志,查看控制台打印信息(tail特性)

5 分组策略和并发度

5.1 分组策略(Stream Grouping)

5.2 并发度

5.3 实时单词统计案例


1 Storm概述

1.1 离线计算是什么?

离线计算:批量获取数据、批量传输数据、周期性批量计算数据、数据展示

代表技术:Sqoop批量导入数据、HDFS批量存储数据、MapReduce批量计算数据、Hive批量计算数据

1.2 流式计算是什么?

流式计算:数据实时产生、数据实时传输、数据实时计算、实时展示

代表技术:Flume实时获取数据、Kafka实时数据存储、Storm/JStorm实时数据计算、Redis实时结果缓存、持久化存储(mysql)。

离线计算与实时计算最大的区别:实时收集、实时计算、实时展示

实时流处理架

Hadoop生态圈(十一):Storm_第1张图片

 

1.3 Storm是什么?

Storm是一个分布式计算框架,主要使用Clojure与Java语言编写,最初是由Nathan Marz带领Backtype公司团队创建,在Backtype公司被Twitter公司收购后进行开源。最初的版本是在2011年9月17日发行,版本号0.5.0。

2013年9月,Apache基金会开始接管并孵化Storm项目。Apache Storm是在Eclipse Public License下进行开发的,它提供给大多数企业使用。经过1年多时间,2014年9月,Storm项目成为Apache的顶级项目。

Storm是一个免费开源的分布式实时计算系统。Storm能轻松可靠地处理无界的数据流,就像Hadoop对数据进行批处理;

1.4 Storm与Hadoop的区别

1)Storm用于实时计算,Hadoop用于离线计算。

2)Storm处理的数据保存在内存中,源源不断;Hadoop处理的数据保存在文件系统中,一批一批处理(批处理)

3)Storm的数据通过网络传输进来;Hadoop的数据保存在磁盘中。

1.5 Storm应用场景及行业案例

Storm用来实时计算源源不断产生的数据,如同流水线生产。

1.5.1 运用场景

Storm能用到很多场景中,包括:实时分析、在线机器学习、连续计算等。

1)推荐系统:实时推荐,根据下单或加入购物车推荐相关商品

2)金融系统:实时分析股票信息数据

3)预警系统:根据实时采集数据,判断是否到了预警阈值。

4)网站统计:实时销量、流量统计,如淘宝双11效果图

1.5.2 典型案列

1)京东-实时分析系统:实时分析用户的属性,并反馈给搜索引擎

最初,用户属性分析是通过每天在云上定时运行的MR job来完成的。为了满足实时性的要求,希望能够实时分析用户的行为日志,将最新的用户属性反馈给搜索引擎,能够为用户展现最贴近其当前需求的结果。

Hadoop生态圈(十一):Storm_第2张图片

2)携程-网站性能监控:实时分析系统监控携程网的网站性能

利用HTML5提供的performance标准获得可用的指标,并记录日志。Storm集群实时分析日志和入库。使用DRPC聚合成报表,通过历史数据对比等判断规则,触发预警事件。

Hadoop生态圈(十一):Storm_第3张图片

3)淘宝双十一:实时统计销售总额

Hadoop生态圈(十一):Storm_第4张图片

2 Storm基础知识

2.1 Storm编程模型

Hadoop生态圈(十一):Storm_第5张图片

 

编程模型中组件介绍:

1. 元组(Tuple)

元组(Tuple),是消息传递的基本单元,是一个命名的值列表,元组中的字段可以是任何类型的对象。Storm使用元组作为其数据模型,元组支持所有的基本类型、字符串和字节数组作为字段值,只要实现类型的序列化接口就可以使用该类型的对象。元组本来应该是一个key-value的Map,但是由于各个组件间传递的元组的字段名称已经事先定义好,所以只要按序把元组填入各个value即可,所以元组是一个value的List。

2. 流(Stream)

流是Storm的核心抽象,是一个无界的元组系列。源源不断传递的元组就组成了流,在分布式环境中并行地进行创建和处理。

3. 水龙头(Spout

Spout是拓扑的流的来源,是一个拓扑中产生源数据流的组件。通常情况下,Spout会从外部数据源中读取数据,然后转换为拓扑内部的源数据。

Spout可以是可靠的,也可以是不可靠的。如果Storm处理元组失败,可靠的Spout能够重新发射,而不可靠的Spout就尽快忘记发出的元组。

Spout可以发出超过一个流。

Spout的主要方法是nextTuple()。NextTuple()会发出一个新的Tuple到拓扑,如果没有新的元组发出,则简单返回。

Spout的其他方法是ack()和fail()。当Storm检测到一个元组从Spout发出时,ack()和fail()会被调用,要么成功完成通过拓扑,要么未能完成。Ack()和fail()仅被可靠的Spout调用。

IRichSpout是Spout必须实现的接口。

4. 转接头(Bolt

在拓扑中所有处理都在Bolt中完成,Bolt是流的处理节点,从一个拓扑接收数据,然后执行进行处理的组件。Bolt可以完成过滤、业务处理、连接运算、连接与访问数据库等任何操作。

Bolt是一个被动的角色,接口中有一个execute()方法,在接收到消息后会调用此方法,用户可以在其中执行自己希望的操作。

Bolt可以完成简单的流的转换,而完成复杂的流的转换通常需要多个步骤,因此需要多个Bolt。

Bolt可以发出超过一个的流。

5. 拓扑(Topology

拓扑(Topology)是Storm中运行的一个实时应用程序,因为各个组件间的消息流动而形成逻辑上的拓扑结构。

把实时应用程序的运行逻辑打成jar包后提交到Storm的拓扑。Storm的拓扑类似于MapReduce的作业(Job)。其主要的区别是,MapReduce的作业最终会完成,而一个拓扑永远都在运行直到它被杀死。一个拓扑是一个图的Spout和Bolt的连接流分组。

2.2 Storm核心组件

Hadoop生态圈(十一):Storm_第6张图片

 

nimbus是整个集群的控管核心,负责topology的提交、运行状态监控、任务重新分配等工作。

zk就是一个管理者,监控者。

总体描述:nimbus下命令(分配任务),zk监督执行(心跳监控,worker、supurvisor的心跳都归它管),supervisor领旨(下载代码),招募人马(创建worker和线程等),worker、executor就给我干活!task就是具体要干的活。

1 主控节点与工作节点

另外一种对Storm集群中节点的说法而已:从它是M/S结构而言的,不要和上面的核心组件混着认识。

Storm集群中有两类节点:主控节点(Master Node)和工作节点(Worker Node)。其中,主控节点只有一个,而工作节点可以有多个。

2 Nimbus进程与Supervisor进程

主控节点运行一个称为Nimbus的守护进程。Nimbus负责在集群中分发代码,对节点分配任务,并监视主机故障。

每个工作节点运行一个称为Supervisor的守护进程。Supervisor监听其主机上已经分配的主机的作业,启动和停止Nimbus已经分配的工作进程。

3 流分组Stream grouping)

流分组,是拓扑定义中的一部分,为每个Bolt指定应该接收哪个流作为输入。流分组定义了流/元组如何在Bolt的任务之间进行分发。Storm内置了8种流分组方式。

4 工作进程(Worker

Worker是Spout/Bolt中运行具体处理逻辑的进程。一个worker就是一个进程,进程里面包含一个或多个线程

5 执行器(Executor

一个线程就是一个executor,一个线程会处理一个或多个任务

6 任务(Task)

一个任务就是一个task

2.3 实时流计算常见框架图

1)Flume获取数据。

2)Kafka临时保存数据。

3)Storm计算数据。

4)Redis是个内存数据库,用来保存数据

3 Storm集群搭建

3.1 环境准备

hadoop101 hadoop102 hadoop103
zookeeper zookeeper zookeeper
storm storm storm

3.2 Storm集群搭建

(1)下载:

官方网站:http://storm.apache.org/downloads.html

网盘链接:请点这里        提取码:4aez 
(2)上传并安装

1)将下载的storm安装包上传到hadoop101的/opt/software/目录下,配置完成后再进行分发

2)解压安装包到指定的目录下

[root@hadoop101 software]$ tar -zxvf apache-storm-1.1.0.tar.gz -C /opt/module/

3)在storm安装目录下创建data文件夹

[root@hadoop101 apache-storm-1.1.0]# mkdir data

4)修改conf/目录下的配置文件(Storm的配置文件非常严格,多或少一个空格都不可以)

修改storm.yaml配置文件

# 设置Zookeeper的主机名称

storm.zookeeper.servers:
     - "hadoop101"
     - "hadoop102"
     - "hadoop103"

# 设置主节点的主机名称

nimbus.seeds: ["hadoop101", "hadoop102", "hadoop103"]

Hadoop生态圈(十一):Storm_第7张图片

# 设置Storm的数据存储路径

storm.local.dir: "/opt/module/apache-storm-1.1.0/data"

# 设置Worker的端口号

supervisor.slots.ports:
    - 6700
    - 6701
    - 6702
    - 6703

Hadoop生态圈(十一):Storm_第8张图片

5)将配置好的Storm分发到其他节点

3.3 启动集群

(1)启动集群

1)后台启动nimbus

[root@hadoop101 storm]$ bin/storm nimbus &

[root@hadoop102 storm]$ bin/storm nimbus &

[root@hadoop103 storm]$ bin/storm nimbus &

2)后台启动supervisor

[root@hadoop101 storm]$ bin/storm supervisor &

[root@hadoop102 storm]$ bin/storm supervisor &

[root@hadoop103 storm]$ bin/storm supervisor &

3)启动Storm ui

[root@hadoop101 storm]$ bin/storm ui

(2)通过浏览器查看集群状态

Hadoop生态圈(十一):Storm_第9张图片

 

(3)Storm日志信息查看

1)查看nimbus的日志信息

在nimbus的服务器上

cd /opt/module/apache-storm-1.1.0/logs

tail -100f /opt/module/apache-storm-1.1.0/logs/nimbus.log

2)查看ui运行日志信息

在ui的服务器上,一般和nimbus一个服务器

cd /opt/module/apache-storm-1.1.0/logs

tail -100f /opt/module/apache-storm-1.1.0/logs/ui.log

3)查看supervisor运行日志信息

在supervisor服务上

cd /opt/module/apache-storm-1.1.0/logs

tail -100f  /opt/module/apache-storm-1.1.0/logs/supervisor.log

4)查看supervisor上worker运行日志信息

在supervisor服务上

cd /opt/module/apache-storm-1.1.0/logs

tail -100f  /opt/module/apache-storm-1.1.0/logs/worker-6702.log

3.4 Storm命令行操作

1)nimbus:启动nimbus守护进程

storm nimbus &

2)supervisor:启动supervisor守护进程

storm supervisor &

3)ui:启动UI守护进程。

storm ui &

4)list:列出正在运行的拓扑及其状态

storm list

6)jar:

storm jar 【jar路径】 【拓扑包名.拓扑类名】 【拓扑名称】

7)kill:杀死名为Topology-name的拓扑

storm kill topology-name [-w wait-time-secs]

-w:等待多久后杀死拓扑

8)active:激活指定的拓扑spout。

storm activate topology-name

9)deactivate:禁用指定的拓扑Spout。

storm deactivate topology-name

10)help:打印一条帮助消息或者可用命令的列表。

storm help

storm help

4 常用API

4.1 API简介

4.1.1 Component组件(Spout和Bolt)

1)基本接口

  • IComponent接口
  • ISpout接口
  • IRichSpout接口
  • IStateSpout接口
  • IRichStateSpout接口
  • IBolt接口
  • IRichBolt接口
  • BasicBolt接口

2)基本抽象类

  • BaseComponent抽象类
  • BaseRichSpout抽象
  • BaseRichBolt抽象
  • BaseTransactionalBolt抽象类
  • BaseBasicBolt抽象类

4.1.2 Spout(水龙头)

Hadoop生态圈(十一):Storm_第10张图片

(1)Open()

是初始化方法

(2)close()

在该spout关闭前执行,但是并不能得到保证其一定被执行,kill -9时不执行,Storm kill {topoName} 时执行

(3)activate()

当Spout已经从失效模式中激活时被调用。该Spout的nextTuple()方法很快就会被调用。

(4)deactivate ()

当Spout已经失效时被调用。在Spout失效期间,nextTuple不会被调用。Spout将来可能会也可能不会被重新激活。

5nextTuple()

当调用nextTuple()方法时,Storm要求Spout发射元组到输出收集器(OutputCollecctor)。NextTuple方法应该是非阻塞的,所以,如果Spout没有元组可以发射,该方法应该返回。nextTuple()、ack()和fail()方法都在Spout任务的单一线程内紧密循环被调用。当没有元组可以发射时,可以让nextTuple去sleep很短的时间,例如1毫秒,这样就不会浪费太多的CPU资源。

(6)ack()

成功处理tuple回调方法

(7)fail()

处理失败tuple回调方法

原则:实现一个Spout,可以直接实现接口IRichSpout,如果不想写多余的代码,可以直接继承BaseRichSpout

4.1.3 Bolt(转接头)

Hadoop生态圈(十一):Storm_第11张图片

(1)prepare()

prepare ()方法在集群的工作进程内被初始化时被调用,提供了Bolt执行所需要的环境。

(2)execute()

接受一个tuple进行处理(业务逻辑),也可emit(发射)数据到下一级组件。

(3)cleanup()

Cleanup方法当一个IBolt即将关闭时被调用。不能保证cleanup()方法一定会被调用,因为Supervisor可以对集群的工作进程使用kill -9命令强制杀死进程命令。

如果在本地模式下运行Storm,当拓扑被杀死的时候,可以保证cleanup()方法一定会被调用。

实现一个Bolt,可以实现IRichBolt接口或继承BaseRichBolt,如果不想自己处理结果反馈,可以实现 IBasicBolt接口或继承BaseBasicBolt,它实际上相当于自动做了prepare方法和collector.emit.ack(inputTuple)。

4.1.4 spouttail特性

Storm可以实时监测文件数据,当文件数据变化时,Storm自动读取。

4.2 网站日志处理案例

4.2.1 环境准备

pom文件中添加以下内容:

<dependencies>
        <dependency>
            <groupId>junitgroupId>
            <artifactId>junitartifactId>
            <version>4.12version>
        dependency>

        <dependency>
            <groupId>org.slf4jgroupId>
            <artifactId>slf4j-log4j12artifactId>
            <version>1.7.10version>
        dependency>

        <dependency>
            <groupId>org.apache.stormgroupId>
            <artifactId>storm-coreartifactId>
            <version>1.1.0version>
        dependency>
    dependencies>

    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.1version>
                <configuration>
                    <source>1.8source>
                    <target>1.8target>
                    <encoding>utf-8encoding>
                configuration>
            plugin>
        plugins>
    build>
project>

4.2.2 需求1

1)需求:将接收到的日志的会话id打印在控制台

(1)模拟访问网站的日志信息,包括:网站名称、会话id、访问网站时间等

(2)将接收到日志的会话id打印到控制台

2)分析

(1)创建网站访问日志工具类

(2)在spout中读取日志文件,并一行一行发射出去

(3)在bolt中将获取到的一行一行数据的会话id获取到,并打印到控制台。

(4)main方法负责拼接spout和bolt的拓扑。

3)代码

(1)生成网站访问日志

package com.bigdata.web;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random;

public class GenerateDate {

    public static void main(String[] args) throws FileNotFoundException {

        File logFlie = new File("F:/webSite.log");
        Random random = new Random();

        // 1 网站名称
        String[] hosts = {"www.bigdata.com"};
        // 2 会话id
        String[] session_id = {"ABYH6Y4V4SCVXTG6DPB4VH9U123", "XXYH6YCGFJYERTT834R52FDXV9U34",
                "BBYH61456FGHHJ7JL89RG5VV9UYU7", "CYYH6Y2345GHI899OFG4V9U567", "VVVYH6Y4V4SFXZ56JIPDPB4V678"};
        // 3 访问网站时间
        String[] time = {"2018-08-07 08:40:50", "2018-08-07 08:40:51", "2018-08-07 08:40:52", "2018-08-07 08:40:53",
                "2018-08-07 09:40:49", "2018-08-07 10:40:49", "2018-08-07 11:40:49", "2018-08-07 12:40:49"};

        // 4.拼接网站访问日志
        StringBuilder sbBuffer = new StringBuilder();
        for (int i = 0; i < 40; i++) {
            sbBuffer.append(hosts[0] + "\t" + session_id[random.nextInt(5)] + "\t" + time[random.nextInt(8)] + "\n");
        }

        // 5.判断log日志是否存在,不存在则创建
        if (!logFlie.exists()) {
            try {
                logFlie.createNewFile();
            } catch (IOException e) {
                System.out.println("Create logFile fail !");
                e.printStackTrace();
            }
        }

        //6.将拼接的日志信息写到日志文件中
        FileOutputStream fos = new FileOutputStream(logFlie);
        try {
            fos.write(sbBuffer.toString().getBytes());
            fos.close();
            System.out.println("===================>>>generate data over");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

(2)创建spout

package com.bigdata.web;

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

import java.io.*;
import java.util.Map;

public class WebLogSpout implements IRichSpout {

    private SpoutOutputCollector collector = null;
    private BufferedReader bufferedReader = null;
    private String str = null;

    @Override
    public void open(Map map, TopologyContext topologyContext, SpoutOutputCollector spoutOutputCollector) {
        this.collector = spoutOutputCollector;
        try {
            this.bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("F:/webSite.log"))));

            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void close() {

    }

    @Override
    public void activate() {

    }

    @Override
    public void deactivate() {

    }

    @Override
    public void nextTuple() {
        try {
            while ((str = bufferedReader.readLine()) != null){
                collector.emit(new Values(str));
                Thread.sleep(2000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void ack(Object o) {

    }

    @Override
    public void fail(Object o) {

    }

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

    @Override
    public Map getComponentConfiguration() {
        return null;
    }
}

(3)创建bolt

package com.bigdata.web;

import org.apache.storm.shade.org.apache.commons.lang.StringUtils;
import org.apache.storm.task.OutputCollector;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.IRichBolt;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.tuple.Tuple;

import java.util.Map;

public class WebLogBolt implements IRichBolt {

    private OutputCollector collector = null;
    private int num = 0;

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

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

            if (StringUtils.isNotBlank(log)) {
                num++;
                System.err.println(Thread.currentThread().getName() + "lines  :" + num + "   session_id:" + log.split("\t")[1]);
            }

            collector.ack(tuple);

            Thread.sleep(1000);
        } catch (InterruptedException e) {
            collector.fail(tuple);
            e.printStackTrace();
        }
    }

    @Override
    public void cleanup() {

    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {

    }

    @Override
    public Map getComponentConfiguration() {
        return null;
    }
}

(4)创建main

package com.bigdata.web;

import org.apache.storm.Config;
import org.apache.storm.LocalCluster;
import org.apache.storm.StormSubmitter;
import org.apache.storm.topology.TopologyBuilder;

//拓扑任务驱动类
public class WebLogDriver {

    public static void main(String[] args){
        // 1.创建拓扑对象
        TopologyBuilder builder = new TopologyBuilder();

        // 2.设置spout和bolt
        builder.setSpout("weblogspout",new WebLogSpout(),1);
        builder.setBolt("weblogbolt",new WebLogBolt(),1).shuffleGrouping("weblogspout");
        //builder.setBolt("weblogbolt",new WebLogBolt(),2).fieldsGrouping("weblogspout",new Fields("log"));

        // 3.配置worker开启个数
        Config config = new Config();
        config.setNumWorkers(2);

        // 4.提交拓扑
        if (args.length > 0) {
            //提交到分布式集群
            try {
                StormSubmitter.submitTopology("weblogtopology",config,builder.createTopology());
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            //本地集群模式执行
            LocalCluster localCluster = new LocalCluster();
            localCluster.submitTopology("weblogtopology",config,builder.createTopology());
        }

    }
}

4.2.3 需求2:动态增加日志,查看控制台打印信息(tail特性

1)在需求1基础上,运行程序。

2)打开website.log日志文件,增加日志调试并保存。

3)观察控制台打印的信息。

结论:Storm可以动态实时监测文件的增加信息,并把信息读取到再处理

5 分组策略和并发度

5.1 分组策略(Stream Grouping)

stream grouping用来定义一个stream应该如何分配给Bolts上面的多个Executors(多线程、多并发)。

Storm里面有7种类型的stream grouping

1)Shuffle Grouping: 随机分组,轮询,平均分配。随机派发stream里面的tuple,保证每个bolt接收到的tuple数目大致相同。

2)Fields Grouping:按字段分组,比如按userid来分组,具有同样userid的tuple会被分到相同的Bolts里的一个task,而不同的userid则会被分配到不同的bolts里的task。

3)All Grouping:广播发送,对于每一个tuple,所有的bolts都会收到。

4)Global Grouping:全局分组,这个tuple被分配到storm中的一个bolt的其中一个task。再具体一点就是分配给id值最低的那个task。

5)Non Grouping:不分组,这stream grouping个分组的意思是说stream不关心到底谁会收到它的tuple。目前这种分组和Shuffle grouping是一样的效果。在多线程情况下不平均分配

6)Direct Grouping:直接分组,这是一种比较特别的分组方法,用这种分组意味着消息的发送者指定由消息接收者的哪个task处理这个消息。只有被声明为Direct Stream的消息流可以声明这种分组方法。而且这种消息tuple必须使用emitDirect方法来发射。消息处理者可以通过TopologyContext来获取处理它的消息的task的id (OutputCollector.emit方法也会返回task的id)。

7)Local or shuffle grouping:如果目标bolt有一个或者多个task在同一个工作进程中,tuple将会被随机发送给这些tasks。否则,和普通的Shuffle Grouping行为一致。

8)测试

(1)spout并发度修改为2,bolt并发度修改为1,shuffleGrouping模式

builder.setSpout("WebLogSpout", new WebLogSpout(),2);

builder.setBolt("WebLogBolt", new WebLogBolt(), 1).shuffleGrouping("WebLogSpout");

Spout开两个线程会对数据读取两份,打印出来就是2份。如果数据源是消息队列,就不会出来读取两份的数据(统一消费者组,只能有一个消费者)

Thread-33-WebLogBolt-executor[1 1]lines:60  session_id:CYYH6Y2345GHI899OFG4V9U567

(2)spout并发度修改为1,bolt并发度修改为2,noneGrouping模式

builder.setSpout("WebLogSpout", new WebLogSpout(),1);

builder.setBolt("WebLogBolt", new WebLogBolt(), 2).noneGrouping("WebLogSpout");

每个bolt接收到的单词不同。

Thread-33-WebLogBolt-executor[1 1]lines:14  session_id:VVVYH6Y4V4SFXZ56JIPDPB4V678

Thread-34-WebLogBolt-executor[2 2]lines:16  session_id:VVVYH6Y4V4SFXZ56JIPDPB4V678

(3)spout并发度修改为1,bolt并发度修改为2,fieldsGrouping

builder.setSpout("WebLogSpout", new WebLogSpout(),1);

builder.setBolt("WebLogBolt", new WebLogBolt(), 2).fieldsGrouping("WebLogSpout", new Fields("log"));

基于web案例不明显,后续案例比较明显

5.2 并发度

Hadoop生态圈(十一):Storm_第12张图片

并发度:用户指定一个任务,可以被多个线程执行,并发度的数量等于线程executor的数量。(并发度就是线程数,即为图中executor的数量

Task就是具体的处理逻辑对象,一个executor线程可以执行一个或多个tasks,但一般默认每个executor只执行一个task,所以我们往往认为task就是执行线程,其实不是。

 

5.3 实时单词统计案例

(1)需求:实时统计发射到Storm框架中各个单词的总数

(2)分析:设计一个拓扑,来实现对文档里面的单词出现的频率进行统计

整个topology分为三个部分:

(1)WordCountSpout:数据源,在已知的英文句子中,随机发送一条句子出去。

(2)WordCountSplitBolt:负责将单行文本记录(句子)切分成单词

(3)WordCountBolt:负责对单词的频率进行累加

Hadoop生态圈(十一):Storm_第13张图片

 

(3)代码

1)创建spout

package com.bigdata.word;

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 WorldCountSpout extends BaseRichSpout {

    private SpoutOutputCollector collector = null;

    @Override
    public void open(Map map, TopologyContext topologyContext, SpoutOutputCollector spoutOutputCollector) {
        this.collector = spoutOutputCollector;
    }

    @Override
    public void nextTuple() {
        collector.emit(new Values("i am chinese love china"));

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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

2)创建切割单词的bolt

package com.bigdata.word;

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 WorldSplitBolt extends BaseRichBolt {

    private OutputCollector collector = null;

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

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

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

3)创建汇总单词的bolt

package com.bigdata.word;

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 WorldCountBolt extends BaseRichBolt {

    private OutputCollector collector = null;
    private Map map = new HashMap<>();

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

    @Override
    public void execute(Tuple tuple) {
        String word = tuple.getString(0);
        Integer num = tuple.getInteger(1);

        if (map.containsKey(word)) {
            Integer count = map.get(word);
            map.put(word, count + num);
        } else {
            map.put(word, num);
        }
        System.err.println(Thread.currentThread().getId() + "  word:" + word + "  num:" + map.get(word));
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer outputFieldsDeclarer) {

    }
}

4)创建程序的拓扑main

package com.bigdata.word;

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

public class WorldDriver {

    public static void main(String[] args) {

        //1.创建拓扑对象
        TopologyBuilder builder = new TopologyBuilder();

        //2.设置spout和bolt
        builder.setSpout("wordSpout", new WorldCountSpout(), 1);
        builder.setBolt("wordSplitBolt", new WorldSplitBolt(), 4).shuffleGrouping("wordSpout");
        builder.setBolt("wordCountBolt", new WorldCountBolt(), 2).fieldsGrouping("wordSplitBolt", new Fields("word"));

        //3.配置Worker的开启数
        Config config = new Config();
        config.setNumWorkers(4);

        //4.提交拓扑
        if (args.length > 0) {
            //提交到分布式集群
            try {
                StormSubmitter.submitTopology("wordTopology", config, builder.createTopology());
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            //提交到本地集群模式
            LocalCluster localCluster = new LocalCluster();
            localCluster.submitTopology("wordCountTopology", config, builder.createTopology());
        }


    }
}

 

你可能感兴趣的:(Hadoop)