該文檔為實實在在的原創文檔,轉載請注明:
類型
|
詳細
|
備注
|
該文檔是群里幾個朋友在storm實戰中遇到的一些問題,及其對應解決辦法。
|
相關描述
|
部分文檔涉及到源碼,有需要的博客留言,關注我的博客。
歡迎加入storm-分布式-IT技術交流群(
191321336
,群中有詳細的資料),一起討論技術,一起分享代碼,一起分享設計。
|
目錄
文檔說明
該 文檔包涵了storm實戰中經常遇到一些問題,及對應解決方案。這個文檔是群里一個朋友在學習storm,並實戰storm中遇到的一些問題,及和群里其 他朋友一起交流給出的對應解決方案,並由他整理好,委托我發布出來(也算是交流者之一),供大家參考,希望能對大家有所幫助。
感謝 某某(哈哈 鑒於部分原因,不便透露名字~~~~!)…
問題錦集
1 關於Storm集群
1.1 關於storm集群的環境變量配置問題
安 裝好JDK后,需要配置環境變量,通常情況下出於經驗,我們往往會修改/etc/profile的值進行環境變量配置,但這在安裝JDK以及后面安裝的 storm集群、zookeeper集群以及metaq集群時會出問題,這時候我們需要在/etc/.bashrc文件中加入環境變量,不然安裝的 java和ZK集群等就無法使用,尤其這個問題在我用shell寫調度腳本去啟動storm集群的時候就遇到過,如果沒有將java的環境變量配置在 /etc/.bashrc文件中,就會報一個錯,這個問題在后面我會提到。
1.2 關於zookeeper集群安裝問題
記 得剛剛接觸storm,在安裝zookeeper集群的時候有這樣的考慮:為什么不可以把zookeeper只安裝在nimbus上,然后讓其他的 supervisor來它這里讀取任務?如果在每台機器上都安裝zookeeper,那nimbus分配任務的時候,是每台機器上的zookeeper都 收到同一份的任務,還是只是將分配給每個supervisor節點的那部分寫到同一節點上的zookeeper中?
有 朋友解答說:ZK也是以集群的方式工作的,ZK集群內部有他自己的一套相互通信機制,而storm正是要借助其通訊機制,例如任務下發等,往往在執行一個 任務的時候,storm會把任務及相關執行的代碼經過序列化之后發送到各個ZK節點供supervisor去下載,然后才會各自執行自己部分的代碼或者任 務。說的直接一點就是每個ZK節點收到的任務是一樣的,而supervisor只需要下載屬於自己的任務即可。
1.3 關於Storm中tuple 的可靠處理問題
Storm 為了保證tuple 的可靠處理,需要保存tuple 信息,這樣會不會導致內存泄漏?
關 於這個問題,其實網上是有資料進行了詳細的解釋的。這里只是大概將一下,如果還不明白,可以上網搜搜“storm可靠處理”。Storm 為了保證tuple 的可靠處理,acker 會保存該節點創建的tuple id的xor (異或)值,這個值稱為ack value,那么每ack 一次,就將tuple id 和ack value做異或(xor)。當所有產生的tuple 都被ack 的時候,ack value 必定為0。這是個很簡單的策略,對於每一個tuple 也只要占用約20 個字節的內存。對於100萬tuple,也才20M 左右,所以一般情況下是不用考慮內存泄漏問題的。
1.4 關於storm計算結果的存放問題
很多人在剛剛學習Storm 的時候都會有這個問題:storm處理后的結果保存在哪里? 內存中?還是其他地方?
官 方解釋說: Storm 是不負責保存計算結果的,這是應用程序里需要負責的事情,如果數據不大,你可以簡單地保存在內存里,也可以每次都更新數據庫,也可以采用NoSQL存儲。 storm 並沒有像s4 那樣提供一個Persist API,根據時間或者容量來做存儲輸出。這部分事情完全交給用戶。數據存儲之后的展現,也是你需要自己處理的,storm UI 只提供對topology 的監控和統計。
1.5 關於Storm如何處理重復的tuple問題
有人問到Storm 是怎么處理重復的tuple?
因為Storm 要保證tuple 的可靠處理,當tuple 處理失敗或者超時的時候,spout 會fail 並重新發送該tuple,那么就會有tuple 重復計算的問題。這個問題是很難解決的,storm 也沒有提供機制幫助你解決。不過也有一些可行的策略:
(1)不處理,這也算是種策略。因為實時計算通常並不要求很高的精確度,后
續的批處理計算會更正實時計算的誤差。
(2)使用第三方集中存儲來過濾,比如利用MySQL、MemCached 或者Redis 根據邏輯主鍵來去重。
(3)使用bloom filter 做過濾,簡單高效。
1.6 關於task與executor的關系問題
在storm的學習過程中,有許多人問到task與executor的關系問題。
在 我們安裝配置storm的時候,不知大家是否主要到了一個問題,就是我們在配置的時候會加幾個worker的端口( supervisor.slots.ports:),比如眾多文檔中提到的6700/6701等等類似的東西。沒錯,這就是我們定義了該 supervisor最多的worker數,worker中執行一個bolt或者spout線程,我們就稱之為task,而executor是物理上的線 程概念,我們可以將其稱為執行線程;而task更多是邏輯概念上的,有時候bolt與spout的task會共用一個executor,特別是在系統負荷 比較高的時候。
1.7 關於Storm UI顯示內容的問題
Storm UI 里spout 統計的complete latency 的具體含義是什么?為什么emit 的數目會是acked 的兩倍?
簡 單地說,complete latency 表示了tuple 從emit 到被acked 經過的時間,可以認為是tuple 以及該tuple 的后續子孫(形成一棵樹)整個處理時間。其次spout 的emit 和transfered 還統計了spout 和acker 之間內部的通信信息,比如對於可靠處理的spout 來說,會在emit 的時候同時發送一個_ack_init 給acker,記錄tuple id 到task id 的映射,以便ack 的時候能找到正確的acker task。
1.8 關於Storm的ack和fail問題
在學習storm的過程中,有不少人對storm的Spout組件中的ack及fail相關的問題存在困惑,這里做一個簡要的概述。
Storm 保證每一個數據都得到有效處理,這是如何保證的呢?正是ack及fail機制確保數據都得到處理的保證,但是storm只是提供給我們一個接口,而具體的 方法得由我們自己來實現。例如在spout下一個拓撲節點的bolt上,我們定義某種情況下為數據處理失敗,則調用fail,則我們可以在fail方法中 進行數據重發,這樣就保證了數據都得到了處理。其實,通過讀storm的源碼,里面有講到,有些類(BaseBasicBolt?)是會自動調用ack和 fail的,不需要我們程序員去ack和fail,但是其他Bolt就沒有這種功能了。
1.9 關於IRichBolt與IBasicBolt接口的區別
首 先從類的組成上進行分析可以看到,IBasicBolt接口只有execute方法和declareOutputFields方法,而IRichBolt 接口上除了以上幾個方法還有prepare方法和cleanup及map方法。而且其中execute方法是有些不一樣的,其參數列表不同。
總 體來說Rich方法比較完善,我們可以使用prepare方法進行該Bolt類的初始化工作,例如我們鏈接數據庫時,需要進行一次數據庫連接操作,我們就 可以把該操作放入prepare中,只需要執行一次就可以了。而cleanup方法能在該類調用結束時進行收尾工作,往往在處理數據的時候用到,例如在寫 hdfs(hadoop的文件系統)數據的時候,在結束時需要進行數據clear,則需要進行數據收尾。當然,根據官網及實際的測驗,該方法往往是執行失 敗的。
2 關於Topology發布
2.1 發布topologies 到遠程集群時,出現Nimbus host is not set 異常
原因是Nimbus 沒有被正確啟動起來,可能是storm.yaml 文件沒有配置,或者配置有問題。
解決方法:打開storm.yaml 文件正確配置:nimbus.host: "xxx.xxx.xxx.xxx",重啟nimbus 后台程序即可。
2.2 發布topology到遠程集群時,出現AlreadyAliveException(msg: xxx is already active)異常
原因是提供的topology 與已經在運行的topology 重名。
解決方法:發布時換一個拓撲名稱即可。
2.3 啟動Supervisor 時,出現java.lang.UnsatisfiedLinkError
具體信息:啟動Supervisor 時,出現java.lang.UnsatisfiedLinkError:
/usr/local/lib/libjzmq.so.0.0.0: libzmq.so.1: cannot open shared object
file: No such file or directory 異常。
原因是未找到zmq 動態鏈接庫。
解決方法1:配置環境變量 export LD_LIBRARY_PATH=/usr/local/lib
解決方法2:編輯/etc/ld.so.conf 文件,增加一行:/usr/local/lib。再執行
sudo ldconfig 命令,重啟Supervisor。
2.4 發布topologies 時,出現不能序列化log4j.Logger 的異常
原因是日志系統無法正確支付序列化。
解決方法:使用slf4j 代替log4j。
2.5 bolt 在處理消息時,worker 的日志中出現Failing message
原因:可能是因為Topology 的消息處理超時所致。
解決方法:提交Topology 時設置適當的消息超時時間,比默認消息超時時間(30
秒)更長。比如:
conf.setMessageTimeoutSecs(60);
2.6 在打包toplogy工程的時候, 如果采用assembly方式, 對於相關的依賴的配置一般要這樣:
Xml
代碼
1.
<dependencySets>
2.
<dependencySet>
3.
<outputDirectory>/
</outputDirectory>
4.
<unpack>true
</unpack>
5.
<excludes>
6.
<exclude>storm:storm
</exclude>
7.
</excludes>
8.
</dependencySet>
9.
</dependencySets>
wiki上說可以 用<scope>compile</scope>。然后將storm依賴設置為runtime, 貌似不行。 另外就是所有的依賴包將全部解壓, 然后將所有依賴的配置和class文件生成一個文件。這個是通過<unpack>true</unpack>參數來控制的。
2.7 在提交topology的時候有時可能出現如下異常:
Exception in thread "main" java.lang.IllegalArgumentException: Nimbus host is not set
at backtype.storm.utils.NimbusClient.<init>(NimbusClient.java:30)
at backtype.storm.utils.NimbusClient.getConfiguredClient(NimbusClient.java:17)
at backtype.storm.StormSubmitter.submitJar(StormSubmitter.java:78)
at backtype.storm.StormSubmitter.submitJar(StormSubmitter.java:71)
at backtype.storm.StormSubmitter.submitTopology(StormSubmitter.java:50)
at com.taobao.kaleidoscope.storm.IcdbTopology.main(IcdbTopology.java:59)
但是啟動nimbus是沒有問題的, 這個主要因為conf_dir路徑設置不正確, 在bin/storm腳本中需要加上這樣一句:
Python
代碼
1. CONF_DIR = STORM_DIR + "/conf"
3 關於DRPC
3.1 發布drpc 類型的topologies 到遠程集群時,出現空指針異常,連接drpc服務器失敗
原因是未正確配置drpc 服務器地址。
解決方法:在conf/storm.yaml 文件中增加drpc 服務器配置,啟動配置文件中
指定的所有drpc 服務。內容如下:
drpc.servers:
- "drpc 服務器ip"
3.2 客戶端調用drpc 服務時,worker 的日志中出現Failing message,而bolt都未收到數據
錯誤日志如下所示:
2011-12-02 09:59:16 task [INFO] Failing message
backtype.storm.drpc.DRPCSpout$DRPCMessageId@3770bdf7: source: 1:27,
stream: 1, id: {-5919451531315711689=-5919451531315711689},
[foo.com/blog/1, {"port":3772,"id":"5","host":"10.0.0.24"}]
原因是主機名,域名,hosts 文件配置不正確會引起這類錯誤。
解決方法:檢查並修改storm 相關機器的主機名,域名,hosts 文件。重啟網絡服務:service network restart。重啟storm,再次調用drpc 服務,成功。Hosts 文件中必須包含如下
內容:
[nimbus 主機ip] [nimbus 主機名] [nimbus 主機別名]
[supervisor 主機ip] [supervisor 主機名] [supervisor 主機別名]
[zookeeper 主機ip] [zookeeper 主機名] [zookeeper 主機別名]
4 關於jzmq安裝
4.1 storm 啟動時報no jzmq in java.library.path 錯誤
原因是找不到jzmq,默認情況下在執行install_zmq.sh 時,那些.so 文件
安裝路徑在/usr/local/lib,但是實際安裝時可能裝在其他的路徑下了。
解決方法:在storm.yaml 中添加:
java.library.path:
"/opt/storm/jzmq/lib:/opt/storm/zeromq/lib:/usr/local/lib:/opt/local/
lib:/usr/lib"
4.2 安裝jzmq 時遇到No rule to make target ‘classdist_noinst.stamp’的make 錯誤
具體的make 錯誤信息:
make[1]: *** No rule to make target `classdist_noinst.stamp',needed by `org/zeromq/ZMQ.class'. Stop.
解決方法:手動創建classdist_noinst.stamp 空文件。
touch src/classdist_noinst.stamp
4.3 安裝jzmq 時遇到cannot access org.zeromq.ZMQ 的make 錯誤
具體的make 錯誤信息:
error: cannot access org.zeromq.ZMQ class file for org.zeromq.ZMQ not found
javadoc: error - Class org.zeromq.ZMQ not found.
解決方法:手動編譯,然后重新make 即可通過。
cd src
javac -d . org/zeromq/*.java
cd ..
4.4 在部署storm節點的時候需要安裝jzmq和zeromq, 在安裝這兩個依賴包之后, 需要執行sudo -u root ldconfig. 否則會出現異常:
2012-02-24 16:30:30 worker [ERROR] Error on initialization of server mk-worker
java.lang.UnsatisfiedLinkError: /usr/local/lib/libjzmq.so.0.0.0: libzmq.so.1: cannot open shared object file: No such file or
directory
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1803)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1728)
at java.lang.Runtime.loadLibrary0(Runtime.java:823)
at java.lang.System.loadLibrary(System.java:1028)
at org.zeromq.ZMQ.<clinit>(ZMQ.java:34)
5 關於Storm的配置問題
1. yaml跟我們一般用的屬性配置文件有所不同, 它的要求更嚴格一些, 因此在往conf/storm.yaml中添加配置的時候必須注意,比如必須注意開始位置和冒號后面的空格, 否則配置不會生效。
2. 如何檢查配置是否生效?
可以使用命令: storm localconfvalue 配置關鍵字
但是這個命令只能在nimbus上生效, 在supervisor看到的還是默認值. 不知道為什么 。
6 關閉storm相關進程
6.1 關閉nimbus相關進程:
kill `ps aux | egrep '(daemon\.nimbus)|(storm\.ui\.core)' | fgrep -v egrep | awk '{print $2}'`
備注:這是在網上看到的,沒有經過實際測試,有興趣的朋友可以自己測試一下。
6.2 干掉supervisor上的所有storm進程:
kill `ps aux | fgrep storm | fgrep -v 'fgrep' | awk '{print $2}'`
備注:這是在網上看到的,沒有經過實際測試,有興趣的朋友可以自己測試一下。
7 關於Topology發布之后的log
1) 用storm jar ...將項目提交給storm集群后,想查看本項目的log信息,要到supervisor機器的:storm安裝路徑/logs/worker-number.log(其中的number視實際情況而定)中查看。
2) 如果是用daemontools啟動的storm,daemontools監控的目錄是/service/storm,那么到/service/storm/logs中查看worker-number.log日志。
3) 若要更改log的級別,是debug還是info等,在storm安裝路徑/log4j下有個配置文件,按需要修改即可。
4) Storm的debug模式下,它本身的log非常龐大,所以我覺得自己的代碼中有些重要的信息,用info比較好,這樣將storm的log級別調整為info比較方便查看。
8 關於maven打包問題
8.1 首先maven的pom文件中的storm依賴,要么加exclude storm的相關語句(github有說明),要么加<scope>,如下:
<dependency>
<groupId>storm</groupId>
<artifactId>storm</artifactId>
<scope>test</scope>
</dependency>
加scope可以使打jar包時,不包含storm。如果包含了storm,那么提交到storm集群,會運行出錯。官方要求打jar包時,要去除storm的依賴。
8.2 使用maven插件,在打jar包時,包含依賴。
在pom中加入:
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.path.to.main.Class</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
打jar包時使用命令:mvn assembly:assembly
8.3 依賴的jar沖突問題
如果本地依賴的jar與storm的lib下的jar有沖突,即都用了一個jar,但是版本不同,那么貌似目前只能改為跟storm保持統一。官方的討論組是這樣說的。
9 關於nimbus的啟動問題
9.1 Storm nimbus啟動失敗
在使用了storm一段時間后,需要重新部署storm的集群,主要是想將storm部署在其它機器上。做了以下錯誤操作:
1) 沒有kill 正在運行的topology,kill nimbus和supervisor的storm進程
2) 刪除了配置中"storm.local.dir"的文件夾內的內容
3) 啟動storm nimbus
報錯:
backtype.storm.daemon.nimbus
$fn__2692$exec_fn__945__auto____2693$this__2731@62135133
java.io.FileNotFoundException: File '/opt/apps-install/storm/
storm_local/nimbus/stormdist/appFailed-6-1325065153/stormconf.ser'
does not exist
at
org.apache.commons.io.FileUtils.openInputStream(FileUtils.java:137)
at
org.apache.commons.io.FileUtils.readFileToByteArray(FileUtils.java:
1135)
at backtype.storm.daemon.nimbus
$read_storm_conf.invoke(nimbus.clj:128)
at backtype.storm.daemon.nimbus
$compute_new_task__GT_node_PLUS_port.invoke(nimbus.clj:244)
at backtype.storm.daemon.nimbus
$mk_assignments.invoke(nimbus.clj:288)
at backtype.storm.daemon.nimbus
$fn__2692$exec_fn__945__auto____2693$this__2731.invoke(nimbus.clj:460)
at backtype.storm.event$event_manager
$fn__2068$fn__2069.invoke(event.clj:25)
at backtype.storm.event$event_manager
$fn__2068.invoke(event.clj:22)
at clojure.lang.AFn.run(AFn.java:24)
at java.lang.Thread.run(Thread.java:662)
2011-12-29 16:15:02 util [INFO] Halting process: ("Error when
processing an event")
報錯原因:因為沒有先kill topology,所以在啟動nimbus時,zookeeper中依然保留了上次運行着的topology的信息。
解決辦法:用zookeeper的zkCli.sh清理一下,我直接重裝了zookeeper。但是據說在storm 0.6.1中解決了該bug。而我用的是storm 0.6.0。
10 Storm使用JVM參數
在配置文件storm.yaml中,有:
# to nimbus
nimbus.childopts: "-Xmx1024m"
# to supervisor
supervisor.childopts: "-Xmx1024m"
# to worker
worker.childopts: "-Xmx768m"
如果worker在運行時,需要用指定的JVM參數,那么可以像這樣配置:
worker.childopts: "-Dworker=worker -Xmx768m -Xdebug –Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8111,suspend=y,server=y "
11 關於spout/bolt的生命周期
一般來說spout/bolt的生命周期如下:
1 在提交了一個topology之后(在nimbus所在的機器), 創建spout/bolt實例(spout/bolt在storm中統稱為component)並進行序列化;
2 將序列化的component發送給所有的任務所在的機器;
3 在每一個任務上反序列化component;
4 在開始執行任務之前, 先執行component的初始化方法(bolt是prepare, spout是open);
因此component的初始化操作應該在prepare/open方法中進行, 而不是在實例化component的時候進行。
12關於storm與spring框架集成問題
首先聲明一下,這個問題是當時有考慮到是否可以將storm與spring集成時,在網上看到的一點介紹,只是為了日后做參考。
在 進行storm與spring集成時,本來想着一次就能成功,抱着很大的希望可是運行時竟然報了個 java.io.NotSerializableException的異常。該異常要求被依賴注入的jar包實現序列化接口,但那些jar包都是別人開發 的你不能一個一個都改掉
源碼才能用到項目里。
再網上找一下還真有人遇到類似的問題,具體原因是對storm的spout和bolt的生命周期理解的不夠深刻。
一般來說spout/bolt的生命周期如下:
1.在提交了一個topology之后(在nimbus所在的機器), 創建spout/bolt實例(spout/bolt在storm中統稱為component)並進行序列化.
2.將序列化的component發送給所有的任務所在的機器
3.在每一個任務上反序列化component.
4.在開始執行任務之前, 先執行component的初始化方法(bolt是prepare, spout是open).
因此component的初始化操作應該在prepare/open方法中進行, 而不是在實例化component的時候進行.
按照這種說法進行改造,結構該問題消失了。但接下來又有了新的問題:
Caused by: org.xml.sax.SAXParseException: Content is not allowed in prolog.
這個異常網上搜索之后發現原來是由於*.xml文件編碼的問題。原因是在從其他項目里或者編輯工具編輯時,在文件編碼中加入了BOM頭的原因,於是用notePad++打開xml文件選擇去掉BOM頭信息,重新進行保存即可。
13 關於java.lang.NoClassDefFoundError: clojure.core.protocols$
原因:JDK版本不匹配,安裝虛擬機時系統自帶一個jdk.1.5.0。
解決辦法:檢查jdk版本,卸載系統自帶的JDK,使用自己安裝的JDK版本。
# rpm –qa | grep java
# rpm –e –nodeps java-*
配置環境變量,vi /etc/profile
重新執行一遍試試,貌似問題解決了。
14 關於storm連接Mysql
連接遠程mysql是報如下錯誤:
message from server:"Host FILTER" is not allowed to connect to this MySQL server
解決方案:
很可能是你沒有給其他IP訪問你數據庫的權限,你可以試試:
在MySql數據庫的主機上,在mysql命令行中輸入以下命令:
grant all on *.* to root@'%' identified by "111111" ;
這樣,給任何IP都賦予了訪問的權限,
任何IP都能以,用戶名:root ,密碼:111111
來進行局域網的訪問!
(命令中*.*是通配任何IP,你也可以指定IP)
15 關於metaq啟動的出現服務拒絕連接的問題
解決辦法:在metaq安裝目錄下,刪掉之前的日志文件,測試網絡是否正常連接。將之前的服務的metaq進程kill掉,然后重啟。
16 關於topology的spout與bolt
之 前有問到,一個topology中可不可以有多個spout?這個問題貌似很幼稚啊,呵呵。關於這個問題,我是這樣考慮的:實際應用中,如果我們每一條應 用都創建一個topology的話,未免也太誇張了。如果是同一個應用,同一個數據來源,但是你想分幾種方式對這個數據做處理的話,這時候就應該是建多個 spout了,讓這些spout並行去讀數據,然后交給訂閱這個spout的bolt去處理就行,沒必要一種處理方式建一個topology。
17 關於shell腳本編碼格式問題
這是我在寫啟動storm集群的shell腳本時遇到的一個實際問題。shell腳本運行時報錯誤:/bin/bash^M: bad interpreter
出現原因:windows上寫的腳本,直接拷貝到linux系統上運行由於格式不兼容導致。
17.1 解決方案(一):
1. 比如文件名為myshell.sh,vim myshell.sh
2. 執行vim中的命令 : set ff?查看文件格式,如果顯示fileformat=dos,證明文件格式有問題。
3. 執行vim中的命令 :set fileformat=unix 將文件格式改過來就可以了,然后:wq保存退出就可以了。
17.2 解決方案(二)
或者使用最笨的方法:將windows下編輯好的腳本通過txt文本格式轉換,然后在拷貝到linux下。
如果是使用Notepad編輯器進行編輯的話,可以在菜單欄上選擇“編輯”—“檔案格式轉換”—“轉換為 UNIX 格式”。
最 后說明一下,這些問題只是storm應用過程中遇到的一小部分問題,其實還有很多問題是涉及到實際項目的考慮的,比如集群硬件要求,參數配置,日志處理等 等,具體問題具體分析吧,也希望哪些在實際項目中用到storm的大神們,能多多和大家分享你們的實際經驗,畢竟實踐出真知,任何新技術,只有經過實際應 用和實際檢驗,分享出來的東西才有說服力。