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)。

離線計算與實時計算最大的區別:實時收集、實時計算、實時展示

實時流處理架

 

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來完成的。爲了滿足實時性的要求,希望能夠實時分析用戶的行爲日誌,將最新的用戶屬性反饋給搜索引擎,能夠爲用戶展現最貼近其當前需求的結果。

2)攜程-網站性能監控:實時分析系統監控攜程網的網站性能

利用HTML5提供的performance標準獲得可用的指標,並記錄日誌。Storm集羣實時分析日誌和入庫。使用DRPC聚合成報表,通過歷史數據對比等判斷規則,觸發預警事件。

3)淘寶雙十一:實時統計銷售總額

2 Storm基礎知識

2.1 Storm編程模型

 

編程模型中組件介紹:

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核心組件

 

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"]

# 設置Storm的數據存儲路徑

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

# 設置Worker的端口號

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

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)通過瀏覽器查看集羣狀態

 

(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 <command>

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(水龍頭)

(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(轉接頭)

(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>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.10</version>
        </dependency>

        <dependency>
            <groupId>org.apache.storm</groupId>
            <artifactId>storm-core</artifactId>
            <version>1.1.0</version>
        </dependency>
    </dependencies>

    <!--指定java編譯版本,固定配置即可-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>utf-8</encoding>
                </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<String, Object> 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<String, Object> 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 併發度

併發度:用戶指定一個任務,可以被多個線程執行,併發度的數量等於線程executor的數量。(併發度就是線程數,即爲圖中executor的數量

Task就是具體的處理邏輯對象,一個executor線程可以執行一個或多個tasks,但一般默認每個executor只執行一個task,所以我們往往認爲task就是執行線程,其實不是。

 

5.3 實時單詞統計案例

(1)需求:實時統計發射到Storm框架中各個單詞的總數

(2)分析:設計一個拓撲,來實現對文檔裏面的單詞出現的頻率進行統計

整個topology分爲三個部分:

(1)WordCountSpout:數據源,在已知的英文句子中,隨機發送一條句子出去。

(2)WordCountSplitBolt:負責將單行文本記錄(句子)切分成單詞

(3)WordCountBolt:負責對單詞的頻率進行累加

 

(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<String, Integer> 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());
        }


    }
}

 

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