一、storm基礎概念

fan_rockrock發表於2015-12-27

1、什麼是storm

       Storm是一個分散式的、高容錯的實時計算系統。
Storm對於實時計算的的意義相當於Hadoop對於批處理的意義。Hadoop為我們提供了Map和Reduce原語,使我們對資料進行批處理變的非常的簡單和優美。同樣,Storm也對資料的實時計算提供了簡單Spout和Bolt原語。
Storm適用的場景:
(1)、流資料處理:Storm可以用來用來處理源源不斷的訊息,並將處理之後的結果儲存到持久化介質中。
(2)、分散式RPC:由於Storm的處理元件都是分散式的,而且處理延遲都極低,所以可以Storm可以做為一個通用的分散式RPC框架使用。

2、入門級程式wordcount

topology:


 SentenceSpout.java

public class SentenceSpout extends BaseRichSpout {
    private SpoutOutputCollector collector;
    private String[] sentences = {
        "my dog has fleas",
        "i like cold beverages",
        "the dog ate my homework",
        "don't have a cow man",
        "i don't think i like fleas"
    };
    private int index = 0;

    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("sentence"));
    }

    public void open(Map config, TopologyContext
            context, SpoutOutputCollector collector) {
        this.collector = collector;
    }
    public void nextTuple() {
        this.collector.emit(new Values(sentences[index]));
        index++;
        if (index >= sentences.length) {
            index = 0;
        }
        Utils.sleep(1);
    }
}

 SplitSentenceBolt.java

public class SplitSentenceBolt extends BaseRichBolt {
    private OutputCollector collector;
    public void prepare(Map config, TopologyContext
            context, OutputCollector collector) {
        this.collector = collector;
    }

    public void execute(Tuple tuple) {
        String sentence = tuple.getStringByField("sentence");
        String[] words = sentence.split(" ");
        for(String word : words){
            this.collector.emit(new Values(word));
        }
    }
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("word"));
    }
}

WordCountBolt.java

public class WordCountBolt extends BaseRichBolt {
    private OutputCollector collector;
    private HashMap<String, Long> counts = null;
    public void prepare(Map config, TopologyContext
            context, OutputCollector collector) {
        this.collector = collector;
        this.counts = new HashMap<String, Long>();
    }

    public void execute(Tuple tuple) {
        String word = tuple.getStringByField("word");
        Long count = this.counts.get(word);
        if(count == null){
            count = 0L;
        }
        count++;
        this.counts.put(word, count);
        this.collector.emit(new Values(word, count));
    }

    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields("word", "count"));
    }
}

ReportBolt.java

public class ReportBolt extends BaseRichBolt {
    private HashMap<String, Long> counts = null;

    public void prepare(Map config, TopologyContext context, OutputCollector collector) {
        this.counts = new HashMap<String, Long>();
    }
    public void execute(Tuple tuple) {
        String word = tuple.getStringByField("word");
        Long count = tuple.getLongByField("count");
        this.counts.put(word, count);
    }

    public void declareOutputFields(OutputFieldsDeclarer declarer) {
       // this bolt does not emit anything
    }
public void cleanup() {
        System.out.println("--- FINAL COUNTS ---");
        List<String> keys = new ArrayList<String>();
        keys.addAll(this.counts.keySet());
        Collections.sort(keys);
        for (String key : keys) {
            System.out.println(key + " : " + this.counts.get(key));
        }
        System.out.println("--------------");
    }

}

WordCountTopology.java

public class WordCountTopology {

    private static final String SENTENCE_SPOUT_ID = "sentence-spout";
    private static final String SPLIT_BOLT_ID = "split-bolt";
    private static final String COUNT_BOLT_ID = "count-bolt";
    private static final String REPORT_BOLT_ID = "report-bolt";
    private static final String TOPOLOGY_NAME = "word-count-topology";

    public static void main(String[] args) throws
            Exception {
        SentenceSpout spout = new SentenceSpout();
        SplitSentenceBolt splitBolt = new
                SplitSentenceBolt();
        WordCountBolt countBolt = new WordCountBolt();
        ReportBolt reportBolt = new ReportBolt();

        TopologyBuilder builder = new TopologyBuilder();
        builder.setSpout(SENTENCE_SPOUT_ID, spout);
        // SentenceSpout --> SplitSentenceBolt
        builder.setBolt(SPLIT_BOLT_ID, splitBolt).shuffleGrouping(SENTENCE_SPOUT_ID);
        // SplitSentenceBolt --> WordCountBolt
        builder.setBolt(COUNT_BOLT_ID, countBolt).fieldsGrouping(
                SPLIT_BOLT_ID, new Fields("word"));
        // WordCountBolt --> ReportBolt
        builder.setBolt(REPORT_BOLT_ID, reportBolt).globalGrouping(COUNT_BOLT_ID);
        Config config = new Config();
        LocalCluster cluster = new LocalCluster();
        cluster.submitTopology(TOPOLOGY_NAME, config,
                builder.createTopology());
        Utils.sleep(10000);
        cluster.killTopology(TOPOLOGY_NAME);
        cluster.shutdown();
    }
}

3、storm叢集結構


4、Topology

一個實時計算應用程式的邏輯在storm裡面被封裝到topology物件裡面, 我把它叫做計算拓補. Storm裡面的topology相當於Hadoop裡面的一個MapReduce Job, 它們的關鍵區別是:一個MapReduce Job最終總是會結束的, 然而一個storm的topoloy會一直執行 — 除非你顯式的殺死它。 一個Topology是Spouts和Bolts組成的圖狀結構, 而連結Spouts和Bolts的則是Stream groupings。


5、spout

噴口(Spout)是拓撲的流的來源,是一個拓撲中產生源資料流的元件。通常情況下,Spout會從外部資料來源(例如Kestrel佇列或Twitter API)中讀取資料,然後轉換為拓撲內部的源資料。Spout可以是可靠的,也可以是不可靠的。如果Storm處理元組失敗,可靠的Spout能夠重新發射,而不可靠的Spout就儘快忘記發出的元組。Spout是一個主動的角色,其介面中有個nextTuple()函式Storm框架會不停地呼叫此函式,使用者只要在其中生成源資料即可。

Spout的其他主要方法是ack()fail()Storm檢測到一個元組從Spout發出時,ack()fail()會被呼叫,要麼成功完成通過拓撲,要麼未能完成ack()fail()僅被可靠的Spout呼叫

6、bolt

所有的訊息處理邏輯被封裝在bolts裡面。 Bolts可以做很多事情: 過濾, 聚合, 查詢資料庫等等。
Bolts的主要方法是execute, 它以一個tuple作為輸入,Bolts使用OutputCollector來發射tuple, Bolts必須要為它處理的每一個tuple呼叫OutputCollector的ack方法,以通知storm這個tuple被處理完成了。– 從而我們通知這個tuple的發射者Spouts。 一般的流程是: Bolts處理一個輸入tuple,  發射0個或者多個tuple, 然後呼叫ack通知storm自己已經處理過這個tuple了。storm提供了一個IBasicBolt會自動呼叫ack

7、tuple和stream

    一個Tuple代表資料流中的一個基本的處理單元,例如一條cookie日誌,它可以包含多個Field,每個Field表示一個屬性。

     

Tuple本來應該是一個Key-Value的Map,由於各個元件間傳遞的tuple的欄位名稱已經事先定義好了,所以Tuple只需要按序填入各個Value,所以就是一個Value List。
一個沒有邊界的、源源不斷的、連續的Tuple序列就組成了Stream。

8、stream groupings

Shuffle Grouping:隨機分組,隨機派發stream裡面的tuple,保證每個bolt接收到的tuple數目相同。
Fields Grouping:按欄位分組,比如按userid來分組,具有同樣userid的tuple會被分到相同的Bolts,而不同的userid則會被分配到不同的Bolts。
All Grouping:廣播傳送,對於每一個tuple,所有的Bolts都會收到。
Global Grouping: 全域性分組,這個tuple被分配到storm中的一個bolt的其中一個task。再具體一點就是分配給id值最低的那個task。
Non Grouping:不分組,這個分組的意思是說stream不關心到底誰會收到它的tuple。目前這種分組和Shuffle grouping是一樣的效果,有一點不同的是storm會把這個bolt放到這個bolt的訂閱者同一個執行緒裡面去執行。
Direct Grouping:直接分組,  這是一種比較特別的分組方法,用這種分組意味著訊息的傳送者指定由訊息接收者的哪個task處理這個訊息。只有被宣告為Direct Stream的訊息流可以宣告這種分組方法。而且這種訊息tuple必須使用emitDirect方法來發射。訊息處理者可以通過TopologyContext來獲取處理它的訊息的taskid (OutputCollector.emit方法也會返回taskid)
Local or shuffle grouping:如果目標bolt有一個或者多個task在同一個工作程式中,tuple將會被隨機發生給這些tasks。否則,和普通的Shuffle Grouping行為一致。 


相關文章