【问题标题】:Attempting to make Nashorn thread safe试图使 Nashorn 线程安全
【发布时间】:2016-02-22 20:32:56
【问题描述】:

我最近一直在与 Play 合作!框架和 Nashorn 尝试渲染 Redux 应用程序。最初,我在 ThreadPoolExecutor 中实现了多个 Nashorn 引擎,并在运行 engine.eval() 时使用了期货。性能很糟糕,我假设是由于高 I/O 和我使用的阻塞未来。

在熟悉 Play 之后!以及他们的 async/promise 模式,我试图从一个 ScriptEngine 重新开始;根据https://stackoverflow.com/a/30159424/5956783,脚本引擎本身是线程安全的,但绑定不是。这是整个课程:

package services;

import akka.actor.ActorSystem;

import play.libs.F;
import scala.concurrent.ExecutionContext;

import java.io.FileReader;


import javax.inject.Inject;
import javax.inject.Singleton;
import javax.script.*;

@Singleton
public class NashornEngine extends JSEngineAbstract {

    private NashornThread engine;
    private final ActorSystem actorSystem;


    protected class NashornThread {
        private ScriptEngine engine;
        private final ExecutionContext executionContext = actorSystem.dispatchers().lookup("js-engine-executor");
        private CompiledScript compiledScript;


        public NashornThread() {
            try {
                String dir = System.getProperty("user.dir");
                this.engine = new ScriptEngineManager(null).getEngineByName("nashorn");
                this.compiledScript = ((Compilable) this.engine).compile(new FileReader(dir + "/public/javascripts/bundle.js"));
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }

        public ScriptEngine getEngine() {
            return engine;
        }

        public F.Promise<String> getContent(String path, String globalCode) {
            return F.Promise.promise(() -> this.executeMethod(path, globalCode), this.executionContext);
        }

        private String executeMethod(String path, String json) throws ScriptException {
            Bindings newBinding = engine.createBindings();
            try {
                this.compiledScript.eval(newBinding);
                getEngine().setBindings(newBinding, ScriptContext.ENGINE_SCOPE);
                getEngine().eval("var global = this;");
                getEngine().eval("var console = {log: print, error: print, warn: print};");
                String result = getEngine().eval("App.renderApp('" + path + "', " + json + ")").toString();

                return result;
            } catch(Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }


    @Inject
    public NashornEngine(ActorSystem actorSystem) {
        this.actorSystem = actorSystem;
        this.engine = new NashornThread();
    }

    @Override
    public F.Promise<String> getContent(String path, String globalCode) {
        try {
            F.Promise<String> result = engine.getContent(path, globalCode);

            return result.map((String r) -> r);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

我的 JavaScript 应用程序导出了一个 App 对象,其方法 renderApp(path, state) 可见。这实际上确实有效,但只有 16 次(是的,总是 16 次)。从迭代 17 开始,我得到以下异常和伴随的堆栈跟踪:

java.lang.ClassCastException: jdk.nashorn.internal.objects.NativeRegExpExecResult cannot be cast to jdk.nashorn.internal.objects.NativeArray
    at jdk.nashorn.internal.objects.NativeArray.getContinuousArrayDataCCE(NativeArray.java:1900)
    at jdk.nashorn.internal.objects.NativeArray.popObject(NativeArray.java:937)
    at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:660)
    at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228)
    at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
    at jdk.nashorn.internal.scripts.Script$Recompilation$23589$782286AA$\^eval\_.L:22918$L:22920$matchPattern(<eval>:23049)
    at jdk.nashorn.internal.scripts.Script$Recompilation$23588$816752AAAAAA$\^eval\_.L:24077$L:24079$matchRouteDeep(<eval>:24168)
    at jdk.nashorn.internal.scripts.Script$Recompilation$23587$820262DAA$\^eval\_.L:24077$L:24079$matchRoutes$L:24252$L:24253(<eval>:24254)
    at jdk.nashorn.internal.scripts.Script$Recompilation$23586$809060$\^eval\_.L:23848$loopAsync$next(<eval>:23869)
    at jdk.nashorn.internal.scripts.Script$Recompilation$23584$808906AAA$\^eval\_.L:23848$loopAsync(<eval>:23875)
    at jdk.nashorn.internal.scripts.Script$Recompilation$23583$820189$\^eval\_.L:24077$L:24079$matchRoutes$L:24252(<eval>:24253)
    at jdk.nashorn.internal.scripts.Script$Recompilation$23582$819862AAA$\^eval\_.L:24077$L:24079$matchRoutes(<eval>:24252)
    at jdk.nashorn.internal.scripts.Script$Recompilation$23580$789440AA$\^eval\_.L:23151$L:23153$useRoutes$L:23209$match(<eval>:23239)
    at jdk.nashorn.internal.scripts.Script$Recompilation$23518$847004AA$\^eval\_.L:25026$L:25028$match(<eval>:25084)
    at jdk.nashorn.internal.scripts.Script$Recompilation$23468$3872AA$\^eval\_.L:53$renderApp(<eval>:147)
    at jdk.nashorn.internal.scripts.Script$23467$\^eval\_.:program(<eval>:1)
    at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:640)
    at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:228)
    at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:446)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:403)
    at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:399)
    at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:155)
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
    at services.NashornEngine$NashornThread.executeMethod(NashornEngine.java:62)
    at services.NashornEngine$NashornThread.lambda$getContent$0(NashornEngine.java:48)
    at play.core.j.FPromiseHelper$$anonfun$promise$2.apply(FPromiseHelper.scala:36)
    at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
    at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
    at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40)
    at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:397)
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

我的印象是,使用已编译脚本创建新绑定将被视为引擎的全新绑定,但似乎并非如此。如果有的话,我在这里做错了什么?

我也尝试使用Invocable 来调用App 对象上的方法,但这没有任何区别。

编辑 我在一台带有超线程的 8 核机器上,所以这可以解释第 17 次尝试失败之前的 16 次成功尝试。另外,我已将executeMethod 方法更新为以下内容:

private String executeMethod(String path, String json) throws ScriptException {
            Bindings newBinding = engine.createBindings();
            try {
                this.compiledScript.eval(newBinding);
                getEngine().eval("var global = this;", newBinding);
                getEngine().eval("var console = {log: print, error: print, warn: print};", newBinding);
                Object app = getEngine().eval("App", newBinding);
                Invocable invEngine = (Invocable) getEngine();
                String result = invEngine.invokeMethod(app, "renderApp", path, json).toString();
//              String result = getEngine().eval("App.renderApp('" + path + "', " + json + ")", newBinding).toString();

                return result;
            } catch(Exception e) {
                e.printStackTrace();
                return null;
            }
        }

【问题讨论】:

    标签: java playframework playframework-2.0 nashorn


    【解决方案1】:

    更新到最新版本的 Nashorn 或 Java 1.8u76 可解决此问题。

    【讨论】:

      【解决方案2】:

      不幸的是,您在 Nashorn 中找到了 bug。好消息是它已经被修复了,修复程序可以在 current early access releases 找到。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-14
        • 1970-01-01
        • 1970-01-01
        • 2021-07-12
        • 2011-03-30
        相关资源
        最近更新 更多