文章目录

  • 1. 需引入的pom依赖
  • 2. Flink SQL批处理Demo
  • 3. Flink SQL流处理Demo
  • 4. Old Planner与Blink Planner
  • 4.1 Old Planner的使用
  • 4.2 Blink Planner的优势与局限


Flink各个版本之间的API有比较大的gap,笔者将程序从Flink 1.7升级到Flink 1.11时,中间遇到了很多小问题。这里,给出一个使用Flink 1.11版本SQL API使用demo,并对需要注意的点和编写过程进行详细说明。

1. 需引入的pom依赖

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-clients_${scala.version}</artifactId>
    <version>${flink.version}</version>
</dependency>

<!-- Table API and SQL components -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-table</artifactId>
    <version>${flink.version}</version>
    <type>pom</type>
</dependency>

上面两个是Flink API和Table&SQL开发基础依赖,没什么好说的,新建项目时引入就对了。

<!-- blink planner -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-table-planner-blink_${scala.version}</artifactId>
    <version>${flink.version}</version>
</dependency>

<!-- flink planner -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-table-planner_${scala.version}</artifactId>
    <version>${flink.version}</version>
</dependency>

Flink 1.9 到 Flink 1.11 版本中,Flink old Planner和Blink Planner并存,且Flink 1.11将Blink Planner设置为默认Planner。因此开发的时候,需要根据需要引入其一,或两个都引入也可以。

注意:从maven上直接拷贝下来的pom依赖的设置为provided,本地执行flink程序时,会报找不到相应planner。删掉<scope>provided</scope>即可。

Exception in thread “main” org.apache.flink.table.api.NoMatchingTableFactoryException: Could not find a suitable table factory for ‘org.apache.flink.table.delegation.ExecutorFactory’ in
the classpath.

<!-- use the Table API & SQL for defining pipelines -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-table-api-java-bridge_${scala.version}</artifactId>
    <version>${flink.version}</version>
</dependency>

使用Flink Table API和SQL API时需要适配不同语言的pipelines,因此需要根据自己实际开发语言引入bridge依赖,这里笔者使用java开发。当使用scala开发时,则引入相应scala-bridge。

<!-- kafka connector -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-kafka_${scala.version}</artifactId>
    <version>${flink.version}</version>
</dependency>

<!-- flink-sql-connector-kafka -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-sql-connector-kafka_${scala.version}</artifactId>
    <version>${flink.version}</version>
</dependency>

实时开发时使用最多的数据源就是kafka,上面两个依赖分别为Flink API 和Flink SQL 使用kafka的connector。

注意:要注意flink与kafka版本适配,flink 1.11只支持kafka 10和11,否则程序运行时会报错。

<!-- flink-json -->
<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-json</artifactId>
    <version>${flink.version}</version>
</dependency>

使用Flink的format格式解析文本时,需要引入相应format依赖。笔者程序中读取的数据为json格式,因此引入flink-json的依赖。

2. Flink SQL批处理Demo

话不多说,先给代码😋

import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.TableEnvironment;

/**
 * Created by search-lemon on 2020/10/27.
 * 读取本地url_parse_100.txt文件,解析后输出到控制台.
 */
public class BatchSqlTest {

    // use blink planner
    private static TableEnvironment tableEnv;

    public static void main(String[] args) {
        initBlinkEnv();
        registerFileSource();
        print();
    }

    private static void initBlinkEnv() {
        EnvironmentSettings tableEnvSettings = EnvironmentSettings
            .newInstance()
            .useBlinkPlanner() // 设置使用BlinkPlanner
            .inBatchMode() // 设置批处理模式
            .build();

        tableEnv = TableEnvironment.create(tableEnvSettings);
    }

    private static void registerFileSource() {

        String sourceSql = "CREATE TABLE url_parse_100 ("
            + " logtime STRING,"
            + " sign STRING,"
            + " version STRING"
            + " ) WITH ( "
            + " 'connector' = 'filesystem',"
            + " 'path' = 'E:/url_parse_100.txt',"
            + " 'format' = 'json'"
            + ")";

        tableEnv.executeSql(sourceSql); // 注册source表到env中
    }

    /**
     * 注册print table并输出数据.
     */
    private static void print() {

        String printTable = "CREATE TABLE print_table"
            + " WITH ('connector' = 'print')"
            + " LIKE url_parse_100 (EXCLUDING ALL)"; // 注册print表到env中

        tableEnv.executeSql(printTable);

        String printData = "INSERT INTO print_table"
            + " SELECT logtime, sign, version"
            + " FROM url_parse_100";

        tableEnv.executeSql(printData); // 输出数据到控制台

    }

上例很好理解,读取文本格式为json的 E:/url_parse_100.txt 文件,解析出 logtime, sign, version 三个字段,再将其输出到控制台。

3. Flink SQL流处理Demo

import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.TableEnvironment;

/**
 * Created by search-lemon on 2020/10/27.
 * 读取kafka数据,解析后输出到控制台.
 */
public class StreamSqlTest {

    // use blink planner
    private static TableEnvironment tableEnv;

    public static void main(String[] args) throws Exception {
        initBlinkEnv();
        registerKafkaSource();
        print();
    }

    private static void initBlinkEnv() {
        EnvironmentSettings tableEnvSettings = EnvironmentSettings
            .newInstance()
            .useBlinkPlanner()
            .inStreamingMode()
            .build();

        // stream也可使用StreamTableEnvironment
        tableEnv = TableEnvironment.create(tableEnvSettings);
    }

    /**
     * Flink 1.11只支持kafka 10和11版本.
     */
    private static void registerKafkaSource() {

        String sourceSql = "CREATE TABLE flink_input_test ("
            + " logtime STRING,"
            + " sign STRING,"
            + " version STRING"
            + " ) WITH ( "
            + " 'connector' = 'kafka',"
            + " 'topic' = 'flink_input_test'," // kafak topic
            + " 'properties.bootstrap.servers' = '*******'," // kafak brokers
            + " 'properties.group.id' = 'test_20201102'," // kafka group.id
            + " 'format' = 'json'," // kafka中数据格式
            + " 'scan.startup.mode' = 'latest-offset'" // 设置从最新offset开始消费
            + ")";

        tableEnv.executeSql(sourceSql);
    }

    /**
     * 注册print table并输出数据.
     */
    private static void print() {

        String printTable = "CREATE TABLE print_table"
            + " WITH ('connector' = 'print')"
            + " LIKE flink_input_test (EXCLUDING ALL)"; // 这里使用LIKE直接创建注册print表

        tableEnv.executeSql(printTable);

        String printData = "INSERT INTO print_table"
            + " SELECT logtime, sign, version"
            + " FROM flink_input_test";

        tableEnv.executeSql(printData);

    }

上述Batch和Stream的程序都使用了BlinkPlanner(),可以看出它们的程序非常相似,这是由于Flink 1.11的Blink Planner在Table&SQL API上已经实现了批流合一,都可以使用TableEnvironment对象作为程序运行环境,通过配置EnvironmentSettings控制运行的是实时还是离线程序。这样(除了source源表的注册)实时和离线系统完全可使用同一套代码,减少了大量重复的开发工作。

4. Old Planner与Blink Planner

4.1 Old Planner的使用

import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.bridge.java.BatchTableEnvironment;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;

/**
 * Created by search-lemon on 2020/10/27.
 * 测试初始化 Old Planner运行环境.
 */
public class InitEnvTest {

    // use flink old planner
    private static StreamTableEnvironment streamTableEnv;
    private static BatchTableEnvironment bTableEnv;

    public static void main(String[] args) {
        initFlinkBatchEnv(); // 初始化batch环境
        initFlinkStreamEnv(); // 初始化stream环境
    }

    private static void initFlinkBatchEnv() {
        ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
        bTableEnv = BatchTableEnvironment.create(env);
    }

    private static void initFlinkStreamEnv() {
        EnvironmentSettings settings = EnvironmentSettings
            .newInstance()
            .useOldPlanner()
            .inStreamingMode()
            .build();

        StreamExecutionEnvironment streamEnv = StreamExecutionEnvironment.getExecutionEnvironment();
        streamTableEnv = StreamTableEnvironment.create(streamEnv, settings);
    }

可以看出,Flink Old Planner中批和流处理环境是分离的,需要分别使用StreamTableEnvironmentBatchTableEnvironment来运行实时和离线程序。

不同的Environment和Planner是配套使用的。当然,如果使用Blink Planner来处理流数据也可以使用StreamExecutionEnvironment。反之,Flink Old Planner没法儿使用批流合一的TableEnvironment。

4.2 Blink Planner的优势与局限

从Flink最近几个版本的发展来看,Blink Planner逐渐成为主流。Flink Old Planner是将Flink 1.9版本之前的核心执行计划优化和转化部分提取出来形成一个模块,与新开发的Blink Planner并列,提供两个Planner供用户自行选择。

Old Planner中,Table&SQL API是对底层API(DataStream和DataSet)的封装,批和流的处理完全是两套逻辑代码。而Blink Planner中很多SQL逻辑执行计划可直接转换为物理执行计划执行。架构上,Blink Planner Table&SQL API与DataStream API平行,不再是其简单的封装,舍弃了DataSet API,批和流处理使用同一套转换执行逻辑代码,实现了批流合一。但是局限也是很明显的,TableEnvironment不支持Table&SQL API和DataStream API之间的相互转换,是一个纯SQL的处理过程。这样从低版本直接升级上来的程序如果想要使用新特性就需要做较多适配工作。

Old Planner

Blink Planner(1.9以后版本)

Table API&SQL翻译为物理层API方式

根据“流/批”输入决定翻译为DataStream还是DataSet程序

无论输入为流或批,都翻译为DataStream程序(有些直接翻译为transfomation/底层执行,使得Table API&SQL与DataStream API在架构上是平行的)

Table API&SQL是否可以与物理层API集成(嵌套使用/互相转换)

支持与DataSet API和DataStream API程序集成

将批视为流的特殊情况,不支持Table和DataSet之间的转换,不支持BatchTableSource,使用bounded StreamTableSource替代(根据是否为bounded source区分批流)