flink-1.10 pyflink onyarn 安装文档(支持 UDF)

官方文档对 pyflink 的使用说明不是很详细。本文主要参考了 孙金城 大佬的这篇文章
结合自己测试过程,有些地方做了修改,做一个记录

1.从源码编译 flink

注意:需要 Python 3.5+
如果已经有 apache-flink-*.dev0.tar.gz 二进制包,可以跳过这一步。

mvn clean install -DskipTests

会在 flink-python/dist 目录生成一个 apache-flink-*.dev0.tar.gz 二进制包,拷贝该包到需要安装 pyflink 的机器

2.安装并测试

本文只介绍 onyarn 模式的安装、部署
安装机器环境要求:

  • Apache Beam version == 2.15.0
  • Python 3.5+
  • Pip (version >= 7.1.0)
  • hadoop 文件系统

2.1 install pyflink

-i 指定国内 pip 源,安装会快一点

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple  apache-flink-1.10.dev0.tar.gz

2.2 打包 Python 环境

因为 yarn 集群上 python 环境不一定符合 flink 程序的版本要求,所以我们需要使用 virtualenv 打包 python 环境。

2.2.1 install virtualenv

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple  virtualenv

2.2.2 创建 Python 环境

用 virtualenv 以 always-copy 方式建立一个全新的 Python 环境,这里定义名称为 venv:

virtualenv --always-copy venv

会在当前目录生成一个 venv 文件

拷贝原环境的 python 标准库到 venv 中:

VENV_PYTHON=venv/bin/python
DST=`$VENV_PYTHON -c "import os;import pip;print(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(pip.__file__)))))"`
SRC=`$VENV_PYTHON -c "import os;import contextlib;print(os.path.dirname(os.path.abspath(contextlib.__file__)))"`
if [ "$SRC" != "$DST" ]; then find "$SRC" -maxdepth 1 ! -name "site-packages" ! -name "__pycache__" ! -name "python3.*" -exec cp -r -- "{}" "${DST}" \; ; fi

2.2.3 新环境安装 apache-beam

使用 venv 中的 pip 安装 apache-beam

venv/bin/pip install -i https://pypi.tuna.tsinghua.edu.cn/simple apache-beam==2.15.0

2.2.4 打包 Python 环境

把独立 python 环境打成 zip 包,目前打成的包比较大,有 90 多 MB

zip -r venv.zip venv

2.3 例子测试

拷贝下面的代码到 pyflink_job.py 文件:

import logging
import os
import shutil
import sys
import tempfile

from pyflink.table import BatchTableEnvironment, EnvironmentSettings
from pyflink.table.descriptors import FileSystem, OldCsv, Schema
from pyflink.table.types import DataTypes
from pyflink.table.udf import udf


def word_count():
   environment_settings = EnvironmentSettings.new_instance().in_batch_mode().use_blink_planner().build()
   t_env = BatchTableEnvironment.create(environment_settings=environment_settings)

   # register Results table in table environment
   #tmp_dir = tempfile.gettempdir()
   result_path = 'hdfs:///user/flink/python_result'
   #if os.path.exists(result_path):
   #    try:
   #        if os.path.isfile(result_path):
   #            os.remove(result_path)
   #        else:
   #            shutil.rmtree(result_path)
   #    except OSError as e:
   #        logging.error("Error removing directory: %s - %s.", e.filename, e.strerror)

   logging.info("Results directory: %s", result_path)

   # we should set the Python verison here if `Python` not point
   # t_env.get_config().set_python_executable("python3")

   t_env.connect(FileSystem().path(result_path)) \
       .with_format(OldCsv()
                    .field_delimiter(',')
                    .field("city", DataTypes.STRING())
                    .field("sales_volume", DataTypes.BIGINT())
                    .field("sales", DataTypes.BIGINT())) \
       .with_schema(Schema()
                    .field("city", DataTypes.STRING())
                    .field("sales_volume", DataTypes.BIGINT())
                    .field("sales", DataTypes.BIGINT())) \
       .create_temporary_table("Results")

   @udf(input_types=DataTypes.STRING(), result_type=DataTypes.ARRAY(DataTypes.STRING()))
   def split(input_str: str):
       return input_str.split(",")

   @udf(input_types=[DataTypes.ARRAY(DataTypes.STRING()), DataTypes.INT()], result_type=DataTypes.STRING())
   def get(arr, index):
       return arr[index]

   t_env.register_function("split", split)
   t_env.register_function("get", get)

   t_env.get_config().get_configuration().set_string("parallelism.default", "1")

   data = [("iPhone 11,30,5499,Beijing", ),
           ("iPhone 11 Pro,20,8699,Guangzhou", ),
           ("MacBook Pro,10,9999,Beijing", ),
           ("AirPods Pro,50,1999,Beijing", ),
           ("MacBook Pro,10,11499,Shanghai", ),
           ("iPhone 11,30,5999,Shanghai", ),
           ("iPhone 11 Pro,20,9999,Shenzhen", ),
           ("MacBook Pro,10,13899,Hangzhou", ),
           ("iPhone 11,10,6799,Beijing", ),
           ("MacBook Pro,10,18999,Beijing", ),
           ("iPhone 11 Pro,10,11799,Shenzhen", ),
           ("MacBook Pro,10,22199,Shanghai", ),
           ("AirPods Pro,40,1999,Shanghai", )]
   t_env.from_elements(data, ["line"]) \
       .select("split(line) as str_array") \
       .select("get(str_array, 3) as city, "
               "get(str_array, 1).cast(LONG) as count, "
               "get(str_array, 2).cast(LONG) as unit_price") \
       .select("city, count, count * unit_price as total_price") \
       .group_by("city") \
       .select("city, "
               "sum(count) as sales_volume, "
               "sum(total_price) as sales") \
       .insert_into("Results")

   t_env.execute("word_count")


if __name__ == '__main__':
   logging.basicConfig(stream=sys.stdout, level=logging.INFO, format="%(message)s")
   word_count()

执行以下命令,以 per-job 模式提交:

bin/flink run -m yarn-cluster -pyarch venv.zip -pyexec venv.zip/venv/bin/python -py pyflink_job.py

参数说明:

参数 说明
-pyarch/–pyArchives venv.zip 将当前目录下的 venv.zip 上传到yarn集群. -pyarch/--pyArchives 可以上传独立 python 环境或者数据文件,并且只能是 zip 压缩格式。flink 将指定的 zip 文件将被解压到 python UDF worker 的工作目录,默认解压目录名和 zip 压缩包名相同,可以通过#指定解压的目录名,例如 -pyarch data.zip#data1将被解压为 data1。还可以使用 , 分隔指定多个 zip 压缩包,例如:-pyarch file:///tmp/py37.zip,file:///tmp/data.zip#data,然后可以通过 -pyexec 指定 python UDF worker 的 python 路径,例如 -pyexec py37.zip/py37/bin/python;python UDF worker 可以通过 f = open('data/data.txt', 'r')方式获取数据文件
-pyexec/–pyExecutable venv.zip/venv/bin/Python 指定venv.zip中的Python解释器来执行Python UDF,路径需要和zip包内部结构一致。
-py/–python pyflink_job.py 程序入口

flink 还提供了一些其他参数:

参数 说明
-pyfs,–pyFiles 为作业添加自定义的 python 文件,这些文件会被添加到 python UDF woker 的 PYTHONPATH 环境变量,标准 python resource 文件后缀如 .py/.egg/.zip 或者目录都是支持的,可以使用 , 分隔上传多个文件,例如 --pyFiles file:///tmp/myresource.zip,hdfs:///$namenode_address/myresource2.zip
-pym,–pyModule 指定 python 入口文件的 Python module,必须和 --pyFiles 一起使用
-pyreq,–pyRequirements 指定一个 requirements.txt 文件,用来安装第三方依赖,这些依赖会被添加到 python UDF worker 的 PYTHONPATH 环境变量中。用户可以选择的将这些依赖放到一个目录中(python UDF worker 就可以直接用了)用 # 指定要上传的目录。例如 --pyRequirements file:///tmp/requirements.txt#file:///tmp/cached_dir

本文参考
https://enjoyment.cool/2020/01/02/Apache-Flink-%E8%AF%B4%E9%81%93%E7%B3%BB%E5%88%97-PyFlink-%E4%BD%9C%E4%B8%9A%E7%9A%84%E5%A4%9A%E7%A7%8D%E9%83%A8%E7%BD%B2%E6%A8%A1%E5%BC%8F/#more
https://enjoyment.cool/2019/12/05/Apache-Flink-%E8%AF%B4%E9%81%93%E7%B3%BB%E5%88%97-%E5%A6%82%E4%BD%95%E5%9C%A8PyFlink-1-10%E4%B8%AD%E8%87%AA%E5%AE%9A%E4%B9%89Python-UDF

更多内容请关注 大神博客

你可能感兴趣的:(flink-1.10,flink)