一、前述
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小的那個裡面去傳送
企業中常用的也就是這幾個!!!