本文主要参考了 孙金城 大佬的下面几篇文章:
Apache Flink 说道系列 - PyFlink 作业的多种部署模式
Three Min Series - How to using PyFlink Shell
代码参考:https://github.com/pyflink/playgrounds
结合自己测试过程,有些地方做了修改,做一个记录。
如果已经获得 flink 二进制包,可以跳过该步骤。
可以参考官方文档 Build Flink
注意环境要求:
= Java 8u51
使用下面命令编译:
git clone https://github.com/apache/flink
mvn clean install -DskipTests
如果已经获得 pyflink 二进制包,或者使用 pip 安装,可以跳过该步骤。
参考官方文档 Build PyFlink
需要注意的是,需要在 Python 3.5+ 环境下编译。
注意
:需要 Python 3.5+。
进入 flink 源码目录,执行以下命令:
cd flink-python; python setup.py sdist bdist_wheel
上面的命令可以生成 sdist 包和 bdist_wheel 包。生成目录为 flink-python/dist/
,sdist 包和 bdist_wheel 都可以用来安装 pyflink,选择一种即可。
本文只介绍 onyarn 模式的安装、部署
安装机器环境要求:
安装 beam:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple apache-beam==2.19.0
将获得的 sdist 包(apache-flink-*.tar.gz) 拷贝到需要安装的机器上。
-i 指定国内 pip 源,安装会快一点
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple apache-flink-1.11.dev0.tar.gz
这样,通过 pip list 就可以看到 apache-flink 安装好了:
Package Version
------------------- ---------
apache-beam 2.19.0
apache-flink 1.11.dev0
...
因为 yarn 集群上 python 环境不一定符合 flink 程序的版本要求,所以我们需要使用 virtualenv 打包 python 环境。
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple virtualenv
用 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
使用 venv 中的 pip 安装 apache-beam
venv/bin/pip install -i https://pypi.tuna.tsinghua.edu.cn/simple apache-beam==2.19.0
把独立 python 环境打成 zip 包,目前打成的包比较大,有 100 多 MB
zip -r venv.zip venv
Bath:拷贝下面的代码到 word_count.py
文件:
import logging
import os
import shutil
import sys
import tempfile
from pyflink.dataset import ExecutionEnvironment
from pyflink.table import BatchTableEnvironment, TableConfig
def word_count():
content = "line Licensed to the Apache Software Foundation ASF under one " \
"line or more contributor license agreements See the NOTICE file " \
"line distributed with this work for additional information " \
"line regarding copyright ownership The ASF licenses this file " \
"to you under the Apache License Version the " \
"License you may not use this file except in compliance " \
"with the License"
t_config = TableConfig()
env = ExecutionEnvironment.get_execution_environment()
t_env = BatchTableEnvironment.create(env, t_config)
# register Results table in table environment
tmp_dir = tempfile.gettempdir()
result_path = tmp_dir + '/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)
sink_ddl = """
create table Results(
word VARCHAR,
`count` BIGINT
) with (
'connector.type' = 'filesystem',
'format.type' = 'csv',
'connector.path' = '{}'
)
""".format(result_path)
t_env.sql_update(sink_ddl)
elements = [(word, 1) for word in content.split(" ")]
t_env.from_elements(elements, ["word", "count"]) \
.group_by("word") \
.select("word, count(1) as count") \
.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 word_count.py
Streaming: from_kafka_to_kafka.py
from pyflink.datastream import StreamExecutionEnvironment, TimeCharacteristic
from pyflink.table import StreamTableEnvironment, DataTypes, EnvironmentSettings
from pyflink.table.descriptors import Schema, Kafka, Json, Rowtime, Csv
def from_kafka_to_kafka_demo():
# init environment
s_env = StreamExecutionEnvironment.get_execution_environment()
s_env.set_stream_time_characteristic(TimeCharacteristic.EventTime)
s_env.set_parallelism(1)
# use blink table planner
st_env = StreamTableEnvironment \
.create(s_env, environment_settings=EnvironmentSettings
.new_instance()
.in_streaming_mode()
.use_blink_planner().build())
# register source and sink
register_rides_source(st_env)
register_rides_sink(st_env)
# query
st_env.from_path("source").insert_into("sink")
# execute
st_env.execute("2-from_kafka_to_kafka")
def register_rides_source(st_env):
st_env \
.connect( # declare the external system to connect to
Kafka()
.version("universal")
.topic("words")
.start_from_earliest()
.property("zookeeper.connect", "localhost:2181")
.property("bootstrap.servers", "localhost:9092")) \
.with_format( # declare a format for this system
Csv()
.field_delimiter("\n")
.schema(DataTypes.ROW([DataTypes.FIELD("line", DataTypes.STRING())]))) \
.with_schema( # declare the schema of the table
Schema()
.field("line", DataTypes.STRING())) \
.in_append_mode() \
.create_temporary_table("source")
def register_rides_sink(st_env):
st_env \
.connect( # declare the external system to connect to
Kafka()
.version("universal")
.topic("tempwords")
.property("zookeeper.connect", "localhost:2181")
.property("bootstrap.servers", "localhost:9092")) \
.with_format( # declare a format for this system
Csv()
.field_delimiter("\n")
.schema(DataTypes.ROW([DataTypes.FIELD("line", DataTypes.STRING())]))) \
.with_schema( # declare the schema of the table
Schema()
.field('line', DataTypes.STRING())) \
.in_append_mode() \
.create_temporary_table("sink")
if __name__ == '__main__':
from_kafka_to_kafka_demo()
执行以下命令,以 per-job 模式提交:
bin/flink run -m yarn-cluster -pyarch venv.zip -pyexec venv.zip/venv/bin/python -py from_kafka_to_kafka.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
更多内容请关注 大神博客