一. wordCount Topology開發:
1.spout數據收集器(SentenceSpout類):
有兩種方法來開發spout類,第一種是實現backtype.storm.topology.IRichSpout接口,第二種是繼承backtype.storm.topology.base.BaseRichSpout類。
其中,IRichSpout接口提供了更多的一些需要實現的方法,BaseRichSpout類只提供了3個需要實現的方法。
@Override
public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
// TODO Auto-generated method stub
this.collector = collector;
}
@Override
public void close() {
// TODO Auto-generated method stub
}
@Override
public void activate() {
// TODO Auto-generated method stub
}
@Override
public void deactivate() {
// TODO Auto-generated method stub
}
@Override
public void nextTuple() {
// TODO Auto-generated method stub
}
@Override
public void ack(Object msgId) {
// TODO Auto-generated method stub
}
@Override
public void fail(Object msgId) {
// TODO Auto-generated method stub
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
// TODO Auto-generated method stub
}
@Override
public Map<String, Object> getComponentConfiguration() {
// TODO Auto-generated method stub
return null;
}
上邊的這些方法中,有幾個比較重要,需要實現。
1.nextTuple():
在該方法中,編寫從數據源獲取數據的邏輯。該方法程序循環調用,collector.emit()向后邊的bolt發射數據。
2.declareOutputFields():
該方法聲明向后邊發射的記錄的字段名稱。
3.tuple
collector.emit()方法發射的內容是Tuple,類型為List<Object> tuple。 tuple元組是一系列key,value對的集合。例如:(a:a_value,b:b_value,c:c_value,...,n:n_value)。其中,collector.emit(new Values())聲明的是tuple的value值,而declarer.declare(new Fields())聲明的是tuple的key值,兩者是一一對應的(假如new Values(val1,val2),那么,declarer.declare(new Fields(key1,key2))也需要聲明2個值,并且key1對一個val1,key2對應val2)。
4.open():
該方法是初始化方法,將會第一個被調用,一般,我們可以在該方法內實例化定義的類。
2.bolt組件(SplitBolt、CountBolt類):
開發bolt組件,需要實現backtype.storm.topology.IRichBolt接口,或者繼承類backtype.storm.topology.base.BaseRichBolt。
下面幾個方法比較重要:
1.prepare()
初始化方法,將會第一個被調用,一般,我們可以在該方法內實例化定義的類。
2.execute()
循環調用,被動執行,前面數據來源向該bolt發射tuple的時候,就會調用execute方法。
3.declareOutputFields
與spout相同。
3.Topology驅動類(WordsToplogy類):
向集群提交Topology,需要使用類backtype.storm.topology.TopologyBuilder。TopologyBuilder類可以配置spout、bolt組件的記錄發射關系(前后依賴關系,例如:spout --> bolt1 -->bolt2等)。
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("Spout", new SentenceSpout());
builder.setBolt("SplitBolt", new SplitBolt()).shuffleGrouping("Spout");
builder.setBolt("CountBolt", new CountBolt())
.fieldsGrouping("SplitBolt", new Fields("Word"));
所謂的grouping策略就是在Spout與Bolt、Bolt與Bolt之間傳遞Tuple的方式。Grouping分組策略主要有以下幾種:
1.shuffleGrouping:隨機分組。將流分組定義為混排。這種混排分組意味著來自Spout的輸入將混排,或隨機分發給此Bolt中的任務。shuffle grouping對各個task的tuple分配的比較均勻。
2.fieldsGrouping:按照字段分組。Storm能保證所有相同Field值的數據到達的是相同的Blot,但是不保證一個Blot只處理一個值域。這對于分組統計的應用來說是比較重要的,如果分組不正確的話會造成統計出錯。
3.globalgrouping:全局分組,前面組件的數據,全部只會往該組件的其中一個上傳送。
4.allGrouping:廣播發送,即每一個Tuple,每一個Bolt都會收到。
4.Storm集群參數配置:
對于storm集群的參數,可以通過Config對象來配置。
Config conf = new Config();
conf.setMaxSpoutPending(10);
也可以通過conf.put(key, value)來配置xml文件中的參數。
5.單機運行或者提交storm集群:
單機提交topology(主要用于提交集群前的測試,非常重要)。使用LocalCluster類來提交單機測試topology:
LocalCluster local = new LocalCluster();
local.submitTopology("LocalTest", conf, builder.createTopology());
集群提交topology:
StormSubmitter.submitTopology("WordCount", conf, builder.createTopology());
6.代碼示例:
下面的代碼實現了一個詞頻統計的storm實例,功能非常簡單,隨機發送sentence并拆分統計單詞。
https://github.com/neil-ma/storm-pmpa/tree/master/storm-pirate/src/main/java/com/pmpa/storm/words
二. Topology并發控制:
1.并發控制組件:
Storm的并發度最終表示的Task的并發度。Storm執行架構有三個層次 Worker -> Executor -> Task。配置以上3個組件的數量來控制并發度。
Worker進程:針對具體的Topology,worker上只運行與之相關的Topology,一個worker進程上可以啟動多個executor線程。
Executor線程:針對具體的task(spout、bolt),一個Executor線程上可以跑多個task,默認一個Executor運行一個task。
Task:指定多個task來運行spout或者bolt組件。
2.參數配置:
1.Worker進程數量:
通過Config設置 : conf.setNumWorkers(4); // 設置worker個數為4
Supervisor進程負責啟動worker,假如有3個supervior,這3個supervisor會平均配置4個Worker,例如: 2 1 1 。
2.Executor個數:
在構造Topology時,在setSpout()或者setBolt()方法中設定executor的數量。例如下例子:
builder.setBolt("SplitBolt", new SplitBolt(),3).shuffleGrouping("Spout")
代碼表示,需要啟動3個executor來運行SplitBolt。需要注意的是,這里表示一共有3個executor,而不是每個worker上運行3個executor。假如說,config的配置一共有2個worker,那么分配的結果就是一個worker上執行2個executor,另一個worker上執行1個executor。后邊的task配置也是一樣的道理。
3.task個數
task的數量由setNumTasks()方法確定,例如下邊的定義:
builder.setBolt("SplitBolt", new SplitBolt(),3).shuffleGrouping("Spout")
.setNumTasks(6);
上邊代碼表示3個executor共執行6個task,storm會平均分配一個executor執行2個task(系統自動做到盡量均勻)。如果不指定setNumTasks()方法,默認1個Executor運行一個Task,上邊代碼如果不指定setNumTasks()方法會有3個Task執行。
三. Storm消息可靠性保障機制:
對于某些實時大數據應用,例如銀行的實時數據、交管部門的實時數據等,需要保證數據的可靠性,在實施這類應用時,就需要開啟storm的消息可靠性保障機制。消息可靠性保障機制實際上就是Storm需要對spout發送的每一條消息是否被后續的bolt成功處理完成有一條反饋。
1.原理和機制:
1.ack機制:
為了保證storm的每條記錄都能正確處理,Storm會對Spout發送的每一個tuple進行跟蹤。這里面包括ack/fail的處理,一個tuple處理成功是指這個Tuple以及這個Tuple產生的所有Tuple都被成功處理, 會調用spout的ack方法;失敗是指這個Tuple或這個Tuple產生的所有Tuple中的某一個tuple處理失敗, 則會調用spout的fail方法;在處理tuple的每一個bolt都會通過OutputCollector來告知storm, 當前bolt處理是否成功。
2.ack原理:
Storm中有個特殊的task名叫acker,他們負責跟蹤spout發出的每一個Tuple的Tuple樹。當acker(框架自啟動的task)發現一個Tuple樹已經處理完成了,它會發送一個消息給產生這個Tuple的那個task。
2.實現:
1.spout處理:
(1)spout往后發射tuple時,需要指定一個msgId。
2.bolt處理:
(1)bolt處理接收到tuple,如果還需要繼續往后邊的bolt發射,需要追溯前邊的tuple(這么做的目的是構建Tuple樹)
collector.emit(input,new Values(word));
(2)處理完bolt,一定要調用collector的ack方法,
四. Trident介紹和實現:
1. 問題:
前邊介紹的基礎的storm都是逐條處理數據的(一個tuple、一個tuple處理)。在生產環境中,一般都是Kafka + Storm + HBase/Redis 架構處理實時數據。如果只是逐條處理的話,對下游數據庫(HBase、Redis)的壓力就會非常大。
Trident是Storm提供的解決方案,一個批次一個批次處理實時數據,其中一個批次封裝了多條tuple。Trident能夠提高數據處理效率和性能,同時也減小了對后端數據庫的壓力。因為Trident是以批次為單位來處理數據的,所以這里就涉及到事務的問題。Trident中已經封裝了事務管理、狀態管理的功能(框架幫我們自動實現),而且還封裝了一系列的常用操作,鏈式調用。真正實現流式處理數據。
Storm從0.7版本開始引入事務管理,之前版本中提供的Transactional Topology API已經廢棄不用了。
2. Storm事務管理:
Storm事務管理分為3個層次:
(1)No Transactional:
不進行事務管理。一個批次中的tuple可能有的成功,有的失敗,不限制一致性。tuple處理成功次數可能不止一次,同一個tuple可能在多個批次中處理,并且都成功,也可能一次都不成功。
(2)Transactional :
保證tuple只會在一個批次中出現,即使失敗重試,tuple的批次號還是不變的,同一個tuple保證最多成功一次。
(3)Opaque Transactional:
不透明事務,和第2種類似。相比于第2種,提供了容錯的機制。某些tuple在某個批次中處理失敗后,可以在另外一個批次里處理成功(失敗后,將該tuple轉到另外一個批次中處理),但不會成功多次。
3. Storm事務原理:
(1)將多條tuple封裝成一個批次,并且給該批次指定一個唯一的批次號(batchId)。
(2)后邊組件處理數據按照批次先后順序處理(前邊的批次更新后,才能處理后邊的批次),結果的更新,一定是前面的批次更新成功后才能進行后面的批次結果更新。
4. WordCountTridentTopology實現:
TridentTopology需要開發自己的spout(以前是逐條發送tuple,現在的需要將多條tuple封裝成一個batch發送),自己的function(在trident中不叫bolt,而是function,實現的功能與bolt一樣),下面實現了一個最簡單的實例:
https://github.com/neil-ma/storm-pmpa/tree/master/storm-pirate/src/main/java/com/pmpa/storm/wordstrident
五. Trident編程:
1. 編寫Trident Spout:
編寫Trident Spout需要自行實現將tuple打包成batch的邏輯。