Storm入門指南第三章 拓撲結構
在本章你將會看到如何在一個Storm拓撲的不同元件間傳遞元組,以及如何將一個topology部署到一個執行的Storm叢集上。
流分組
在設計一個topology時,一件最重要的事就是定義資料在元件之間怎樣交換(流怎樣被bolt消費)。流分組(Stream Grouping)指定了每個bolt消費哪些流,以及這些流如何被消費。
提示:一個節點可以傳送不止一條資料流,流分組允許我們選擇接收哪些流。
正如第二章中見到的,流分組在topology被定義的時候就已經被設定了:
1
2
|
builder.setBolt( "word-normalizer" , new
WordNormalizer()) .shuffleGrouping( "word-reader" ); |
在上面的程式碼塊中,在topology builder上設定了一個bolt,然後設定該bolt的源使用隨機分組(shuffleGrouping)。通常情況下,一個流分組攜帶源元件的Id作為引數,並且還有其他可選引數,這取決於流分組的種類。
提示:每個InputDeclarer可以有不止一個源,並且每個源可以用不同的流分組來分組。
隨機分組
隨機分組(Shuffle Grouping)是最常用的分組方式,該分組方式攜帶一個引數(源元件Id),源元件傳送元組到一個隨機選擇的bolt並確保每個消費者(即bolt)會收到相等數量的元組。
隨機分組對於做原子操作(例如數學運算)是很有用的。然而,如果操作不能被隨機分配,就應該考慮使用其他分組,例如在第二章為單詞計數的例子中。
欄位分組
欄位分組(Fields Grouping)允許你基於tuple中的一個或多個欄位控制元組如何被髮送到bolt,該分組方式確保了對於一個組合欄位所確定的的值集合總是會被髮送到相同的bolt。回到單詞計數的例子,如果你根據“word”欄位將流分組,則WordNormalizer bolt總是會將包含給定的單詞的元組一起傳送到相同的WordCounter bolt例項中。
1
2
|
builder.setBolt( "word-counter" , new
WordCounter(), 2 ) .fieldsGrouping( "word-normalizer" , new
Fields( "word" )); |
提示:欄位分組中設定的所有欄位在源元件的輸出欄位宣告中也必須存在(譯者注:在源元件的declareOutputFields()方法中宣告)。
全部分組
全部分組(All Grouping)會向接收bolt的所有例項傳送一份每個元組的副本,這種分組方式被用於向bolts傳送訊號。例如,如果你需要重新整理快取,你可以向所有bolt傳送一個重新整理快取訊號。在第二章單詞計數的例子中,可以使用所有分組方式增加清空計數器快取的功能(WordCounter中相應程式碼如下)
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public
void
execute(Tuple input) { String
str = null ; try { if (input.getSourceStreamId().equals( "signals" )){ str
=input.getStringByField( "action" ); if ( "refreshCache" .equals(str)) counters.clear(); } } catch (IllegalArgumentException
e) { //Do
nothing } ... } |
上面的程式碼中增加了一個if檢查源流的名字 (sourceStreamId) 是否為”signal”。Storm中可以宣告命名的流 (named streams) ,如果你不傳送元組到一個命名的流,則流的預設名字為“default”。這是一個非常好的方式來確定元組的源,正如這個例子中我們需要確定訊號一樣。
在topology定義中,向word-counter bolt 增加第二個流分組方式,以便來自signals-spout 流中的每個tuple能傳送到bolt的所有例項中。
1
2
3
|
builder.setBolt( "word-counter" , new
WordCounter(), 2 ) .fieldsGrouping( "word-normalizer" , new
Fields( "word" )) .allGrouping( "signals-spout" , "signals" ) |
關於signals-spout的實現可以在git庫中找到。
自定義分組
通過實現CustomStreamGrouping介面,你也可以建立自定義流分組,這讓你有權決定每個元組將被哪個(些)bolt接收。
下面我們修改單詞計數的例子,將元組分組以便相同字母開頭的單詞能被相同的bolt接收。
01
02
03
04
05
06
07
08
09
10
11
12
|
public
class
ModuleGrouping implements
CustomStreamGrouping,Serializable{ int
numTasks= 0 ; @Override public
List chooseTasks(List</pre> 上面是一個CustomStreamGrouping的簡單實現,在這裡我們使用任務的數量
(numTasks) 來對單詞的第一個字元的整型值取模,由此選擇哪個bolt將接收這個元組。 <p
align= "left" >要在單詞計數的例子中使用這種分組,按照下列方式修改word-counter分組(<strong>譯者注:</strong>此處有修改):</p> 1 builder.setBolt( "word-counter" , new
WordCounter()) .customGrouping( "word-normalizer" , new
ModuleGrouping()); |
直接分組
這是一個特殊的分組方式,由源元件決定哪個元件將接收元組。同前面的例子類似,源元件將基於單詞中的第一個字母決定哪個bolt接收這個tuple,為了使用直接分組,需要在WordNormalizer bolt中使用emitDirect()方法代替emit方法。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
public
void
execute(Tuple input) { ... for (String
word:words){ if (!word.isEmpty()){ ... collector.emitDirect(getWordCountIndex(word), new
Values(word)); } } //
Acknowledge the tuple collector.ack(input); } public
Integer getWordCountIndex(String word) { word
=word.trim().toUpperCase(); if (word.isEmpty()) return
0 ; else return
word.charAt( 0 )
% numCounterTasks; } |
在prepare()方法中算出目標任務的數目:
1
2
3
4
5
|
public
void
prepare(Map stormConf,TopologyContext context, OutputCollector
collector) { this .collector=collector; this .numCounterTasks=context.getComponentTasks( "word-counter" ); } |
在topology的定義中,指定流將被直接分組:
1
2
|
builder.setBolt( "word-counter" , new
WordCounter(), 2 ) .directGrouping( "word-normalizer" ); |
全域性分組
全域性分組(Global Grouping)將源元件的所有例項產生的元組傳送到一個目標元件的例項()中(具體地說,是bolt中Id最小的那個任務)。
無分組
在Storm0.7.1時,使用這種分組和使用隨機分組一樣,即不關注流怎樣被分組。
LocalCluster vs. StormSubmitter
到現在為止,你都在使用一個叫LocalCluster的工具在本地計算機上執行topology。在自己的計算機上執行Storm基礎結構可以使你方便地執行和除錯不同的topology。但是當你想將你的topology提交到一個執行的Storm叢集上時該怎麼做呢?Storm的一大特點就是可以很方便地將你的topology傳送到一個真實的叢集上執行,此時你需要將LocalCluster改成StormSubmitter並且實現其中的submitTopology()方法,該方法負責將topology傳送到叢集上。
程式碼改變如下:
1
2
3
4
5
6
7
|
//LocalCluster
cluster = new LocalCluster(); //cluster.submitTopology("Count-Word-Topology-With-Refresh-Cache",conf, builder.createTopology()); StormSubmitter.submitTopology( "Count-Word-Topology-With-Refresh-Cache" ,conf, builder.createTopology()); //Thread.sleep(1000); //cluster.shutdown(); |
提示:當使用StormSubmitter時,不可以在程式碼中控制叢集,但是使用LocalCluster可以。
接著,將原始碼打包成一個jar檔案,它將在你執行Storm客戶端命令提交topology時被髮送。因為使用的是Maven,所以你只需進入原始檔夾下執行命令:mvn package
生成jar檔案後,使用 storm jar 命令提交topology(如何安裝Storm客戶端參見附錄A),這個命令的語法是:
storm jar allmycode.jar org.me.MyTopology arg1 arg2 arg3
本例中,在topologies的源專案資料夾執行:
storm jar target/Topologies-0.0.1-SNAPSHOT.jar countword.TopologyMain src/main/resources/words.txt
通過這些命令,你就已經將topology提交到叢集上了。要想停止或者殺死該topology,執行:
storm kill Count-word-Topology-With-Refresh-Cache
提示:Topology的名字必須唯一。
DRPC拓撲結構
有一種被稱為DRPC(分散式遠端過程呼叫,Distributed Remote Procedure Call)的特殊的topology型別,它使用Storm分散式的能力來執行遠端過程呼叫 (RPC)。如圖3-1所示,Storm提供了一些工具讓你使用DRPC,第一個工具是DRPC伺服器,它的作用是充當DRPC 客戶端和topology之間的聯結器 ,以及topology spouts的源。DRPC伺服器接收一個函式和它的引數來執行,然後對於函式操作的每個資料片,伺服器都分配一個在整個topology中使用的請求ID來識別RPC請求。當topology執行最後一個bolt時,它必須傳送標識RPC的請求ID和結果,使得DRPC伺服器可以返回結果至正確的客戶端。
提示:一個DRPC伺服器可以執行很多函式,每個函式都被一個唯一的ID標識。
Storm提供的第二個工具是LinearDRPCTopologyBuilder——一個來幫助構建DRPC topologies的抽象。構建的topology建立DRPCSpouts(它連線DRPC伺服器,並且傳送資料到topology的剩餘部分)、包裝bolt(以便結果從最後一個bolt返回)。所有新增到LinearDRPCTopologyBuilder的bolt被順序執行。
作為這種型別topology的一個例子,我們將建立一個累加數的程式。這個例子很簡單,但是這種理念可以被擴充套件到執行復雜的分散式數學運算。
bolt有如下輸出宣告:
1
2
3
|
public
void
declareOutputFields(OutputFieldsDeclarer declarer) { declarer.declare( new
Fields( "id" , "result" )); } |
由於這是topology中的唯一bolt,所以必須傳送標識RPC的請求ID和結果。
execute()方法負責執行累加操作:
01
02
03
04
05
06
07
08
09
10
11
|
public
void
execute(Tuple input) { String[]
numbers= input.getString( 1 ).split( "\\+" ); Integer
added = 0 ; if (numbers.length< 2 ){ throw
new
InvalidParameterException( "Shouldbe
at least 2 numbers" ); } for (String
num:numbers){ added
+=Integer.parseInt(num); } collector.emit( new
Values(input.getValue( 0 ),added)); } |
包含做累加的bolt的topology的定義如下:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
public
static
void
main(String[] args) { LocalDRPC
drpc = new
LocalDRPC(); LinearDRPCTopologyBuilder
builder = new
LinearDRPCTopologyBuilder( "add" ); builder.addBolt( new
AdderBolt(), 2 ); Config
conf = new
Config(); conf.setDebug( true ); LocalCluster
cluster = new
LocalCluster(); cluster.submitTopology( "drpc-adder-topology" ,conf, builder.createLocalTopology(drpc)); String
result =drpc.execute( "add" , "1+-1" ); checkResult(result, 0 ); result
=drpc.execute( "add" , "1+1+5+10" ); checkResult(result, 17 ); cluster.shutdown(); drpc.shutdown(); } |
建立LocalDRPC物件來在本地執行DRPC。接著,建立topology builder來新增bolt到topology中。為了測試這個topology,在DRPC物件上使用execute方法。
提示:使用DRPCClient類連線到遠端DRPC伺服器。DRPC伺服器提供一個可被多種語言使用的Trift API,並且在本地或者遠端執行DRPC伺服器使用同樣的API。 為了將topology提交到Storm叢集,使用builder物件中的createRemoteTopology()方法代替createLocalTopology()方法,該方法使用storm配置中的DRPC配置。
- 本文固定連結: http://www.flyne.org/article/96
- 轉載請註明: 東風化宇 2014年03月11日 於 Flyne 發表
相關文章
- Storm入門之第8章事務性拓撲ORM
- 網路拓撲結構
- Storm入門指南第二章 入門ORM
- 拓撲排序小結排序
- 網路拓撲結構視覺化呈現方案視覺化
- 關於Flume拓撲結構--Flink進階認識
- 拓撲排序排序
- 兩層網路監控拓撲結構的原因和方法
- 搞定storm-入門ORM
- Storm入門指南第一章 基礎知識ORM
- 拓撲排序,YYDS排序
- 拓撲排序模板排序
- ODA入門指南總結
- 圖論——拓撲排序圖論排序
- 筆記:拓撲排序筆記排序
- 基於 WebGL 的 HTML5 網路拓撲結構 3D 圖WebHTML3D
- 演算法資料結構 | 圖論基礎演算法——拓撲排序演算法資料結構圖論排序
- 基於 HTML5 WebGL 的 3D 網路拓撲結構圖HTMLWeb3D
- Storm入門之附錄CORM
- storm的很好的入門文件ORM
- 網路拓撲圖:網路拓撲圖介紹及線上製作
- Reward (圖論+拓撲排序)圖論排序
- 拓撲排序 - Topological Sort排序
- 拓撲排序核心程式碼排序
- HDU 4857 逃生(拓撲排序)排序
- 【筆記/模板】拓撲排序筆記排序
- AOV網與拓撲排序排序
- DFS實現拓撲排序排序
- 基於 HTML5 網路拓撲圖的快速開發之入門篇(二)HTML
- 基於 HTML5 網路拓撲圖的快速開發之入門篇(一)HTML
- 快速開發基於 HTML5 網路拓撲圖應用--入門篇(一)HTML
- 快速開發基於 HTML5 網路拓撲圖應用--入門篇(二)HTML
- Flink原理與實現:架構和拓撲概覽架構
- 拓撲排序就這麼回事排序
- HDU4857逃生(拓撲排序)排序
- 四、事務拓撲(Transactional Topolgoy)Go
- 紙上談兵: 拓撲排序排序
- poj 1094 拓撲排序排序