Проблемы с Scala ScriptEngine внутри spark подать заявку
Я работаю в системе, где я позволяю пользователям писать DSLS, и я загружаю его как экземпляры моего Типа во время выполнения, и они могут применяться поверх RDD. Все приложение работает как приложение spark-submit, и я использую движок ScriptEngine для компиляции DSL, написанных на самой Scala. Каждый тест прекрасно работает в SBT и IntelliJ. Но пока я делаю искры-отправку, мои собственные типы, доступные в моей толстой банке, недоступны для импорта в Script. Я инициализирую скрипт-движок следующим образом.
val engine: ScriptEngine = new ScriptEngineManager().getEngineByName("scala")
private val settings: Settings = engine.asInstanceOf[scala.tools.nsc.interpreter.IMain].settings
settings.usejavacp.value = true
settings.embeddedDefaults[DummyClass]
private val loader: ClassLoader = Thread.currentThread().getContextClassLoader
settings.embeddedDefaults(loader)
Кажется, что это проблема с загрузчиком классов во время spark-submit. Но я не могу выяснить причину, по которой мои собственные типы в моем банке, в котором также есть основная программа для spark-submit, недоступны в моем скрипте, созданном в той же JVM. Scala-компилятор Scala, Scala-Reflect и Scala-библиотеки версии являются 2.11.8. Некоторая помощь будет принята с благодарностью.
2 ответа
Я нашел рабочее решение. Пройдя через код и много отладок, я наконец-то обнаружил, что ScriptEngine создает Classloader для себя, используя строку Classpath Classloader, использованную для его создания. В случае spark-submit, spark создает специальный загрузчик классов, который может читать как локальные, так и hdfs-файлы. Но строка пути к классу, полученная из этого загрузчика классов, не будет содержать файлов jar нашего приложения, присутствующих в HDFS.
Ручное добавление файла jar приложения в путь к классу ScriptEngine перед его инициализацией решило мои проблемы. Чтобы это работало, мне пришлось локально загрузить JAR-файл приложения в HDFS на локальный, прежде чем добавлять его.
Если вы создаете экземпляр интерпретатора Scala напрямую, а не через ScriptEngineManager, вы можете передать настройки и переопределить путь к классам:
val cl = java.lang.Thread.currentThread.getContextClassLoader
val jar = cl.asInstanceOf[java.net.URLClassLoader].getURLs.toList.head.toString
val settings = new scala.tools.nsc.Settings()
settings.classpath.value = jar
val engine = scala.tools.nsc.interpreter.Scripted(settings = settings)