如何从 0 到 1 开发 PyFlink API 作业

简介: 以 Flink 1.12 为例,介绍如何使用 Python 语言,通过 PyFlink API 来开发 Flink 作业。

Apache Flink 作为当前最流行的流批统一的计算引擎,在实时 ETL、事件处理、数据分析、CEP、实时机器学习等领域都有着广泛的应用。从 Flink 1.9 开始,Apache Flink 社区开始在原有的 Java、Scala、SQL 等编程语言的基础之上,提供对于 Python 语言的支持。经过 Flink 1.9 ~ 1.12 以及即将发布的 1.13 版本的多个版本的开发,目前 PyFlink API 的功能已经日趋完善,可以满足绝大多数情况下 Python 用户的需求。接下来,我们以 Flink 1.12 为例,介绍如何使用 Python 语言,通过 PyFlink API 来开发 Flink 作业。内容包括:

  1. 环境准备
  2. 作业开发
  3. 作业提交
  4. 问题排查
  5. 总结

 

环境准备

第一步:安装 Python

PyFlink 仅支持 Python 3.5+,您首先需要确认您的开发环境是否已安装了 Python 3.5+,如果没有的话,首先需要安装 Python 3.5+。

第二步:安装 JDK

我们知道 Flink 的运行时是使用 Java 语言开发的,所以为了执行 Flink 作业,您还需要安装 JDK。Flink 提供了对于 JDK 8 以及 JDK 11 的全面支持,您需要确认您的开发环境中是否已经安装了上述版本的 JDK,如果没有的话,首先需要安装 JDK。

第三步:安装 PyFlink

接下来需要安装 PyFlink,可以通过以下命令进行安装:

# 创建 Python 虚拟环境
python3 -m pip install virtualenv
virtualenv -p `which python3` venv

# 使用上述创建的 Python 虚拟环境
./venv/bin/activate

# 安装 PyFlink 1.12
python3 -m pip install apache-flink==1.12.2

作业开发

PyFlink Table API 作业

我们首先介绍一下如何开发 PyFlink Table API 作业。

■ 1)创建 TableEnvironment 对象

对于 Table API 作业来说,用户首先需要创建一个 TableEnvironment 对象。以下示例定义了一个 TableEnvironment 对象,使用该对象的定义的作业,运行在流模式,且使用 blink planner 执行。

env_settings = EnvironmentSettings.new_instance().in_streaming_mode().use_blink_planner().build()
t_env = StreamTableEnvironment.create(environment_settings=env_settings)

■ 2)配置作业的执行参数

可以通过以下方式,配置作业的执行参数。以下示例将作业的默认并发度设置为4。

t_env.get_config().get_configuration().set_string('parallelism.default', '4')

■ 3)创建数据源表

接下来,需要为作业创建一个数据源表。PyFlink 中提供了多种方式来定义数据源表。

方式一:from_elements

PyFlink 支持用户从一个给定列表,创建源表。以下示例定义了包含了 3 行数据的表:[("hello", 1), ("world", 2), ("flink", 3)],该表有 2 列,列名分别为 a 和 b,类型分别为 VARCHAR 和 BIGINT。

tab = t_env.from_elements([("hello", 1), ("world", 2), ("flink", 3)], ['a', 'b'])

说明:

  • 这种方式通常用于测试阶段,可以快速地创建一个数据源表,验证作业逻辑
  • from_elements 方法可以接收多个参数,其中第一个参数用于指定数据列表,列表中的每一个元素必须为 tuple 类型;第二个参数用于指定表的 schema

方式二:DDL

除此之外,数据也可以来自于一个外部的数据源。以下示例定义了一个名字为my_source,类型为 datagen 的表,表中有两个类型为 VARCHAR 的字段。

t_env.execute_sql("""
        CREATE TABLE my_source (
          a VARCHAR,
          b VARCHAR
        ) WITH (
          'connector' = 'datagen',
          'number-of-rows' = '10'
        )
    """)

tab = t_env.from_path('my_source')

说明:

  • 通过 DDL 的方式来定义数据源表是目前最推荐的方式,且所有 Java Table API & SQL 中支持的 connector,都可以通过 DDL 的方式,在 PyFlink Table API 作业中使用,详细的 connector 列表请参见 Flink 官方文档 [1]。
  • 当前仅有部分 connector 的实现包含在 Flink 官方提供的发行包中,比如 FileSystem,DataGen、Print、BlackHole 等,大部分 connector 的实现当前没有包含在 Flink 官方提供的发行包中,比如 Kafka、ES 等。针对没有包含在 Flink 官方提供的发行包中的 connector,如果需要在 PyFlink 作业中使用,用户需要显式地指定相应 FAT JAR,比如针对 Kafka,需要使用 JAR 包 [2],JAR 包可以通过如下方式指定:
# 注意:file:///前缀不能省略
t_env.get_config().get_configuration().set_string("pipeline.jars", "file:///my/jar/path/flink-sql-connector-kafka_2.11-1.12.0.jar")

方式三:catalog

hive_catalog = HiveCatalog("hive_catalog")
t_env.register_catalog("hive_catalog", hive_catalog)
t_env.use_catalog("hive_catalog")

# 假设hive catalog中已经定义了一个名字为source_table的表
tab = t_env.from_path('source_table')

这种方式和 DDL 的方式类似,只不过表的定义事先已经注册到了 catalog 中了,不需要在作业中重新再定义一遍了。

■ 4)定义作业的计算逻辑

方式一:通过 Table API

得到 source 表之后,接下来就可以使用 Table API 中提供的各种操作,定义作业的计算逻辑,对表进行各种变换了,比如:

@udf(result_type=DataTypes.STRING())
def sub_string(s: str, begin: int, end: int):
   return s[begin:end]

transformed_tab = tab.select(sub_string(col('a'), 2, 4))

方式二:通过 SQL 语句

除了可以使用 Table API 中提供的各种操作之外,也可以直接通过 SQL 语句来对表进行变换,比如上述逻辑,也可以通过 SQL 语句来实现:

t_env.create_temporary_function("sub_string", sub_string)
transformed_tab = t_env.sql_query("SELECT sub_string(a, 2, 4) FROM %s" % tab)

说明:

  • TableEnvironment 中提供了多种方式用于执行 SQL 语句,其用途略有不同:

image.png

■ 5)查看执行计划

用户在开发或者调试作业的过程中,可能需要查看作业的执行计划,可以通过如下方式。

方式一:Table.explain

比如,当我们需要知道 transformed_tab 当前的执行计划时,可以执行:print(transformed_tab.explain()),可以得到如下输出:

== Abstract Syntax Tree ==
LogicalProject(EXPR$0=[sub_string($0, 2, 4)])
+- LogicalTableScan(table=[[default_catalog, default_database, Unregistered_TableSource_582508460, source: [PythonInputFormatTableSource(a)]]])

== Optimized Logical Plan ==
PythonCalc(select=[sub_string(a, 2, 4) AS EXPR$0])
+- LegacyTableSourceScan(table=[[default_catalog, default_database, Unregistered_TableSource_582508460, source: [PythonInputFormatTableSource(a)]]], fields=[a])

== Physical Execution Plan ==
Stage 1 : Data Source
    content : Source: PythonInputFormatTableSource(a)

    Stage 2 : Operator
        content : SourceConversion(table=[default_catalog.default_database.Unregistered_TableSource_582508460, source: [PythonInputFormatTableSource(a)]], fields=[a])
        ship_strategy : FORWARD

        Stage 3 : Operator
            content : StreamExecPythonCalc
            ship_strategy : FORWARD

方式二:TableEnvironment.explain_sql

方式一适用于查看某一个 table 的执行计划,有时候并没有一个现成的 table 对象可用,比如:

print(t_env.explain_sql("INSERT INTO my_sink SELECT * FROM %s " % transformed_tab))

其执行计划如下所示:

== Abstract Syntax Tree ==
LogicalSink(table=[default_catalog.default_database.my_sink], fields=[EXPR$0])
+- LogicalProject(EXPR$0=[sub_string($0, 2, 4)])
   +- LogicalTableScan(table=[[default_catalog, default_database, Unregistered_TableSource_1143388267, source: [PythonInputFo
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值