Storm 安装与运维问题
运行 storm 命令报错
出现语法错误:
File "/home/storm/apache-storm-0.9.3/bin/storm", line 61
normclasspath = cygpath if sys.platform == 'cygwin' else identity
^
SyntaxError: invalid syntax
这是由于系统中安装的低版本 Python 部分语法不支持,需要重新安装高版本 Python(如2.7.x)。
注意:部分系统Python默认安装位置不是
/usr/bin/python
,必须在 Python 安装完成之后将安装版本Python关联到该位置。参考操作方法:cd /usr/bin
mv python python.bk
ln -s /usr/local/Python-2.7.8/python python
Storm 在 ssh 断开后自动关闭
这是由于 Storm 是由默认的 Shell 机制打开运行,在 ssh 或 telnet 断开后终端会将挂断信号发送到控制进程,进而会关闭该 Shell 进程组中的所有进程。因此需要在 Storm 后台启动时使用 nohup
命令和 &
标记可以使进程忽略挂断信号,避免程序的异常退出:
nohup storm nimbus &
nohup storm ui &
nohup storm supervisor &
nohup storm logviewer &
Storm UI 网页无法打开
检查 Storm 主机(nimbus 与 ui 所在运行服务器)的防火墙设置,是否存在监控端口屏蔽(ui 的默认端口是 8080)
[注] 测试环境下可以不考虑安全问题直接关闭防火墙
Strom UI 网页中没有 topology 信息
只有集群(Cluster)模式的 topology 才会在监控页面显示,需要将提交到集群的 topology 的运行模式由本地模式(local mode)改为集群模式
Storm UI 网页中无法打开各个端口的 worker.log
在需要查看 log 的机器上启动 logviewer 进程:
nohup storm logviewer &
supervisor 启动报错
supervisor 启动后异常退出,日志中显示以下错误信息:
2015-04-29T09:08:59.771+0800 b.s.event [ERROR] Error when processing event
java.io.FileNotFoundException: File '/home/storm/workdir/supervisor/stormdist/storm-test-1-1429603683/stormconf.ser' does not exist
这主要是在 storm 中有 topology 运行的情况下(错误信息中显示的 storm topology 名称为 “storm-test”),启动 supervisor 时会先尝试清空数据,supervisor 启动后准备重新载入 topology 数据时就会出错。所以,直接的解决办法是先 kill 掉 topology,然后再次启动 supervisor。
另一种解决方法是先启动另一台闲置的 supervisor 服务器,等待集群刷新 topology 数据,然后再启动第一台 supervisor 机器,当然,这种方法的前提是有另一台闲置的服务器。
[补充说明] 这是 storm 的 0.9.4 以下版本的 bug,目前已修复但未合并到发布版本中,因此仍然需要手动处理。网上盛传需要清空、重启 zookeeper,实际上没有必要,而且在生产环境中会造成严重问题。另外一种说法是将 “
nimbus.monitor.freq.secs
” 配置为120(默认为10),这种做法没有尝试过,效果有待观察。
expected '<document start>', but found BlockMappingStart 错误
Storm 启动失败,在 nohup.out 中有如下错误信息
Exception in thread "main" expected '<document start>', but found BlockMappingStart
一般在这类信息后会有相关错误位置说明信息,如
in 'reader', line 23, column 2:
nimbus.host: "hd124"
^
或者
in 'reader', line 7, column 1:
storm.zookeeper.port: 2181
^
这类错误主要是storm.yaml文件的配置格式错误造成的,一般是配置项的空格遗漏问题。如上面两例分别表示nimbus.host与storm.zookeeper.port两个配置项开头缺少空格,或者“:”后缺少空格。正确添加空格后重新启动Storm即可。
Storm 应用开发问题
Storm 本地调试报错
本地模式运行 topology 时报错:
java.lang.NoSuchMethodError: org.yaml.snakeyaml.Yaml.<init>(Lorg/yaml/snakeyaml/constructor...
这是由于testng的包依赖冲突造成的,需要修改 pom.xml
:
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.8.5</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>snakeyaml</artifactId>
<groupId>org.yaml</groupId>
</exclusion>
</exclusions>
</dependency>
Storm worker 数量与配置数量不一致
在 topology 中设置 worker 数量:
conf.setNumWorkers(6);
但是,集群中实际的 worker 数量却不到6。
这是由于每个 supervisor 中有 worker 数量的上限,这个上限值除了要满足系统允许的最大 slot 上限值 8
之外,还需要小于 Storm 配置文件中的端口数量:
supervisor.slots.ports:
- 6700
- 6701
- 6702
- 6703
例如这里 supervisor 只配置了 4 个端口,那么在这个 supervisor 上最多只能运行 4 个 worker 进程。因此,如果需要更多的 worker 就需要配置更多的端口。
日志无法记录到程序中配置的路径
Storm 默认将日志统一记录到 $STORM_HOME/logs
目录中,不支持在程序中自定义的路径。但是,集群的日志记录目录是可以修改的,0.9 以上版本的 Storm 可以在 $STORM_HOME/logback/cluster.xml
配置文件中修改,其他早期版本可以在 log4j/*.properties
配置文件中修改。
log4j 包冲突
传统的日志记录方法是如下所示引入 apache 的 log4j 包来记录日志
import org.apache.log4j.Logger;
public class ClassifyBolt {
private static final Logger LOG = Logger.getLogger(ClassifyBolt.class);
}
这种方式在 Storm 开发中会报包冲突错误
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/F:/maven/repository/ch/qos/logback/logback-classic/1.0.13/logback-classic-1.0.13.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/F:/maven/repository/org/slf4j/slf4j-log4j12/1.6.1/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
SLF4J: Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path, preempting StackOverflowError.
SLF4J: See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
Exception in thread "main" java.lang.ExceptionInInitializerError
at org.apache.log4j.Logger.getLogger(Logger.java:39)
at org.apache.log4j.Logger.getLogger(Logger.java:43)
at com.enjoyor.storm.estimation.bolt.ClassifyBolt.<clinit>(ClassifyBolt.java:25)
at com.enjoyor.storm.estimation.topology.SimulationTopology.main(SimulationTopology.java:153)
Caused by: java.lang.IllegalStateException: Detected both log4j-over-slf4j.jar AND slf4j-log4j12.jar on the class path, preempting StackOverflowError. See also http://www.slf4j.org/codes.html#log4jDelegationLoop for more details.
at org.apache.log4j.Log4jLoggerFactory.<clinit>(Log4jLoggerFactory.java:49)
... 4 more
解决方法是改变日志类处理方式,替换 apache 的依赖为 slf4j 原生包,如下所示
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ClassifyBolt {
private static final Logger LOG = LoggerFactory
.getLogger(ClassifyBolt.class);
}
部件命名重复错误
对于具有相似功能的 Bolt/Spout 可能会出现命名冲突问题,例如这里定义了三个中介者 Bolt:
String preInter = "medium";
String detInter = "medium";
String devInter = "medium";
builder.setBolt(preInter, new InterBolt().shuffleGrouping(pre); builder.setBolt(detInter, new InterBolt().shuffleGrouping(det);
builder.setBolt(devInter, new InterBolt().shuffleGrouping(dev);
虽然他们的变量名不同,但是实际的字符串对象名称是相同的(都是“medium”),这就会产生如下的非法参数错误
Exception in thread "main" java.lang.IllegalArgumentException: Bolt has already been declared for id medium
at backtype.storm.topology.TopologyBuilder.validateUnusedId(TopologyBuilder.java:212)
at backtype.storm.topology.TopologyBuilder.setBolt(TopologyBuilder.java:139)
at com.enjoyor.storm.estimation.topology.SimulationTopology.main(SimulationTopology.java:182)
所以,对于不同的部件(Spout/Bolt),务必要区别命名:
String preInter = "premedium";
String detInter = "detmedium";
String devInter = "devmedium";
这样就能保证部件的ID互不相同,就能够避免错误了。
下游Bolt未定义数据流错误
在下游Bolt接收数据时,往往会忽略具体的接收数据流名称,例如
builder.setBolt(devInter, new InterBolt().shuffleGrouping(dev);
这里的 grouping 过程就忽略了“dev”的数据流ID(streamId),在运行时会报错
3839 [main] WARN backtype.storm.daemon.nimbus - Topology submission exception. (topology name='simulation') #<InvalidTopologyException InvalidTopologyException(msg:Component: [devInter] subscribes from non-existent stream: [default] of component [dev])>
7119 [main] ERROR org.apache.storm.zookeeper.server.NIOServerCnxnFactory - Thread Thread[main,5,main] died
因为不定义数据流时Spout/Bolt 会默认发送/接收streamId为“default”的数据流,而当上游Bolt发送了包含自定义数据流ID的数据流时,下游Bolt就无法识别,所以此时需要在下游Bolt中定义数据流
builder.setBolt(devInter, new InterBolt().shuffleGrouping(dev, signalStream);
这里的“signalStream”就是上游Bolt发送的具体数据流名称。
Updating...
Comments
comments powered by Disqus