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