“这就是我,一个低调的作者。”
一、包冲突
在开发storm时,由于会引入很多依赖包,因此,不免就会造成包冲突的问题,一般的问题都是日志框架的依赖重复,如下:
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.
这个是非常常见,但是很头疼的问题,我个人百度了好久,利用mvn dependency:tree 查看到了重复的包,也是坑了好久,最后索性不去留一个日志框架了,直接在Eclipse把所有的日志框架都剪掉,然后我自己添加了一个日志框架:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.7</version>
</dependency>
然后,再次提交storm的Topology,又发现了新的问题,下一个问题。
二、Log4J序列化问题
发布topologies 时,出现不能序列化log4j.Logger 的异常:
Exception in thread "main" java.lang.IllegalStateException: Bolt 'BottomMenuStatBolt' contains a non-serializable field of type org.apache.log4j.Logger, which was instantiated prior to topology creation. org.apache.log4j.Logger should be instantiated within the prepare method of 'BottomMenuStatBolt at the earliest.
在这里,我们有两种解决方案:
1)Log4J框架换成Slf4J框架(网上说的);但是貌似不是很容易解决问题;
2)把声明为Logger的变量忽略序列化即可:
transient Logger logger = Logger.getLogger(BottomMenuStatBolt.class);
这种方式还是很好解决问题的。
三、没有定义输出列
Component: [BottomMenuStatBolt] subscribes from non-existent stream: [BottomMenuClickStat] of component [BottomMenuFilterBolt]
检查Spout和Bolt代码中的declareOutputFields方法 declare的Field数量 等于 collector.emit数量
四、没有发现类的异常
java.lang.NoClassDefFoundError: kafka/javaapi/consumer/SimpleConsumer at storm.kafka.DynamicPartitionConnections.register(DynamicPartitionConnections.java:33) at storm.kafka.PartitionManager.<init>(
由于我在maven引入了本地的kafka.jar,因此,在打包的时候无法将本地包打入到maven的jar里面(PS:scope为system时,与provided相类似,不加入maven的带有依赖的jar中),因此,我采用了另外一种解决方案,即将本地包安装到我的maven本地库中:
>mvn install:install-file -Dfile=./kafka-0.7.1.jar -DgroupId=org.apache.kafka -DartifactId=kafka -Dversion=0.7.1 -Dpackaging=jar
然后,我在pom中引用的时候就可以将scope设置为compile,然后就可以打包到maven的jar了。
PS:上述这个问题的原因,也有可能是因为引入的jar包版本的问题导致,需要找到对应版本的jar,如果在maven上没有的话,还需要自行下载,然后按照上面给出的步骤安装到本地maven仓库~
五、看起来代码都很正常,但是bolt无法emit数据到下一个bolt
这个可能是因为你写的collector变量名不对,我就遇到了这样的坑,原本我定义了_collector,prepare方法中这么写:
@Override
public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
this._collector = collector;
}
但是,请注意,这里由于Clojure这门语言我不是很了解,我认为是可能带有_的变量,加上this会有问题,因此,我重新定义了collector,上述代码也变成了:
@Override
public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
this.collector = collector;
}