导读:本文以仓库 python/README.md 为骨架,完整解析 Apache Zeppelin 中 Python 解释器的两代实现——基于
ProcessBuilder+ Py4J 的原生 Python 解释器,以及基于jupyter_client+ gRPC 的 IPython 解释器。你将掌握:解释器进程如何被拉起与通信、段落执行/中断/结果回传的底层协议、matplotlib 内联渲染与%python.sql的实现机制、全部可配置参数及其默认值,以及如何运行依赖真实 Python 环境的完整测试套件。
一、Python 解释器在 Zeppelin 中的定位
Apache Zeppelin 的 notebook 支持以 SQL、Scala 等多种语言编写数据驱动、交互式的分析段落,Python 是其最常用的解释器之一。按 python/README.md 的说明,该模块提供两大能力:
- Python interpreter:经典实现,通过系统 Python 进程执行代码,支持 Py4J 动态表单、matplotlib 内联绘图、
%python.sql(Pandas DataFrame 上的 SQL); - IPython interpreter:将整个执行工作委托给 IPython kernel,通过 gRPC 通信,尽量让所有 IPython 生态特性(如自动补全、富输出)在 Zeppelin 中原样可用。
整个python模块位于仓库 python/ 目录,核心 Java 类集中在 python/src/main/java/org/apache/zeppelin/python/(PythonInterpreter、IPythonInterpreter、PythonInterpreterPandasSql、PythonZeppelinContext等),Python 侧引导代码在 python/src/main/resources/python/ 与 python/src/main/resources/grpc/python/。
二、原生 Python 解释器的总体架构
README 原文:Current interpreter implementation spawns new system python process through
ProcessBuilderand re-directs it's stdin\strout to Zeppelin(当前实现通过ProcessBuilder派生一个系统 Python 进程,并将其 stdin/stdout 重定向到 Zeppelin)。
整体数据流如下:
- Zeppelin 解释器进程(JVM)在
open()时启动一个常驻(sleeping)的 Python 进程; - 用户的段落代码由 JVM 写入 Python 进程的 stdin;
- Python 执行结果、stdout 输出被重定向回 Zeppelin,并实时流转到 notebook 段落输出区;
- JVM 与 Python 进程之间同时建立一条Py4J 双向通道:Python 侧通过 JavaGateway 反向调用 JVM 对象(如动态表单、上下文),JVM 侧通过 gateway 的
entry_point暴露解释器实例。
2.1 进程拉起细节:-i与-u选项
按 README 的 Technical overview 描述:When interpreter is starting it launches a python process inside a Java ProcessBuilder. Python is started with-i(interactive mode) and-u(unbuffered stdin, stdout and stderr) options. Thus the interpreter has a "sleeping" python process.(启动时以-i交互模式与-u无缓冲模式启动 Python,因此解释器持有一个"休眠中"的 Python 进程。)
-u保证 stdout/stderr 不缓冲,使 JVM 侧能实时读取输出;-i让进程进入交互 REPL 状态等待指令,避免每条语句单独拉起进程、从而在段落间保留变量与状态。
在 PythonInterpreter.java 的createGatewayServerAndStartScript()中可以看到具体实现:
- 将资源脚本
python/zeppelin_python.py复制为临时文件(构造器中创建于/tmp,见 PythonInterpreter.java); - 通过
findRandomOpenPortOnAllLocalInterfaces()随机选择一个空闲端口,启动 Py4JGatewayServer(监听0.0.0.0); - 用
CommandLine.parse(getPythonCommand())构造命令行并追加脚本路径、端口、本机 IP 三个参数,交给 Apache Commons Exec 的DefaultExecutor异步执行; - 设置
PYTHONPATH环境变量,把py4j-0.9.2/src与interpreter/lib/python(内含 matplotlib 内联后端backend_zinline.py、mpl_config.py)注入 Python 进程,见 PythonInterpreter.java 与 interpreter/lib/python/; - 使用
ExecuteWatchdog(INFINITE_TIMEOUT)守护进程,确保 Python 进程在整个解释器生命周期内持续存活。
2.2 请求/响应协议:从 flush marker 到 Py4J 回调
README 记载的经典协议为:Interpreter sends command to python with a JavaoutputStreamWriterand read from anInputStreamReader. To know when stop reading stdout, interpreter sendsprint "*!?flush reader!?*"after each command and reads stdout until he receives back the*!?flush reader!?*.(JVM 用outputStreamWriter发送命令、用InputStreamReader读取输出;每条命令后发送print "*!?flush reader!?*",直到读回该标记才停止读取。)
需要说明的是,在当前仓库快照的实现中,段落请求与结果回传改由 Py4J 回调完成:Python 侧主循环通过intp.getStatements()阻塞等待 JVM 下发代码,执行完毕后调用intp.setStatementsFinished(out, error)回传结果与错误状态,见 python/src/main/resources/python/zeppelin_python.py;JVM 侧对应方法在 PythonInterpreter.java。整个握手围绕statementSetNotifier、statementFinishedNotifier、pythonScriptInitializeNotifier三个同步对象完成,例如interpret()在等待 Python 完成初始化时最多阻塞MAX_TIMEOUT_SEC = 10秒,超时则返回 "python is not responding",见 PythonInterpreter.java。可以推断 flush marker 协议对应更早期的实现,README 描述的是该机制的设计意图——即"如何界定一段 stdout 的读取边界"。
2.3 Bootstrap 引导与z上下文对象
README 指出:When interpreter is starting, it sends some Python code (bootstrap.py and bootstrap_input.py) to initialize default behavior and functions (help(), z.input()...). bootstrap_input.py is sent only if py4j library is detected inside Python process.(启动时发送 bootstrap 代码初始化默认行为与函数;bootstrap_input.py仅在 Python 进程内检测到 py4j 库时才发送。)
在当前快照中,该职责由 zeppelin_python.py 承担:
- 通过
GatewayClient(address=host, port=int(sys.argv[1]))连接 JVM 侧 gateway,gateway.entry_point即解释器实例; - 定义
PyZeppelinContext(别名z/__zeppelin__),向 Python 用户空间暴露:z.input(name, defaultValue)、z.textbox、z.noteTextbox:动态表单输入;z.select、z.noteSelect、z.checkbox、z.noteCheckbox:选项/复选表单(选项通过gateway.jvm构造 Java 侧的OptionInput.ParamOption数组);z.show(df):以 Zeppelin%table显示系统渲染 DataFrame,默认最多输出max_result(1000)行;z.show(plt):以 base64 PNG/SVG 内联渲染 matplotlib 图;z.registerHook / unregisterHook / registerNoteHook:注册执行钩子(如post_exec)。
- 语句按 AST 分别以
exec与single模式编译执行,使最后一条表达式的求值结果能打印到 stdout(模拟 REPL 行为),见 zeppelin_python.py; - 段落的用户变量保存在独立命名空间
_zcUserQueryNameSpace,与解释器内部命名隔离。
2.4 段落中断:SIGINT 信号
README 原文:JavaBuilder can't send SIGINT signal to interrupt paragraph execution. Therefore interpreter directly send akill SIGINT PIDto python process to interrupt execution. Python process catch SIGINT signal with some code defined in bootstrap.py.(JVM 无法直接向 Python 进程发送 SIGINT,因此解释器直接执行kill SIGINT PID;Python 进程在 bootstrap 中注册 SIGINT 处理器。)
源码印证:
- JVM 侧
interrupt()在pythonPid > -1时执行Runtime.getRuntime().exec("kill -SIGINT " + pythonPid);对非 UNIX/Linux 系统则回退为直接关闭解释器,见 PythonInterpreter.java。pythonPid由 Python 进程启动时通过intp.onPythonScriptInitialized(os.getpid())上报; - Python 侧在引导代码中注册
signal.signal(signal.SIGINT, handler_stop_signals),收到信号后抛出带信号信息的异常结束当前段落,见 zeppelin_python.py。
2.5 matplotlib 内联渲染
README 原文:Matplotlib figures are displayed inline with the notebook automatically using a built-in backend for zeppelin in conjunction with a post-execute hook.(使用 Zeppelin 内置后端 + post-execute 钩子自动将 matplotlib 图形内联显示在 notebook 中。)
实现要点:
- JVM 在
open()时注册POST_EXEC_DEV钩子__zeppelin__._displayhook(),见 PythonInterpreter.java; - Python 侧
_setup_matplotlib()优先将 matplotlib 切到module://backend_zinline(位于 interpreter/lib/python/backend_zinline.py),并调用 interpreter/lib/python/mpl_config.py 配置width=600, height=400, dpi=72, fontsize=10, interactive=True, format='png';若后端缺失则回退到 Agg,见 zeppelin_python.py; show_matplotlib()将图形编码为data:image/png;base64,...的<img>HTML,以%html输出。
2.6%python.sql:对 Pandas DataFrame 执行 SQL
README 原文:%python.sqlsupport for Pandas DataFrames is optional and provided using pandasql if user have one installed.(%python.sql对 Pandas DataFrame 的 SQL 支持是可选的,依赖用户安装 pandasql。)
- SQL 引导脚本 python/src/main/resources/python/bootstrap_sql.py 定义
pysqldf = lambda q: sqldf(q, globals());若未安装 pandas/pandasql,则给出友好提示而非报错中断; PythonInterpreterPandasSql.open()通过bootStrapInterpreter("/python/bootstrap_sql.py")注入该函数,interpret()将用户的 SQL 包装为__zeppelin__.show(pysqldf('...'))委托给PythonInterpreter执行,见 PythonInterpreterPandasSql.java。其注释明确:目的是复刻%spark.sql对 Spark DataFrame 的体验。
三、解释器配置参数速查
依据 python/src/main/resources/interpreter-setting.json,%python组共注册五个解释器(python、ipython、sql、conda、docker),其中可配置参数如下:
| 解释器 | 参数 | 默认值 | 说明 |
|---|---|---|---|
| python | zeppelin.python | python | Python 可执行文件路径,默认假定python在$PATH中 |
| python | zeppelin.python.maxResult | 1000 | DataFrame 表格显示的最大行数 |
| python | zeppelin.python.useIPython | true | 当 IPython 可用时是否优先使用 IPython 解释器 |
| ipython | zeppelin.ipython.launch.timeout | 30000 | IPython kernel 启动超时(毫秒) |
| ipython | zeppelin.ipython.grpc.message_size | 33554432(32M) | gRPC 消息大小上限(字节) |
代码层面对应关系:zeppelin.python.maxResult在 PythonInterpreter.java 解析并传入PythonZeppelinContext;zeppelin.python在getPythonBindPath()中解析,缺省回退python(PythonInterpreter.java);zeppelin.ipython.launch.timeout与zeppelin.ipython.grpc.message_size均在 IPythonInterpreter.java 读取。
四、开发前提与测试
4.1 Dev prerequisites
按 python/README.md:
- 每台机器需安装Python 2 或 3,且装有py4j(0.9.2)与matplotlib(1.31 或更高);
- 单元测试只校验解释器逻辑,不会启动真实 Python 进程——Python 进程被一个"把输入原样输出"的 mock 类替代;
- 写在
bootstrap.py/bootstrap_input.py(当前快照对应 zeppelin_python.py 与 bootstrap_sql.py)中的代码必须同时兼容 Python 2 与 3; - Python 代码遵循PEP8规范。
4.2 运行完整测试套件
默认构建会跳过依赖真实 Python 环境与外部库(Pandas、Pandasql 等)的用例;要运行包括这些用例在内的全部测试,执行 README 给出的命令:
mvn -Dpython.test.exclude='' test -pl python -am其中-Dpython.test.exclude对应 python/pom.xml 中 surefire 插件的<excludes>配置(默认排除值使慢速用例跳过),-pl python -am表示仅构建 python 模块及其依赖模块。相关测试类位于 python/src/test/java/org/apache/zeppelin/python/,如PythonInterpreterTest、PythonInterpreterMatplotlibTest、PythonInterpreterPandasSqlTest、IPythonInterpreterTest等。
五、IPython 解释器:架构与要求
5.1 依赖要求
按 python/README.md,IPython 解释器正常工作需要安装以下 Python 包:
- jupyter 5.x
- IPython
- ipykernel
- grpcio
若已安装 Anaconda,则只需额外安装grpc(其余包自带)。
源码中的前置检查更严格:checkIPythonPrerequisite()会执行pip freeze,并依次确认jupyter-client、ipykernel、ipython、grpcio、protobuf均已安装,任一缺失即返回对应错误信息,见 IPythonInterpreter.java。
5.2 架构:jupyter_client + gRPC
README 原文:Current interpreter delegate the whole work to ipython kernel viajupyter_client. Zeppelin would launch a python process which host the ipython kernel. Zeppelin interpreter process will communicate with the python process viagrpc. Ideally every feature works in IPython should work in Zeppelin as well.(解释器通过jupyter_client把全部工作委托给 IPython kernel;Zeppelin 启动一个承载 IPython kernel 的 Python 进程,并通过 gRPC 与之通信。理论上 IPython 中可用的每个特性都应在 Zeppelin 中可用。)
具体链路(依据 IPythonInterpreter.java 与 python/src/main/resources/grpc/python/):
open()随机分配两个端口:ipythonPort(gRPC 服务)与jvmGatewayPort(Py4J gateway);- 将
ipython_server.py、ipython_pb2.py、ipython_pb2_grpc.py复制到临时目录,用python ipython_server.py <port>拉起承载 IPython kernel 的进程,见 IPythonInterpreter.java; - 启动 JVM 侧 Py4J
GatewayServer,并通过 gRPC 向 kernel 注入 grpc/python/zeppelin_python.py(占位符${JVM_GATEWAY_PORT}被替换为实际端口),打通 Python ↔ JVM 的动态表单通道,见 IPythonInterpreter.java; - 启动后轮询
status直到 kernel 处于RUNNING,超过zeppelin.ipython.launch.timeout(默认 30 秒)则报错退出。
5.3 gRPC 协议定义
通信契约定义在 python/src/main/proto/ipython.proto,由 protobuf-maven-plugin 编译生成 Java 存根(依赖grpc 1.15.0,见 python/pom.xml)。IPython服务共五个 RPC:
execute(ExecuteRequest) returns (stream ExecuteResponse):执行代码,以流式返回输出(支持 TEXT / IMAGE 两种输出类型);complete(CompletionRequest) returns (CompletionResponse):代码补全;cancel(CancelRequest) returns (CancelResponse):取消正在运行的语句;status(StatusRequest) returns (StatusResponse):查询 kernel 状态(STARTING/RUNNING);stop(StopRequest) returns (StopResponse):关闭 kernel。
段落执行时,IPythonInterpreter.interpret()调用ipythonClient.stream_execute(...),将执行输出经InterpreterOutputStream实时写入段落,最终根据ExecuteStatus(SUCCESS/ERROR)映射为InterpreterResult,见 IPythonInterpreter.java。补全结果在completion()中被解析为 Zeppelin 的InterpreterCompletion列表,因此支持TAB键补全(见 interpreter-setting.json 中 ipython 的"completionKey": "TAB")。
5.4 与原生解释器的自动切换
PythonInterpreter.open() 展示了两代实现的衔接逻辑:
- 若
zeppelin.python.useIPython=true(默认)且checkIPythonPrerequisite()返回空(前置满足),则优先打开IPythonInterpreter,其后interpret/cancel/progress/completion全部委托给它; - 若 IPython 不可用或打开失败,自动回退到原生
PythonInterpreter(此时才注册 matplotlib 钩子并拉起 gateway + Python 进程)。
该设计让用户在未安装 IPython 生态包的环境下仍能正常使用%python,无需手工切换配置。子类还可通过setAdditionalPythonPath/setAdditionalPythonInitFile/setAddBulitinPy4j扩展(如 PySpark 场景追加 PYTHONPATH 与初始化代码),见 IPythonInterpreter.java。
六、小结
Apache Zeppelin 的 Python 支持呈现了一条清晰的演进路径:
- 经典 Python 解释器:进程级集成——
ProcessBuilder拉起带-i -u的常驻 Python,Py4J 双向桥接提供动态表单与上下文,SIGINT 实现段落中断,内置 backend + post-exec 钩子实现 matplotlib 内联,pandasql 带来%python.sql; - IPython 解释器:内核级集成——由
jupyter_client托管 IPython kernel,gRPC 流式执行代码并回传输出,保留了补全、取消、状态查询等完整能力,并在前置依赖不满足时优雅回退到经典实现。
无论是阅读 python/README.md 快速上手,还是深入 PythonInterpreter.java、IPythonInterpreter.java、ipython.proto 与 zeppelin_python.py 验证实现细节,理解上述两条通信链路与配置参数,是排查%python执行异常、定制 Python 环境与扩展解释器能力的起点。
- 后端
- 前端
- 大数据
- 数据分析
【免费下载链接】zeppelin
Web-based notebook that enables>项目地址:https://gitcode.com/gh_mirrors/zeppelin2/zeppelin
相关推荐
Apache Zeppelin Python 解释器深度解析:进程架构、Py4J 桥接与 IPython 内核集成
Apache Zeppelin Python 解释器深度解析:进程架构、Py4J 桥接与 IPython 内核集成 Apache Zeppelin 的 Pyth
数据分析数据可视化大数据后端前端任务调度如何快速获取网盘直链:LinkSwift 网盘直链下载助手完整使用指南
如何快速获取网盘直链:LinkSwift 网盘直链下载助手完整使用指南 LinkSwift 是一个浏览器用户脚本,作用是在百度网盘、阿里云盘等九大网盘的网页端帮
数据分析数据可视化大数据后端Apache Zeppelin Python 解释器实战指南:%python、IPython 与 Pandas SQL 全解析
Apache Zeppelin Python 解释器实战指南:%python、IPython 与 Pandas SQL 全解析 导读 :本文以 Apache Z
数据分析数据可视化大数据后端