在非默认类加载器下运行本地群集

时间:2017-01-19 15:26:58

标签: java classloader apache-storm

来自Web类加载器的本地群集

我正在尝试从Web容器运行本地群集(是的,它仅用于开发和测试目的)并且在使用类加载器时遇到了困难。

直接进场

当我这么做时,简单而https://about.gitlab.com/2015/07/29/feature-highlight-merge-request-approvals/方式,

ILocalCluster localCluster = new LocalCluster();
localCluster.submitTopology(topologyName, stormConf, topology);

我获得了奖励

Async loop died!: java.lang.ClassCastException: my.company.storm.bolt.SomeFilteringBolt cannot be cast to org.apache.storm.task.IBolt
    at org.apache.storm.daemon.executor$fn__7953$fn__7966.invoke(executor.clj:787)
    at org.apache.storm.util$async_loop$fn__625.invoke(util.clj:482)
    at clojure.lang.AFn.run(AFn.java:22)
    at java.lang.Thread.run(Thread.java:745)

这是因为用于加载和实例化StormTopology的类加载器是Jetty WebAppClassLoader的实例,但LocalCluster.submitTopology()生成的(子)进程显然使用了系统类加载器。我通过在SomeFilteringBolt的静态块中记录类加载器来确认这一点 - 该类确实已加载两次,并且WebAppCL中的螺栓显然无法在以后的系统类加载器上强制转换为螺栓。

预期行为

现在,这让我感到惊讶,因为我认为Storm将序列化StormTopology实例,“在本地发送”,反序列化并运行它。如果它这样做,它肯定会起作用。相反,似乎它直接使用提供的StormTopology实例,这在不同的类加载器下是有问题的。

我从

尝试了什么

我尝试将这些设置为true以强制Storm在本地序列化我的拓扑。没有变化。

我尝试在系统类加载器下运行LocalCluster:

ClassLoader originalClassloader = Thread.currentThread().getContextClassLoader();
try {
    Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader());

    Config topologyConf = createTopologyConfig();
    Map<String, Object> stormConf = createStormConfig(topologyConf);
    StormTopology topology = createTopology(topologyConf);

    ILocalCluster localCluster = new LocalCluster();
    localCluster.submitTopology(topologyName, stormConf, topology);
} finally {
    Thread.currentThread().setContextClassLoader(originalClassloader);
}

这实际上让我更进一步:

Thread  died: java.lang.ExceptionInInitializerError
    at clojure.core__init.__init0(Unknown Source)
    at clojure.core__init.<clinit>(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at clojure.lang.RT.classForName(RT.java:2154)
    at clojure.lang.RT.classForName(RT.java:2163)
    at clojure.lang.RT.loadClassForName(RT.java:2182)
    at clojure.lang.RT.load(RT.java:436)
    at clojure.lang.RT.load(RT.java:412)
    at clojure.lang.RT.doInit(RT.java:454)
    at clojure.lang.RT.<clinit>(RT.java:330)
    at clojure.lang.Namespace.<init>(Namespace.java:34)
    at clojure.lang.Namespace.findOrCreate(Namespace.java:176)
    at clojure.lang.Var.internPrivate(Var.java:151)
    at org.apache.storm.LocalCluster.<clinit>(Unknown Source)
    at my.company.storm.LocalTopologyRunner.startTopology(LocalTopologyRunner.java:146)
    ... 10 more
Caused by: java.lang.IllegalStateException: Attempting to call unbound fn: #'clojure.core/refer
    at clojure.lang.Var$Unbound.throwArity(Var.java:43)
    at clojure.lang.AFn.invoke(AFn.java:32)
    at clojure.lang.Var.invoke(Var.java:379)
    at clojure.lang.RT.doInit(RT.java:467)
    at clojure.lang.RT.<clinit>(RT.java:330)
    ... 18 more

笏?!

问题

如何从系统类加载器以外的类加载器安全地以本地模式运行Storm拓扑?

我在Apache Storm 1.0.1,Jetty 8.1,Java 8u112 x64,Windows 7 x64上运行。

2 个答案:

答案 0 :(得分:1)

根本不是风暴专家,但这让我想起过去曾经遇到的旧“身份危机”问题。

要尝试两件事:

  • 通过调用org.eclipse.jetty.webapp.WebAppContext.setParentLoaderPriority(true)

  • 将优先级设置为System类加载器
  • 如果它不起作用,您可以调用方法org.eclipse.jetty.webapp.WebAppContext.setSystemClassesorg.eclipse.jetty.webapp.WebAppContext.addSystemClass来控制哪些类被视为webapp域中的系统类。

在加载它们之前(在webapp init期间),为整个风暴包(它允许使用“org.apache.storm。”之类的通配符)执行此操作。

值得一试!祝你好运。

答案 1 :(得分:1)

Apache Storm 1.0.3神奇地解决了这个问题。

即使没有TOPOLOGY_TESTING_ALWAYS_TRY_SERIALIZE,即使发行说明中没有修复的痕迹,因此我无法将其跟踪到代码更改。无论如何,我们很高兴现在按预期工作。