阅读更多
最近打算在项目中用storm从一个database生成多个搜索引擎的实时索引, 于是看了一些相关的资料就开始上手了. 按照以前熟悉的套路, 使用spring来管理对象之间的依赖, 于是我开始将topology, spout, bolt看成一个个bean, 然后放在spring容器中来完成初始化, 但是在本地模式运行的时候, 出现序列化的问题(抛出java.io.NotSerializableException异常), 以为是有些类忘记实现序列化接口了, 于是给相关类加上了序列化接口, 再跑, 还是抛java.io.NotSerializableException异常, 说另外一个依赖jar中的类需要实现序列化接口, 这个就麻烦了, 另外一个jar我没法修改代码, 我想可能是我哪些地方用的不对, 于是在group( http://groups.google.com/group/storm-user)中google之, 发现很多人都碰到了跟我类似的问题, 看了里面的讨论, 发现storm的作者Nathan Marz( http://nathanmarz.com)针对这个问题给出了合理的解释:
The lifecycle of a bolt or spout is as follows:
1. Created on client side (from where you submit the topology) and
serialized using Java serialization
2. Serialized component is sent to all the tasks
3. Each task executing that component deserializes the component
4. The task calls "prepare" (for bolts) or "open" (for spouts) on the
component before it starts executing.
So if you need to do something like connect to a database, you should do
that in the "prepare" or "open" method.
出现这个问题主要是对一个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的时候进行.
后来我又看了这篇文章"Twitter Storm源代码分析之Topology的执行过程(http://xumingming.sinaapp.com/647/twitter-storm-code-analysis-topology-execution/)", 不过里面没有提到Nathan所说的这些内容.
知道了上面的过程之后, 于是我开始改造我的代码, 那么该如何将storm与spring结合起来呢? 于是我又开始在group中google之, 发现有个韩国人写了一个storm-spring-example( https://github.com/mykidong/storm-spring-example), 于是下载下来研究了一番, 原来需要在每个spout/bolt维护自己的ApplicationConext, 于是将一个all in one 的spring打散成spring-spout.xml, spring-bolt-xxx.xml. 然后在open/prepare方法中加载对应的spring配置文件.
改造完成之后, 运行OK!