该文档为实实在在的原创文档,转载请注明:php
http://blog.sina.com.cn/s/blog_8c243ea30101k0k1.htmlhtml
类型java |
详细node |
备注mysql |
该文档是群里几个朋友在storm实战中遇到的一些问题,及其对应解决办法。linux |
相关描述git |
² 其余相关文档请参考新浪博客http://blog.sina.com.cn/huangchongyuan程序员 ² 有任何其余想法,能够邮件874450476@qq.comgithub ² 文档及相关资料下载请我的360云盘http://yunpan.cn/QGf2GDaRFpcDt及百度文库、新浪爱问搜索。spring ² 部分文档涉及到源码,有须要的博客留言,关注个人博客。 ² 欢迎加入storm-分布式-IT技术交流群(191321336,群中有详细的资料),一块儿讨论技术,一块儿分享代码,一块儿分享设计。
|
目录
1.3 关于Storm中tuple 的可靠处理问题... 2
1.5 关于Storm如何处理重复的tuple问题... 3
1.9 关于IRichBolt与IBasicBolt接口的区别... 4
2.1 发布topologies 到远程集群时,出现Nimbus host is not set 异常... 4
2.2 发布topology到远程集群时,出现AlreadyAliveException(msg: xxx is alreadyactive)异常 4
2.3 启动Supervisor 时,出现java.lang.UnsatisfiedLinkError. 4
2.4 发布topologies 时,出现不能序列化log4j.Logger 的异常... 4
2.5 bolt 在处理消息时,worker 的日志中出现Failing message. 5
2.6 在打包toplogy工程的时候, 若是采用assembly方式, 对于相关的依赖的配置通常要这样: 5
2.7 在提交topology的时候有时可能出现以下异常: 5
3.1 发布drpc 类型的topologies 到远程集群时,出现空指针异常,链接drpc服务器失败... 6
3.2 客户端调用drpc 服务时,worker 的日志中出现Failingmessage,而bolt都未收到数据 6
4.1 storm 启动时报no jzmq in java.library.path 错误... 6
4.2 安装jzmq 时遇到No rule to make target ‘classdist_noinst.stamp’的make错误... 7
4.3 安装jzmq 时遇到cannot access org.zeromq.ZMQ 的make 错误... 7
4.4 在部署storm节点的时候须要安装jzmq和zeromq, 在安装这两个依赖包以后,须要执行sudo -u root ldconfig. 不然会出现异常: 7
6.2 干掉supervisor上的全部storm进程: 8
8.1 首先maven的pom文件中的storm依赖,要么加excludestorm的相关语句(github有说明),要么加<scope>,以下: 9
8.2 使用maven插件,在打jar包时,包含依赖。... 9
12 关于storm与spring框架集成问题... 11
13 关于java.lang.NoClassDefFoundError: clojure.core.protocols$.12
15 关于metaq启动的出现服务拒绝链接的问题... 13
该文档包涵了storm实战中常常遇到一些问题,及对应解决方案。这个文档是群里一个朋友在学习storm,并实战storm中遇到的一些问题,及和群里其余朋友一块儿交流给出的对应解决方案,并由他整理好,委托我发布出来(也算是交流者之一),供你们参考,但愿能对你们有所帮助。
感谢 某某(哈哈 鉴于部分缘由,不便透露名字~~~~!)…
安装好JDK后,须要配置环境变量,一般状况下出于经验,咱们每每会修改/etc/profile的值进行环境变量配置,但这在安装JDK以及后面安装的storm集群、zookeeper集群以及metaq集群时会出问题,这时候咱们须要在/etc/.bashrc文件中加入环境变量,否则安装的Java和ZK集群等就没法使用,尤为这个问题在我用shell写调度脚本去启动storm集群的时候就遇到过,若是没有将java的环境变量配置在/etc/.bashrc文件中,就会报一个错,这个问题在后面我会提到。
记得刚刚接触storm,在安装zookeeper集群的时候有这样的考虑:为何不能够把zookeeper只安装在nimbus上,而后让其余的supervisor来它这里读取任务?若是在每台机器上都安装zookeeper,那nimbus分配任务的时候,是每台机器上的zookeeper都收到同一份的任务,仍是只是将分配给每一个supervisor节点的那部分写到同一节点上的zookeeper中?
有朋友解答说:ZK也是以集群的方式工做的,ZK集群内部有他本身的一套相互通讯机制,而storm正是要借助其通信机制,例如任务下发等,每每在执行一个任务的时候,storm会把任务及相关执行的代码通过序列化以后发送到各个ZK节点供supervisor去下载,而后才会各自执行本身部分的代码或者任务。说的直接一点就是每一个ZK节点收到的任务是同样的,而supervisor只须要下载属于本身的任务便可。
Storm 为了保证tuple 的可靠处理,须要保存tuple 信息,这样会不会致使内存泄漏?
关于这个问题,其实网上是有资料进行了详细的解释的。这里只是大概将一下,若是还不明白,能够上网搜搜“storm可靠处理”。Storm为了保证tuple 的可靠处理,acker 会保存该节点建立的tuple id的xor (异或)值,这个值称为ackvalue,那么每ack 一次,就将tuple id 和ack value作异或(xor)。当全部产生的tuple 都被ack的时候,ack value 一定为0。这是个很简单的策略,对于每个tuple 也只要占用约20个字节的内存。对于100万tuple,也才20M 左右,因此通常状况下是不用考虑内存泄漏问题的。
不少人在刚刚学习Storm 的时候都会有这个问题:storm处理后的结果保存在哪里? 内存中?仍是其余地方?
官方解释说: Storm是不负责保存计算结果的,这是应用程序里须要负责的事情,若是数据不大,你能够简单地保存在内存里,也能够每次都更新数据库,也能够采用NoSQL存储。storm并无像s4 那样提供一个PersistAPI,根据时间或者容量来作存储输出。这部分事情彻底交给用户。数据存储以后的展示,也是你须要本身处理的,storm UI只提供对topology 的监控和统计。
有人问到Storm 是怎么处理重复的tuple?
由于Storm 要保证tuple 的可靠处理,当tuple 处理失败或者超时的时候,spout 会fail并从新发送该tuple,那么就会有tuple 重复计算的问题。这个问题是很难解决的,storm也没有提供机制帮助你解决。不过也有一些可行的策略:
(1)不处理,这也算是种策略。由于实时计算一般并不要求很高的精确度,后
续的批处理计算会更正实时计算的偏差。
(2)使用第三方集中存储来过滤,好比利用MySQL、MemCached 或者Redis 根据逻辑主键来去重。
(3)使用bloom filter 作过滤,简单高效。
在storm的学习过程当中,有许多人问到task与executor的关系问题。
在咱们安装配置storm的时候,不知你们是否主要到了一个问题,就是咱们在配置的时候会加几个worker的端口(supervisor.slots.ports:),好比众多文档中提到的6700/6701等等相似的东西。没错,这就是咱们定义了该supervisor最多的worker数,worker中执行一个bolt或者spout线程,咱们就称之为task,而executor是物理上的线程概念,咱们能够将其称为执行线程;而task更可能是逻辑概念上的,有时候bolt与spout的task会共用一个executor,特别是在系统负荷比较高的时候。
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。
在学习storm的过程当中,有很多人对storm的Spout组件中的ack及fail相关的问题存在困惑,这里作一个简要的概述。
Storm保证每个数据都获得有效处理,这是如何保证的呢?正是ack及fail机制确保数据都获得处理的保证,可是storm只是提供给咱们一个接口,而具体的方法得由咱们本身来实现。例如在spout下一个拓扑节点的bolt上,咱们定义某种状况下为数据处理失败,则调用fail,则咱们能够在fail方法中进行数据重发,这样就保证了数据都获得了处理。其实,经过读storm的源码,里面有讲到,有些类(BaseBasicBolt?)是会自动调用ack和fail的,不须要咱们程序员去ack和fail,可是其余Bolt就没有这种功能了。
首先从类的组成上进行分析能够看到,IBasicBolt接口只有execute方法和declareOutputFields方法,而IRichBolt接口上除了以上几个方法还有prepare方法和cleanup及map方法。并且其中execute方法是有些不同的,其参数列表不一样。
整体来讲Rich方法比较完善,咱们可使用prepare方法进行该Bolt类的初始化工做,例如咱们连接数据库时,须要进行一次数据库链接操做,咱们就能够把该操做放入prepare中,只须要执行一次就能够了。而cleanup方法能在该类调用结束时进行收尾工做,每每在处理数据的时候用到,例如在写hdfs(Hadoop的文件系统)数据的时候,在结束时须要进行数据clear,则须要进行数据收尾。固然,根据官网及实际的测验,该方法每每是执行失败的。
缘由是Nimbus 没有被正确启动起来,多是storm.yaml 文件没有配置,或者配置有问题。
解决方法:打开storm.yaml 文件正确配置:nimbus.host: "xxx.xxx.xxx.xxx",重启nimbus后台程序便可。
缘由是提供的topology 与已经在运行的topology 重名。
解决方法:发布时换一个拓扑名称便可。
具体信息:启动Supervisor 时,出现java.lang.UnsatisfiedLinkError:
/usr/local/lib/libjzmq.so.0.0.0: libzmq.so.1: cannot open sharedobject
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。
缘由是日志系统没法正确支付序列化。
解决方法:使用slf4j 代替log4j。
缘由:多是由于Topology 的消息处理超时所致。
解决方法:提交Topology 时设置适当的消息超时时间,比默认消息超时时间(30
秒)更长。好比:
conf.setMessageTimeoutSecs(60);
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>参数来控制的。
Exception in thread "main"java.lang.IllegalArgumentException: Nimbus host is notset
atbacktype.storm.utils.NimbusClient.<init>(NimbusClient.java:30)
atbacktype.storm.utils.NimbusClient.getConfiguredClient(NimbusClient.java:17)
atbacktype.storm.StormSubmitter.submitJar(StormSubmitter.java:78)
atbacktype.storm.StormSubmitter.submitJar(StormSubmitter.java:71)
atbacktype.storm.StormSubmitter.submitTopology(StormSubmitter.java:50)
atcom.taobao.kaleidoscope.storm.IcdbTopology.main(IcdbTopology.java:59)
可是启动nimbus是没有问题的, 这个主要由于conf_dir路径设置不正确,在bin/storm脚本中须要加上这样一句:
Python代码
1. CONF_DIR = STORM_DIR + "/conf"
缘由是未正确配置drpc 服务器地址。
解决方法:在conf/storm.yaml 文件中增长drpc 服务器配置,启动配置文件中
指定的全部drpc 服务。内容以下:
drpc.servers:
- "drpc 服务器ip"
错误日志以下所示:
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 networkrestart。重启storm,再次调用drpc 服务,成功。Hosts 文件中必须包含以下
内容:
[nimbus 主机ip] [nimbus 主机名] [nimbus 主机别名]
[supervisor 主机ip] [supervisor 主机名] [supervisor 主机别名]
[zookeeper 主机ip] [zookeeper 主机名] [zookeeper 主机别名]
缘由是找不到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"
具体的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
具体的make 错误信息:
error: cannot access org.zeromq.ZMQ class file fororg.zeromq.ZMQ not found
javadoc: error - Class org.zeromq.ZMQ not found.
解决方法:手动编译,而后从新make 便可经过。
cd src
javac -d . org/zeromq/*.java
cd ..
2012-02-24 16:30:30 worker [ERROR] Error oninitialization 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 fileor
directory
at java.lang.ClassLoader$NativeLibrary.load(NativeMethod)
atjava.lang.ClassLoader.loadLibrary0(ClassLoader.java:1803)
atjava.lang.ClassLoader.loadLibrary(ClassLoader.java:1728)
atjava.lang.Runtime.loadLibrary0(Runtime.java:823)
atjava.lang.System.loadLibrary(System.java:1028)
atorg.zeromq.ZMQ.<clinit>(ZMQ.java:34)
1. yaml跟咱们通常用的属性配置文件有所不一样, 它的要求更严格一些,所以在往conf/storm.yaml中添加配置的时候必须注意,好比必须注意开始位置和冒号后面的空格, 不然配置不会生效。
2. 如何检查配置是否生效?
可使用命令: storm localconfvalue 配置关键字
可是这个命令只能在nimbus上生效, 在supervisor看到的仍是默认值.不知道为何 。
kill `ps aux | egrep '(daemon\.nimbus)|(storm\.ui\.core)' |fgrep -v egrep | awk '{print $2}'`
备注:这是在网上看到的,没有通过实际测试,有兴趣的朋友能够本身测试一下。
kill `ps aux | fgrep storm | fgrep -v 'fgrep' | awk '{print$2}'`
备注:这是在网上看到的,没有通过实际测试,有兴趣的朋友能够本身测试一下。
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比较方便查看。
<dependency>
<groupId>storm</groupId>
<artifactId>storm</artifactId>
<scope>test</scope>
</dependency>
加scope可使打jar包时,不包含storm。若是包含了storm,那么提交到storm集群,会运行出错。官方要求打jar包时,要去除storm的依赖。
在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
若是本地依赖的jar与storm的lib下的jar有冲突,即都用了一个jar,可是版本不一样,那么貌似目前只能改成跟storm保持统一。官方的讨论组是这样说的。
在使用了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)
atbacktype.storm.daemon.nimbus
$read_storm_conf.invoke(nimbus.clj:128)
atbacktype.storm.daemon.nimbus
$compute_new_task__GT_node_PLUS_port.invoke(nimbus.clj:244)
atbacktype.storm.daemon.nimbus
$mk_assignments.invoke(nimbus.clj:288)
atbacktype.storm.daemon.nimbus
$fn__2692$exec_fn__945__auto____2693$this__2731.invoke(nimbus.clj:460)
atbacktype.storm.event$event_manager
$fn__2068$fn__2069.invoke(event.clj:25)
atbacktype.storm.event$event_manager
$fn__2068.invoke(event.clj:22)
atclojure.lang.AFn.run(AFn.java:24)
atjava.lang.Thread.run(Thread.java:662)
2011-12-29 16:15:02 util [INFO] Halting process: ("Errorwhen
processing an event")
报错缘由:由于没有先killtopology,因此在启动nimbus时,zookeeper中依然保留了上次运行着的topology的信息。
解决办法:用zookeeper的zkCli.sh清理一下,我直接重装了zookeeper。可是听说在storm0.6.1中解决了该bug。而我用的是storm 0.6.0。
在配置文件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"
通常来讲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的时候进行。
首先声明一下,这个问题是当时有考虑到是否能够将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 allowedin prolog.
这个异常网上搜索以后发现原来是因为*.xml文件编码的问题。缘由是在从其余项目里或者编辑工具编辑时,在文件编码中加入了BOM头的缘由,因而用notePad++打开xml文件选择去掉BOM头信息,从新进行保存便可。
缘由:JDK版本不匹配,安装虚拟机时系统自带一个jdk.1.5.0。
解决办法:检查jdk版本,卸载系统自带的JDK,使用本身安装的JDK版本。
# rpm –qa | grep java
# rpm –e –nodeps java-*
配置环境变量,vi /etc/profile
从新执行一遍试试,貌似问题解决了。
链接远程mysql是报以下错误:
message from server:"Host FILTER" is not allowed to connect tothis MySQL server
解决方案:
极可能是你没有给其余IP访问你数据库的权限,你能够试试:
在MySql数据库的主机上,在mysql命令行中输入如下命令:
grant all on *.* to root@'%' identified by "111111" ;
这样,给任何IP都赋予了访问的权限,
任何IP都能以,用户名:root ,密码:111111
来进行局域网的访问!
(命令中*.*是通配任何IP,你也能够指定IP)
解决办法:在metaq安装目录下,删掉以前的日志文件,测试网络是否正常链接。将以前的服务的metaq进程kill掉,而后重启。
以前有问到,一个topology中可不能够有多个spout?这个问题貌似很幼稚啊,呵呵。关于这个问题,我是这样考虑的:实际应用中,若是咱们每一条应用都建立一个topology的话,未免也太夸张了。若是是同一个应用,同一个数据来源,可是你想分几种方式对这个数据作处理的话,这时候就应该是建多个spout了,让这些spout并行去读数据,而后交给订阅这个spout的bolt去处理就行,不必一种处理方式建一个topology。
这是我在写启动storm集群的shell脚本时遇到的一个实际问题。shell脚本运行时报错误:/bin/bash^M: badinterpreter
出现缘由:windows上写的脚本,直接拷贝到linux系统上运行因为格式不兼容致使。
1. 好比文件名为myshell.sh,vim myshell.sh
2. 执行vim中的命令 : set ff?查看文件格式,若是显示fileformat=dos,证实文件格式有问题。
3. 执行vim中的命令 :set fileformat=unix将文件格式改过来就能够了,而后:wq保存退出就能够了。
或者使用最笨的方法:将windows下编辑好的脚本经过txt文本格式转换,而后在拷贝到linux下。
若是是使用Notepad编辑器进行编辑的话,能够在菜单栏上选择“编辑”—“档案格式转换”—“转换为 UNIX 格式”。
最后说明一下,这些问题只是storm应用过程当中遇到的一小部分问题,其实还有不少问题是涉及到实际项目的考虑的,好比集群硬件要求,参数配置,日志处理等等,具体问题具体分析吧,也但愿哪些在实际项目中用到storm的大神们,能多多和你们分享大家的实际经验,毕竟实践出真知,任何新技术,只有通过实际应用和实际检验,分享出来的东西才有说服力。