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

    }
}
 

 

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