Apache Toree 0.5.0-incubating Spark 集成进阶实战详解

深入 Apache Toree 0.5.0-incubating 与 Spark 的集成实战,覆盖版本配对的 Scala 2.12 根因与两种 JDK 失败模式、spark-context-initialization-mode 实测、--spark_opts 调参与运行时覆盖、SQL 双路径实测(SQL 专属内核挂起结论)与 %AddDeps 依赖链路,全部已在 Ubuntu 22.04 实测。

Apache ToreeApache Toree2026/10/15

Apache Toree 0.5.0-incubating Spark 集成进阶实战详解

目标:深入掌握 Apache Toree 0.5.0-incubating 与 Apache Spark 集成的高级主题——版本配对的 Scala 2.12 根因与两种实测失败模式、spark-context-initialization-mode 的真实行为、--spark_opts 调参与运行时覆盖、SQL 双路径(Scala 内核 %%SQL vs SQL 专属内核)实测结论、%AddDeps 依赖链路与排障手法。以下全部命令与回显均已在 Ubuntu 22.04(x86_64)、OpenJDK 1.8.0_504、Spark 3.0.3 实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • JDK:OpenJDK 1.8.0_504(另装 openjdk-17-jdk 17.0.20.1 用于失败模式实测)
  • Python:3.10.12,Jupyter notebook 7.6.3、jupyter_core 5.9.1
  • 内核:apache-toree 0.5.0(pip 包,内核 jar toree-assembly-0.5.0-incubating.jar)
  • Spark:spark-3.0.3-bin-hadoop2.7(主配置)、spark-2.4.8-bin-hadoop2.7(失败模式实测)
  • 内核注册:apache_toree_scala(默认)、toree_none_scala(none 模式)、toree_local2_scala(local[2])、toree_dual_sql(SQL 专属)
  • 验证用工作目录:/root/toree-test

确认四个实测内核已注册:

jupyter kernelspec list
Available kernels:
  apache_toree_scala    /usr/local/share/jupyter/kernels/apache_toree_scala
  python3               /usr/local/share/jupyter/kernels/python3
  toree_dual_sql        /usr/local/share/jupyter/kernels/toree_dual_sql
  toree_local2_scala    /usr/local/share/jupyter/kernels/toree_local2_scala
  toree_none_scala      /usr/local/share/jupyter/kernels/toree_none_scala

各内核注册命令见第四章;安装基础步骤见第 1 篇《Ubuntu 22.04 安装 Apache Toree 指南》。

二、版本配对深入:为什么是 Spark 3.0.3 + JDK 8

Toree 0.5.0 与 Spark 的耦合点在内核 jar 的编译期 Scala 版本与 spark-submit 的运行期 JVM 版本,配对错误的两种失败模式均已实测复现。

官方测试基线:Toree 仓库 v0.5.0-incubating 标签的集成测试环境定义(toree-dev 构建文件)使用 Spark 3.0.3 + OpenJDK 8,即 Scala 2.12 构建的 Spark 发行版(jars 下为 spark-core_2.12-3.0.3.jar)。

失败模式一:Spark 用了 Scala 2.11 构建(如 spark-2.4.8-bin-hadoop2.7)。内核按 Scala 2.12 编译,运行时被 Spark 自带的 scala-library-2.11.12 抢先加载,报错(手动 spark-submit 复现实测):

Exception in thread "main" java.lang.NoSuchMethodError: 'void scala.App.$init$(scala.App)'

核对 Spark 构建侧的 Scala 版本:

ls /usr/local/spark/jars/ | grep -E 'spark-core|scala-library'
scala-library-2.11.12.jar
spark-core_2.11-2.4.8.jar

失败模式二:JDK 用了 17(Spark 3.0.x 只支持 JDK 8/11)。换回 Spark 3.0.3 后在 JDK 17.0.20.1 下实测:

Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make private java.nio.DirectByteBuffer(long,int) accessible: module java.base does not "opens java.nio" to unnamed module @4f51ab48

可用组合的判定清单:

检查项预期说明
`ls $SPARK_HOME/jarsgrep spark-core`spark-core_2.12-3.0.3.jar
java -version1.8.0_xxxrun.sh 经 spark-submit 间接使用该 JVM
spark-submit 手动复现无异常输出,进入等待连接状态见 7.2 节定位法

💡 技术拓展:同为「Jupyter 上的 Spark」,Toree 与 sparkmagic 的耦合层次完全不同——

维度Apache Toreesparkmagic
Spark 部署形态内核进程内经 spark-submit 直接起 Spark(local/standalone/yarn 均可)依赖独立 Livy 服务代理远端 Spark
版本耦合与 Spark 同 JVM,Scala 版本必须匹配(本文实测结论)REST 协议解耦,Spark 侧版本由 Livy 决定
适用单机/自管集群交互开发多用户共享远端集群

本文采用 Apache Toree:无中间服务、延迟低、变量与依赖直接落在内核进程内,代价是必须严守版本配对(本节即配对判定方法)。

三、SparkContext 初始化模式实测

--toree_opts='--spark-context-initialization-mode 取值' 控制 SparkContext 的创建时机,官方可选值有 eager/lazy/none。本文实测默认(未显式设置)与 none 两种模式在同一台机器上的差异。

注册 none 模式内核:

jupyter toree install --spark_home=/usr/local/spark --kernel_name='Toree None' --toree_opts='--spark-context-initialization-mode none' --replace
[ToreeInstall] Creating kernel Scala
[ToreeInstall] Installed kernelspec toree_none_scala in /usr/local/share/jupyter/kernels/toree_none_scala

执行测试 notebook(cell1 纯 Scala,cell2 访问 sc):

cd /root/toree-test && python3 - <<'EOF'
import json
def code(src): return {'cell_type':'code','execution_count':None,'metadata':{},'outputs':[],'source':src.splitlines(True)}
nb={'cells':[code('val z = 7\nprintln(z * 6)'),
             code('sc.parallelize(1 to 10).sum()')],
   'metadata':{'kernelspec':{'display_name':'Toree None - Scala','language':'scala','name':'toree_none_scala'},'language_info':{'name':'scala'}},'nbformat':4,'nbformat_minor':5}
open('nb-none.ipynb','w').write(json.dumps(nb, indent=1))
print('written')
EOF
timeout 360 jupyter nbconvert --to notebook --execute --allow-errors --ExecutePreprocessor.timeout=300 nb-none.ipynb --output nb-none-out.ipynb
written

实测输出:

===== cell 1
[stream] 42
[display_data] {'text/plain': 'z = 7'}
[execute_result] {'text/plain': '7'}

===== cell 2
[execute_result] {'text/plain': '55.0'}

关键实测结论:即使声明 none 模式,首次访问 sc 时仍会按需创建 SparkContext(1 to 10 求和返回 55.0)——0.5.0 的 none 模式只是跳过启动期 eager 初始化,不是禁用 Spark。两种模式的实际差别在内核启动耗时与启动失败面:默认模式下内核就绪时 SparkContext 已在创建链路上,Spark 配置错误会直接导致 Kernel died;none 模式下内核先就绪,错误推迟到首次访问 sc 的单元格。若确实需要「无 Spark 的纯 Scala 内核」,应从 Spark 依赖装配层面另行处理,而不是依赖 none 模式。

启动耗时参考(实测同一台机器):默认模式内核就绪约 40~60 秒(含 spark-submit 与本地 SparkContext 初始化日志),none 模式内核就绪明显更快,首次访问 sc 的单元格耗时与默认模式相当。

四、Spark 参数调优:--spark_opts 与运行时覆盖

4.1 注册期固定参数

注册时用 --spark_opts 固定 master 与资源参数,写入 kernel.json 的 env.__TOREE_SPARK_OPTS__:

jupyter toree install --spark_home=/usr/local/spark --kernel_name='Toree Local2' --spark_opts='--master=local[2]' --replace
[ToreeInstall] Creating kernel Scala
[ToreeInstall] Installed kernelspec toree_local2_scala in /usr/local/share/jupyter/kernels/toree_local2_scala

执行验证单元格 println("master=" + sc.master) 与 RDD 求和:

master=local[2]
appName=Apache Toree
sum=5050.0

验证点:master=local[2] 说明 --spark_opts 生效;appName=Apache Toree 是 run.sh 固定注入的 --name 'Apache Toree'。常用参数组合:--master=local[*](按核数并行)、--driver-memory 2g(本地模式堆大小)、--conf spark.sql.shuffle.partitions=8(小规模 SQL 探索减少小任务)。

4.2 运行期覆盖

run.sh 的优先级逻辑(源码核对):外部环境变量 SPARK_OPTS/TOREE_OPTS 非空时覆盖 kernel.json 内的占位值。因此同一内核可在启动 Jupyter 前临时换 master:

export SPARK_OPTS='--master=local[4]' && jupyter notebook

同理会话内 TOREE_OPTS 可临时覆盖 --spark-context-initialization-mode 等内核参数,无需重注册。

4.3 接独立集群的参数骨架

从 local 切到 standalone/YARN 只需改 --master 一处:standalone 形如 spark://spark-master:7077(spark-master 为实际集群主节点地址),YARN 直接用 --master yarn,均保持 --deploy-mode client。内核以 client 模式运行时 driver 在内核进程内,notebook 中的 sc 即集群任务的 driver,本文实测范围为 local 模式,集群模式参数请按目标集群版本核对 Spark 官方文档。

五、SQL 双路径实测

Toree 0.5.0 有两条 SQL 路径:Scala 内核上的 %%SQL 单元格 magic,与 --interpreters=SQL 注册的 SQL 专属内核。实测结论先行:生产用 Scala 内核 + %%SQL。

5.1 Scala 内核 + %%SQL(可用)

在 apache_toree_scala 内核上执行(见第 3 篇第三章):

%%SQL
SELECT 1+1 AS two, 'toree' AS name
[execute_result] {'text/plain': '+---+-----+\n|two| name|\n+---+-----+\n|  2|toree|\n+---+-----+\n'}

同一结果表也可用 spark.sql(...) 显式写法获得,好处是能接 Scala 变量与算子链(实测 spark.sql("SELECT 1+1 AS two, 'toree' AS name").show() 输出一致)。

5.2 SQL 专属内核(实测挂起)

--interpreters=SQL 注册的内核(toree_dual_sql,DEFAULT_INTERPRETER=SQL)执行 SQL 单元格实测 300 秒无回复,nbconvert 报超时:

[NbConvertApp] ERROR | Timeout waiting for execute reply (300s).
26/10/15 12:59:40 ERROR InterpreterActor: Could not execute code kernel.magics.SQL("""SELECT 2*3 AS six""") because of exception: Ask timed out on [Actor[akka://spark-kernel-actor-system/user/interpreter/execute_request_task#1868367090]] after [21474835000 ms]. Message of type [scala.Tuple3] was sent by [Actor[akka://spark-kernel-actor-system/user/interpreter#1912344793]]. A typical reason for `AskTimeoutException` is that the recipient actor didn't send a reply.

结论:0.5.0 的 SQL 专属解释器存在执行挂起缺陷(Akka actor 应答超时),同一 SQL 在 Scala 内核的 %%SQL 上秒级返回,因此不要把 SQL 专属内核用于生产 notebook。

六、依赖集成进阶:%AddDeps 与 %AddJar

6.1 %AddDeps 坐标解析链路

实测 org.apache.commons:commons-math3:3.6.1(Maven 坐标以空格分隔):

[stream] Marking org.apache.commons:commons-math3:3.6.1 for download
[stream] Obtained 2 files

Obtained 2 files 对应 jar 与 pom。随后单元格 import 并计算(实测输出):

[stream] mean=4.0
[display_data] {'text/plain': 'DescriptiveStatistics:\nn: 3\nmin: 2.0\nmax: 6.0\nmean: 4.0\nstd dev: 2.0\nmedian: 4.0\nskewness: 0.0\nkurtosis: NaN'}

实测细节:Marking/Obtained 两条日志各出现两次(0.5.0 的重复日志行为),不影响加载;对带传递依赖的坐标,Obtained 文件数会相应增加。依赖已由 --spark_opts/spark-submit --jars 预置时无需 magic,直接 import 即可。

6.2 %AddJar 与 %AddDeps 的分工

场景首选理由
知道 Maven 坐标%AddDeps自动解析坐标与传递依赖,声明可审计
jar 直链/本地文件%AddJarURL 或 file:// 直接拉取,第二次走缓存
依赖固定不变spark-submit --jars(经 spark_opts 传入)内核启动即就绪,避免会话内网络下载

两者叠加使用时注意类加载顺序:后加载的 jar 不会替换已加载的类,同一依赖不要既 AddJar 又 AddDeps。

七、运维与排障

7.1 常见 WARN 的定性

以下 WARN 均为实测捕获,不影响功能:

  • WARN StandardComponentInitialization$$anon$1: Unable to control initialization of REPL class server!:REPL 类加载器初始化控制受限的告警,实测全部单元格正常执行与求值。
  • WARN KernelOutputStream: Suppressing empty output: '':内核抑制空输出的正常行为。
  • Spark 侧 Using Spark's default log4j profile ... To adjust logging level use sc.setLogLevel(newLevel).:Spark 默认日志提示,可在单元格 sc.setLogLevel("WARN") 降噪。

7.2 手动复现定位法

内核启动失败时,绕开 Jupyter 直接复现(最快的根因暴露方式,实测见第二、三章):

timeout 60 /usr/local/spark/bin/spark-submit \
  --name 'Apache Toree' \
  --class org.apache.toree.Main \
  /usr/local/share/jupyter/kernels/apache_toree_scala/lib/toree-assembly-0.5.0-incubating.jar \
  --spark-context-initialization-mode none 2>&1 | head -20

正常时输出 Spark banner 与内核组件加载日志后进入等待连接状态(timeout 60 秒后被杀属预期);异常时首屏即给出 NoSuchMethodError/InaccessibleObjectException 等根因。

7.3 版本升级路径

升级 Toree:pip3 install -U apache-toree 后对每个注册名重跑 jupyter toree install ... --replace(kernelspec 内复制的 jar 不会随 pip 升级自动更新)。升级 Spark:解压新版本到新目录(如 /usr/local/spark-3.0.3),按第二章判定清单核对 Scala 构建后用 --spark_home --replace 重注册,回滚只需指回旧目录。

八、常见问题(FAQ)

Q1:none 模式下能不能彻底禁用 Spark? 不能。实测 none 模式首次访问 sc 仍会创建 SparkContext(第三章 55.0 的求和结果)。它的价值是「先起内核、按需初始化」,用于隔离 Spark 配置错误对内核启动的影响。

Q2:内核就绪后第一个单元格特别慢? 默认模式下首单元格在等待 SparkContext 完成创建(实测约 40~60 秒冷启动)。要加快纯 Scala 验证速度,用 none 模式内核(第三章)。

Q3:%%SQL 的结果想转成结构化数据在 Scala 里继续处理? 改用 val df = spark.sql("SELECT ...") 拿到 DataFrame 后自由操作(.show()、.collect()、.write 等);%%SQL 魔法输出的是格式化文本表,适合展示不适合链式处理。

Q4:SQL 专属内核一定不能用吗? 实测 0.5.0 存在 AskTimeoutException 挂起缺陷(第五章),同一 SQL 在 Scala 内核 %%SQL 上正常。除非后续版本修复,生产统一用 Scala 内核 + %%SQL。

Q5:能同时跑多个 Toree 内核会话吗? 可以。每个 notebook 会话是独立的 spark-submit 进程,各自持有独立 SparkContext;local 模式下注意多会话的内存与核数分配(--driver-memory、--master=local[2] 按会话限流),避免本机资源争抢。

Q6:%AddDeps 报解析失败/超时? 先在单元格内验证网络与仓库可达性(curl repo1.maven.org),内网环境配置自建 Nexus resolver;地址固定的 jar 改用 %AddJar。两者行为差异见 6.2 节分工表。

九、参考链接

  1. Apache Toree 官方 How it works:https://toree.apache.org/docs/current/user/how-it-works
  2. 官方 Advanced Topics:https://toree.apache.org/docs/current/user/advanced-topics
  3. Spark 3.0.3 官方文档(master/deploy-mode 参数):https://spark.apache.org/docs/3.0.3/submitting-applications.html
  4. Toree 源码仓库(v0.5.0-incubating 标签与测试镜像):https://github.com/apache/incubator-toree
  5. 本文配套第 4 篇(命令与 magic 总表):/docs/Apache Toree/Apache Toree 0.5.0-incubating 常用命令与 magic 详解.md
  6. 本文配套第 1 篇(安装与版本选型):/docs/Apache Toree/Ubuntu 22.04 安装 Apache Toree 指南.md