【Storm篇】--Storm分組策略

LHBlog發表於2018-01-25

一、前述

Storm由數源泉spout到bolt時,可以選擇分組策略,實現對spout發出的資料的分發。對多個並行度的時候有用。

二、具體原理

1. Shuffle Grouping
隨機分組,隨機派發stream裡面的tuple,保證每個bolt task接收到的tuple數目大致相同。
輪詢,平均分配

2. Fields Grouping(相同fields去分發到同一個Bolt)
按欄位分組,比如,按"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一樣。

總結:前4種用的多些,後面4種用的少些。

三、具體案例

Spout(產生資料):

package com.sxt.storm.grouping;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.Map;

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

public class MySpout implements IRichSpout {

    private static final long serialVersionUID = 1L;

    FileInputStream fis;
    InputStreamReader isr;
    BufferedReader br;

    SpoutOutputCollector collector = null;
    String str = null;

    @Override
    public void nextTuple() {//真正發的邏輯
        try {
            while ((str = this.br.readLine()) != null) {
                // 過濾動作
                collector.emit(new Values(str, str.split("\t")[1]));//發出資料,一行和一行切分完後第二個欄位。
            }
        } catch (Exception e) {
        }

    }

    @Override
    public void close() {//釋放資源
        try {
            br.close();
            isr.close();
            fis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {//初始化(方法只呼叫一次)
        try {
            this.collector = collector;
            this.fis = new FileInputStream("track.log");
            this.isr = new InputStreamReader(fis, "UTF-8");
            this.br = new BufferedReader(isr);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {//宣告發出去的欄位
        declarer.declare(new Fields("log", "session_id"));
    }

    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }

    @Override
    public void ack(Object msgId) {
        System.out.println("spout ack:" + msgId.toString());
    }

    @Override
    public void activate() {
    }

    @Override
    public void deactivate() {
    }

    @Override
    public void fail(Object msgId) {
        System.out.println("spout fail:" + msgId.toString());
    }

}

 

Bolt:(處理單元)

package com.sxt.storm.grouping;

import java.util.Map;

import backtype.storm.task.OutputCollector;
import backtype.storm.task.TopologyContext;
import backtype.storm.topology.IRichBolt;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;

public class MyBolt implements IRichBolt {

    private static final long serialVersionUID = 1L;

    OutputCollector collector = null;
    int num = 0;
    String valueString = null;

    @Override
    public void cleanup() {

    }

    @Override
    public void execute(Tuple input) {
        try {
            valueString = input.getStringByField("log");//通過fields接收資料

            if (valueString != null) {
                num++;
                System.err.println(input.getSourceStreamId() + " " + Thread.currentThread().getName() + "--id="//列印當前程式名字
                        + Thread.currentThread().getId() + "   lines  :" + num + "   session_id:"//列印當前程式id 
                        + valueString.split("\t")[1]);//這行詞的第二個字母
            }
            collector.ack(input);
            // Thread.sleep(2000);
        } catch (Exception e) {
            collector.fail(input);
            e.printStackTrace();
        }

    }

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

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
        declarer.declare(new Fields(""));//宣告空即可
    }

    @Override
    public Map<String, Object> getComponentConfiguration() {
        return null;
    }

}

 

Main方法:

package com.sxt.storm.grouping;

import backtype.storm.Config;
import backtype.storm.LocalCluster;
import backtype.storm.StormSubmitter;
import backtype.storm.generated.AlreadyAliveException;
import backtype.storm.generated.InvalidTopologyException;
import backtype.storm.topology.TopologyBuilder;
import backtype.storm.tuple.Fields;

public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {

        TopologyBuilder builder = new TopologyBuilder();

        builder.setSpout("spout", new MySpout(), 1);//拓撲名,資料來源,並行度

        builder.setBolt("bolt", new MyBolt(), 2).allGrouping("spout");//兩個spot並行 所有都分發

        //builder.setBolt("bolt", new MyBolt(), 2).shuffleGrouping("spout");// shuffleGrouping其實就是隨機往下游去發,不自覺的做到了負載均衡
 
//builder.setBolt("bolt", new MyBolt(), 2).fieldsGrouping("spout", new Fields("session_id")); // fieldsGrouping其實就是MapReduce裡面理解的Shuffle,根據fields求hash來取模

       //builder.setBolt("bolt", new MyBolt(), 2).globalGrouping("spout"); // 只往一個裡面發,往taskId小的那個裡面去傳送

// builder.setBolt("bolt", new MyBolt(), 2).noneGrouping("spout");   // 等於shuffleGrouping
// Map conf = new HashMap(); // conf.put(Config.TOPOLOGY_WORKERS, 4); Config conf = new Config(); conf.setDebug(false); conf.setMessageTimeoutSecs(30); if (args.length > 0) { try { StormSubmitter.submitTopology(args[0], conf, builder.createTopology());//叢集方式 } catch (AlreadyAliveException e) { e.printStackTrace(); } catch (InvalidTopologyException e) { e.printStackTrace(); } } else { LocalCluster localCluster = new LocalCluster(); localCluster.submitTopology("mytopology", conf, builder.createTopology());// 本地模擬引數分別為名稱,配置,構建拓撲結構。 } } }

 結果:

1. builder.setBolt("bolt", new MyBolt(), 2).allGrouping("spout");//兩個spot並行 所有都分發

 

 

 2. builder.setBolt("bolt", new MyBolt(), 2).shuffleGrouping("spout")其實就是隨機往下游去發,不自覺的做到了負載均衡

3.builder.setBolt("bolt", new MyBolt(), 2).fieldsGrouping("spout", new Fields("session_id")); // fieldsGrouping其實就是MapReduce裡面理解的Shuffle,根據fields求hash來取模,相同的名稱的fields分發到一個bolt裡面。

4.builder.setBolt("bolt", new MyBolt(), 2).globalGrouping("spout"); // 只往一個裡面發,往taskId小的那個裡面去傳送

 企業中常用的也就是這幾個!!!

 

相關文章