jstorm 核心

1.api介紹

生成Topology

Map conf = new HashMp();
//topology所有自定義的配置均放入這個Map

TopologyBuilder builder = new TopologyBuilder();
//創建topology的生成器

int spoutParal = get("spout.parallel", 1);
//獲取spout的并發設置

SpoutDeclarer spout = builder.setSpout(SequenceTopologyDef.SEQUENCE_SPOUT_NAME,
                new SequenceSpout(), spoutParal);
//創建Spout, 其中new SequenceSpout() 為真正spout對象,SequenceTopologyDef.SEQUENCE_SPOUT_NAME 為spout的名字,注意名字中不要含有空格

int boltParal = get("bolt.parallel", 1);
//獲取bolt的并發設置

BoltDeclarer totalBolt = builder.setBolt(SequenceTopologyDef.TOTAL_BOLT_NAME, new TotalCount(),
                boltParal).shuffleGrouping(SequenceTopologyDef.SEQUENCE_SPOUT_NAME);
//創建bolt, SequenceTopologyDef.TOTAL_BOLT_NAME 為bolt名字,TotalCount 為bolt對象,boltParal為bolt并發數,
//shuffleGrouping(SequenceTopologyDef.SEQUENCE_SPOUT_NAME), 
//表示接收SequenceTopologyDef.SEQUENCE_SPOUT_NAME的數據,并且以shuffle方式,
//即每個spout隨機輪詢發送tuple到下一級bolt中

int ackerParal = get("acker.parallel", 1);
Config.setNumAckers(conf, ackerParal);
//設置表示acker的并發數

int workerNum = get("worker.num", 10);
conf.put(Config.TOPOLOGY_WORKERS, workerNum);
//表示整個topology將使用幾個worker

conf.put(Config.STORM_CLUSTER_MODE, "distributed");
//設置topolog模式為分布式,這樣topology就可以放到JStorm集群上運行

StormSubmitter.submitTopology(streamName, conf,
                builder.createTopology());
//提交topology

IRichSpout
IRichSpout 為最簡單的Spout接口

 IRichSpout{

    @Override
    public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
    }

    @Override
    public void close() {
    }

    @Override
    public void activate() {
    }

    @Override
    public void deactivate() {
    }

    @Override
    public void nextTuple() {
    }

    @Override
    public void ack(Object msgId) {
    }

    @Override
    public void fail(Object msgId) {
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
    }

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

其中注意:
=>spout對象必須是繼承Serializable, 因此要求spout內所有數據結構必須是可序列化的
=>spout可以有構造函數,但構造函數只執行一次,是在提交任務時,創建spout對象,因此在task分配到具體worker之前的初始化工作可以在此處完成,一旦完成,初始化的內容將攜帶到每一個=>task內(因為提交任務時將spout序列化到文件中去,在worker起來時再將spout從文件中反序列化出來)。
=>open是當task起來后執行的初始化動作
=>close是當task被shutdown后執行的動作
=>activate 是當task被激活時,觸發的動作
=>deactivate 是task被deactive時,觸發的動作
=>nextTuple 是spout實現核心, nextuple完成自己的邏輯,即每一次取消息后,用collector 將消息emit出去。
=>ack, 當spout收到一條ack消息時,觸發的動作,詳情可以參考 ack機制
=>fail, 當spout收到一條fail消息時,觸發的動作,詳情可以參考 ack機制
=>declareOutputFields, 定義spout發送數據,每個字段的含義
=>getComponentConfiguration 獲取本spout的component 配置

Bolt

IRichBolt {

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

    @Override
    public void execute(Tuple input) {
    }

    @Override
    public void cleanup() {
    }

    @Override
    public void declareOutputFields(OutputFieldsDeclarer declarer) {
    }

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

}

其中注意:
=>bolt對象必須是繼承Serializable, 因此要求spout內所有數據結構必須是可序列化的
=>bolt可以有構造函數,但構造函數只執行一次,是在提交任務時,創建bolt對象,因此在task分配到具體worker之前的初始化工作可以在此處完成,一旦完成,初始化的內容將攜帶到每一個task內(因為提交任務時將bolt序列化到文件中去,在worker起來時再將bolt從文件中反序列化出來)。
=>prepare是當task起來后執行的初始化動作
=>cleanup是當task被shutdown后執行的動作
=>execute是bolt實現核心, 完成自己的邏輯,即接受每一次取消息后,處理完,有可能用collector 將產生的新消息emit出去。 ** 在executor中,當程序處理一條消息時,需要執行collector.ack, 詳情可以參考 ack機制 ** 在executor中,當程序無法處理一條消息時或出錯時,需要執行collector.fail ,詳情可以參考 ack機制
=>declareOutputFields, 定義bolt發送數據,每個字段的含義
=>getComponentConfiguration 獲取本bolt的component 配置

     <dependency>
            <groupId>com.alibaba.jstorm</groupId>
            <artifactId>jstorm-client</artifactId>
            <version>0.9.3.1</version>
            <scope>provided</scope>
        </dependency> 


         <dependency>
            <groupId>com.alibaba.jstorm</groupId>
            <artifactId>jstorm-client-extension</artifactId>
            <version>0.9.3.1</version>
            <scope>provided</scope>
        </dependency>

打包

<build>
        <plugins>

            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <archive>
                        <manifest>
                            <mainClass>storm.starter.SequenceTopology</mainClass>
                        </manifest>
                    </archive>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

提交jar
xxxx.jar 為打包后的jar
com.alibaba.xxxx.xx 為入口類,即提交任務的類
parameter即為提交參數

jstorm jar xxxxxx.jar com.alibaba.xxxx.xx parameter

2.Ack原理

Storm中有個特殊的task名叫acker,他們負責跟蹤spout發出的每一個Tuple的Tuple樹(因為一個tuple通過spout發出了,經過每一個bolt處理后,會生成一個新的tuple發送出去)。當acker(框架自啟動的task)發現一個Tuple樹已經處理完成了,它會發送一個消息給產生這個Tuple的那個task。Acker的跟蹤算法是Storm的主要突破之一,對任意大的一個Tuple樹,它只需要恒定的20字節就可以進行跟蹤。

Acker跟蹤算法的原理:acker對于每個spout-tuple保存一個ack-val的校驗值,它的初始值是0,然后每發射一個Tuple或Ack一個Tuple時,這個Tuple的id就要跟這個校驗值異或一下,并且把得到的值更新為ack-val的新值。那么假設每個發射出去的Tuple都被ack了,那么最后ack-val的值就一定是0。Acker就根據ack-val是否為0來判斷是否完全處理,如果為0則認為已完全處理。
要實現ack機制:

1,spout發射tuple的時候指定messageId
2,spout要重寫BaseRichSpout的fail和ack方法
3,spout對發射的tuple進行緩存(否則spout的fail方法收到acker發來的messsageId,spout也無法獲取到發送失敗的數據進行重發),看看系統提供的接口,只有msgId這個參數,這里的設計不合理,其實在系統里是有cache整個msg的,只給用戶一個messageid,用戶如何取得原來的msg貌似需要自己cache,然后用這個msgId去查詢,太坑爹了3,spout根據messageId對于ack的tuple則從緩存隊列中刪除,對于fail的tuple可以選擇重發。
4,設置acker數至少大于0;Config.setNumAckers(conf, ackerParal);

阿里自己的Jstorm會提供
public interface IFailValueSpout { void fail(Object msgId, List<object>values); }
這樣更合理一些, 可以直接取得系統cache的msg values

ack機制即,spout發送的每一條消息,在規定的時間內,spout收到Acker的ack響應,即認為該tuple 被后續bolt成功處理

在規定的時間內(默認是30秒),沒有收到Acker的ack響應tuple,就觸發fail動作,即認為該tuple處理失敗,timeout時間可以通過Config.TOPOLOGY_MESSAGE_TIMEOUT_SECS來設定。
l或者收到Acker發送的fail響應tuple,也認為失敗,觸發fail動作
注意,我開始以為如果繼承BaseBasicBolt那么程序拋出異常,也會讓spout進行重發,但是我錯了,程序直接異常停止了
這里我以分布式程序入門案例worldcount為例子吧。

Paste_Image.png

問題:

有沒有想過,如果該tuple的眾多子tuple中,某一個子tuple處理
failed了,但是另外的子tuple仍然會繼續執行,如果子tuple都是執
行數據存儲操作,那么就算整個消息失敗,那些生成的子tuple還
是會成功執行而不會回滾的。

(1)關于Storm如何處理重復的tuple問題

有人問到Storm 是怎么處理重復的tuple?
因為Storm 要保證tuple 的可靠處理,當tuple 處理失敗或者超時的時候,spout 會fail并重新發送該tuple,那么就會有tuple 重復計算的問題。這個問題是很難解決的,storm也沒有提供機制幫助你解決。不過也有一些可行的策略:
(1)不處理,這也算是種策略。因為實時計算通常并不要求很高的精確度,后
續的批處理計算會更正實時計算的誤差。
(2)使用第三方集中存儲來過濾,比如利用MySQL、MemCached 或者Redis 根據邏輯主鍵來去重。
(3)使用bloom filter 做過濾,簡單高效。

(2)關于Storm的ack和fail問題

在學習storm的過程中,有不少人對storm的Spout組件中的ack及fail相關的問題存在困惑,這里做一個簡要的概述。

Storm保證每一個數據都得到有效處理,這是如何保證的呢?正是ack及fail機制確保數據都得到處理的保證,但是storm只是提供給我們一個接口,而具體的方法得由我們自己來實現。例如在spout下一個拓撲節點的bolt上,我們定義某種情況下為數據處理失敗,則調用fail,則我們可以在fail方法中進行數據重發,這樣就保證了數據都得到了處理。其實,通過讀storm的源碼,里面有講到,有些類(BaseBasicBolt?)是會自動調用ack和fail的,不需要我們程序員去ack和fail,但是其他Bolt就沒有這種功能了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容

  • Date: Nov 17-24, 2017 1. 目的 積累Storm為主的流式大數據處理平臺對實時數據處理的相關...
    一只很努力爬樹的貓閱讀 2,194評論 0 4
  • Storm入門系列之一:storm核心概念及特性 本文的將介紹一些 storm 入門的基礎知識,包括 storm ...
    zhaif閱讀 3,146評論 0 17
  • 目錄 場景假設 調優步驟和方法 Storm 的部分特性 Storm 并行度 Storm 消息機制 Storm UI...
    mtide閱讀 17,174評論 30 60
  • 一. wordCount Topology開發: 1.spout數據收集器(SentenceSpout類): 有...
    奉先閱讀 1,198評論 0 0
  • 20170902周慧心賞第19天 親愛的老公,最近一段時間,你見到我父母都很有禮貌的叫一聲爸,叫一聲媽,出...
    hmzhou閱讀 179評論 0 1