[Flink/Java] Flink Job 运行问题 FAQ

概述: Flink Job 运行问题 FAQ

历次处理Flink任务的错误情况,一般原因有:

  • 与第三方资源(数据库、OSS等)的网络不互通(搭建环境的早期阶段)、网络不稳定
  • 配置错误 (url / 用户名 / 密码; 大小写、空格等特殊字符)
  • 集群/队列的CU资源不足
  • Flink Job 的 JVM内存不足
  • Flink CDC Job中mysql binlog过期或失效
  • checkpoint保存失败
  • Flink 程序的业务逻辑、数据量太大:导致性能缓慢,导致 checkpoint 超时
  • Flink程序中的依赖组件(OSS、MYSQL、Redis、OLAP数据库等)不稳定/运行崩溃,导致 checkpoint 超时
  • ...
  • 推荐文献

基础常识: Operator Task = subtasks x N 个

  • 当要处理的数据量非常大时,我们可以把一个算子操作,“复制”多份到多个节点,数据来了之后就可以到其中任意一个执行。这样一来,一个算子任务就被拆分成了多个并行的“子任务”(subtasks),再将它们分发到不同节点,就真正实现了并行计算

在Flink执行过程中,每一个算子operator)可以包含一个或多个子任务operator subtask),这些子任务在不同的线程、不同的物理机或不同的容器中完全独立地执行。

image

  • 一个特定算子的子任务subtask)的【个数】被称之为其【并行度】(parallelism)。

这样,包含并行子任务的数据流,就是并行数据流,它需要多个分区stream partition)来分配并行任务。
一般情况下,一个流程序的并行度,可以认为就是其所有算子中最大的并行度
一个程序中,不同的算子可能具有不同的并行度
例如:如上图所示,当前数据流中有source、map、window、sink四个算子,其中sink算子的并行度为1,其他算子的并行度都为2。所以这段流处理程序的并行度就是2。

  • 一个 Flink Job 由多个任务(Task) 组成(算子/Operator、数据源/Source和输出器/Sink)

  • 一个 Task 包括多个并行执行的实例,且每一个实例都处理 Task 输入数据的一个子集。

  • 一个 Task 的并行实例数被称为该 task 的 并行度 (parallelism)。

  • 优先级

  • Task(算子) 级粒度的并行度 > Job级粒度的并行度
  • 代码设置的并行度(C_P) > 大数据(云)平台设置的并行度(BDP_P)

默认并行度

  1. parallelism.default

在任何地方未指定并行度时使用的默认并行度(默认值:1)。

  1. taskmanager.numberOfTaskSlots

任务管理器提供的插槽数(默认值:1)。 每个插槽可以接受一个任务或管道。
在 TaskManager 中拥有多个插槽有助于在并行任务或管道之间分摊某些恒定开销(JVM、应用程序库或网络连接)。
有关详细信息,请参阅任务槽和资源概念部分。
运行更多较小的 TaskManager,每个 TaskManager 有一个插槽是一个很好的起点,可以实现任务之间的最佳隔离。
将相同的资源专用于具有更多插槽的较少较大的 TaskManager 有助于提高资源利用率,但代价是任务之间的隔离较弱(更多任务共享同一个 JVM)。

  • 参考文献

image

最佳实践 : FlinkJobUtils#setParallelism

以某个Task获取/设置并行度的过程为例

  • 目的:既需要能灵活地设置 某个 Task 级的并行度,又能默认设置 Job(全局)级的并行度

支持为每个source/sink/operator设置task级并行度

import cn.johnnyzen.bdp.xxxx.yyyy.zzzz.conf.Constants;
import org.apache.flink.api.java.utils.ParameterTool;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.configuration.GlobalConfiguration;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSink;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FlinkJobUtils {
    private final static Logger logger = LoggerFactory.getLogger(FlinkJobUtils.class);

    /**
     * 全局配置
     * @description
     *  1. 对应 ${FLINK_CONF_DIR}/flink-conf.yaml 配置文件
     *  2. 如 : parallelism.default / taskmanager.numberOfTaskSlots / jobmanager.heap.size / taskmanager.heap.size 等配置项
     */
    private static Configuration globalConfiguration;

    static {
        //globalConfiguration | 读取 {org.apache.flink.configuration.ConfigConstants.ENV_FLINK_CONF_DIR:FLINK_CONF_DIR}/{org.apache.flink.configuration.GlobalConfiguration.FLINK_CONF_FILENAME:"flink-conf.yaml"} 下的配置 | DLI等大数据平台的界面/全局配置也在此处
        globalConfiguration = GlobalConfiguration.loadConfiguration();
        logger.info("Success to loading flink global configuration!");
    }

    public static Integer getDataStreamParallelism(DataStream dataStream){
        return dataStream.getParallelism();
    }
    
    /**
     * 设置 数据流[流式源/流式算子(任务)]并行度 / setting data stream's parallelism
     * @note
     *  1. The parallelism of non parallel operator must be 1.(非并行运算符的并行度必须为1, 如: DataStreamSource 等)
     * @param jobParameterTool | NotEmpty |
     * @param dataStreamOrEnv | NotEmpty | DataStream(Source/Operator/Sink) or StreamExecutionEnvironment
     * @param dataStreamParallelConfiguration | NotEmpty |
     *  eg : {@link  cn.johnnyzen.bdp.xxxx.yyyy.zzzz.conf.Constants#FLINK_SOURCE_KAFKA_PARALLEL_PARAM } / ...
     * @param defaultParallelism | Nullable | 默认并行度 (允许为空,非旁支流不建议设置默认值,即不建议非旁支流在此参数处有值)
     * @reference-doc
     *  [1] [Deployment > Configurations > Parallelism - Apache Flink Docs](https://nightlies.apache.org/flink/flink-docs-release-1.12/deployment/config.html)
     *      # 1) parallelism.default
     *      在任何地方未指定并行度时使用的默认并行度(默认值:1)。
     *      # 2) taskmanager.numberOfTaskSlots
     *      任务管理器提供的插槽数(默认值:1)。 每个插槽可以接受一个任务或管道。
     *      在 TaskManager 中拥有多个插槽有助于在并行任务或管道之间分摊某些恒定开销(JVM、应用程序库或网络连接)。
     *      有关详细信息,请参阅任务槽和资源概念部分。
     *      运行更多较小的 TaskManager,每个 TaskManager 有一个插槽是一个很好的起点,可以实现任务之间的最佳隔离。
     *      将相同的资源专用于具有更多插槽的较少较大的 TaskManager 有助于提高资源利用率,但代价是任务之间的隔离较弱(更多任务共享同一个 JVM)。
     */
    public static void setParallelism(ParameterTool jobParameterTool, Object dataStreamOrEnv, String dataStreamParallelConfiguration, Integer defaultParallelism){
        String parallelism = null;//"1" / ...
        //step1 获取 Task级粒度的并行度配置。若获取失败,则跳过 by 配置文件
        parallelism = jobParameterTool.get(dataStreamParallelConfiguration, null);
        logger.info("{} : {}", dataStreamParallelConfiguration, parallelism);

        //step2 获取 Job 全局级粒度的默认并行度配置。若获取失败,则跳过 by 配置文件
        if(ObjectUtils.isEmpty(parallelism)){
            //step2.1 获取 NACOS 配置中心 或 本地的作业配置文件 中的 全局默认并行度
            //注1 : jobParameterTool : 其配置来源于 NACOS 配置中心 或 本地的作业配置文件, 未合并其他出的配置,如 flink-conf.yaml
            //注2 : 当然可自行优化该对象的配置数据获取逻辑
            parallelism = jobParameterTool.get(Constants.DEFAULT_PARALLEL_PARAM, null);
            logger.info("{} : {} | from `{}` by `jobParameterTool`", dataStreamParallelConfiguration, parallelism, Constants.DEFAULT_PARALLEL_PARAM);

            //step2.2 获取 flink-conf.yaml 的作业配置文件 中的 全局默认并行度
            if(ObjectUtils.isEmpty(parallelism)){
                //注1 : globalConfiguration (来源于 : flink-conf.yaml )
                //注2 : 如果是DLI等大数据平台上(基于YARN/K8S部署运行)时,基本上到了这一步,就不太可能没有值了,因为原则上不可能不提交 flink-conf.yaml (99.999%)
                //注3 : 如果是在本地电脑部署运行时,此处取决于前面步骤是否有配置;否则, 此处仍为 null (但不影响运行)
                parallelism = globalConfiguration.getString(Constants.DEFAULT_PARALLEL_PARAM, ObjectUtils.isEmpty(defaultParallelism)?null : String.valueOf(defaultParallelism) );
                logger.info("{} : {} | from `{}` by `globalConfiguration`", dataStreamParallelConfiguration, parallelism, Constants.DEFAULT_PARALLEL_PARAM);
            }
        }

        //step3 若获取配置成功,则设置,反之跳过
        if(!ObjectUtils.isEmpty(parallelism)){//parallel is not empty
            Integer parallelismInt = Integer.valueOf(parallelism);
            String type = null;
            if(dataStreamOrEnv instanceof SingleOutputStreamOperator){
                SingleOutputStreamOperator dataStreamOperator = (SingleOutputStreamOperator) dataStreamOrEnv;
                dataStreamOperator.setParallelism(parallelismInt);
                type = SingleOutputStreamOperator.class.getCanonicalName();
            } else if (dataStreamOrEnv instanceof DataStreamSource) {
                DataStreamSource dataStreamSource = (DataStreamSource) dataStreamOrEnv;
                dataStreamSource.setParallelism(parallelismInt);
                type = DataStreamSource.class.getCanonicalName();
            } else if(dataStreamOrEnv instanceof DataStreamSink){
                DataStreamSink dataStreamSink = (DataStreamSink) dataStreamOrEnv;
                dataStreamSink.setParallelism(parallelismInt);
                type = DataStreamSink.class.getCanonicalName();
            } else if (dataStreamOrEnv instanceof StreamExecutionEnvironment) {
                StreamExecutionEnvironment streamExecutionEnvironment = (StreamExecutionEnvironment) dataStreamOrEnv;
                streamExecutionEnvironment.setParallelism(parallelismInt);
                type = StreamExecutionEnvironment.class.getCanonicalName();
            } else if ( dataStreamOrEnv instanceof DataSource) {//批处理-数据源
                DataSource dataSource = (DataSource) dataStreamOrEnv;
                dataSource.setParallelism(parallelismInt);
                type = DataSource.class.getCanonicalName();
            } else if( dataStreamOrEnv instanceof MapOperator) {//批处理-Map算子
                MapOperator mapOperator = (MapOperator) dataStreamOrEnv;
                mapOperator.setParallelism(parallelismInt);
                type = MapOperator.class.getCanonicalName();
            } else {
                throw new RuntimeException(String.format("Not support the class type when setting parallelism by parallelism! %s : %d", dataStreamParallelConfiguration, parallelism));
            }
            logger.info("Success to set parallelism({})! type: {}", parallelismInt, type);
        } else {//parallel is empty
            logger.warn("The `{}`'s parallelism is empty now , and will be set a default value = 1 by flink framework!", dataStreamParallelConfiguration);
        }
    }
}
  • Constants
public class Constants {
    /**
     * 作业(全局)默认并行度
     * (Definition by Apache Flink Framework | support versions : [ flink 1.0, flink 1.18 or future version] )
     * @description parallelism.default : 在任何地方未指定并行度时使用的默认并行度(默认值:1)
     * @sample 作业启动时,日志中可见:
     *  "202X-XX-XX 17:06:02,029 INFO  org.apache.flink.configuration.GlobalConfiguration           [] - Loading configuration property: parallelism.default, 2"
     * @reference-doc
     *  [1] [Deployment > Configurations - Apache Flink Docs](https://nightlies.apache.org/flink/flink-docs-release-1.12/deployment/config.html)
     **/
    public final static String DEFAULT_JOB_PARALLEL_PARAM = "parallelism.default";
}
  • Kafka分区数:是指Kafka主题中的分区数量,一个主题可以有多个分区。每个分区都是独立的消息队列,可以并发地接收和处理消息。在Kafka中,分区数决定了消息的并行性,即一个主题的分区数越多,可以同时处理的消息量就越大。

  • Flink并行度:是指一个任务或算子可以并行执行的并发任务数。在Flink中,每个任务或算子都可以独立地处理数据流,而并行度就是同时处理的任务或算子的数量。并行度的设置可以影响Flink任务的并发性以及整体的吞吐量。

  • Kafka分区数与 Flink KafkaSource 并行度之间存在一定的关系。

  • Kafka分区数和Flink KafkaSource 并行度之间的关系是:

  • 1)通常情况下,我们希望【Flink的并行度】与【Kafka的分区数】保持一致

这样每个Flink 任务或KafkaSource算子可以处理一个Kafka分区的数据。
这样可以最大程度地发挥消息的并行性和Flink任务的并发性,提高整体的处理能力和吞吐量。

  • 2)但也可以根据具体的业务需求来调整Flink的并行度。(但必须满足: Kafka Partitions ≥ Flink Job Kafka Source Paralism
  • 如果 Flink KafkaSource 的并行度 < Kafka的分区数,则:Kafka 多个分区的数据将被一个 Flink 任务或算子处理。当数据量大的时候,可能会导致Flink任务数据处理能力的下降、kafka消费组显著的消息积压。
  • 如果 Flink KafkaSource 的并行度 > Kafka的分区数,则:会存在一些任务无法处理数据的情况,Flink KafkaSource 任务因部分节点无法分配到数据而夯住无法提交 checkpoint ,Flink作业必将运行故障(诚然,Flink KafkaSource 也不会自动进入空闲状态)。

此时,您需要降低并行度或向水印策略添加空闲超时。

  • 如果在这段时间内流的某个分区中没有记录流动,则该分区被视为“空闲”,并且不会阻止Flink下游运算符中水印(watermark)的进度。

因此,在设置Flink任务的并行度时,可以考虑将 Flink KafkaSource 的并行度(Paralism)设置为与 Kafka 分区数(Partitions)为【相等】或【倍数关系】( N*Partitions = Partitions),以达到最佳的处理性能。

例如:Flink作业中KafkaSource的下游Task来不及消费/处理上游 KafkaSource Task 的数据,项目实践中可能将 KafkaSource 调小。
例如:也可将Kafka的分区数增大,但增大后,Flink Job需要重新启动或者在作业中开启Kafka的分区自动发现特性。

  • 扩展: flink sink 到 kafka 时,如果Flink并行度 > kafka分区数时,则:会轮询把数据插入到kafka分区中,数据不会丢失。
  • 扩展: flink sink 到 kafka 时,如果Flink并行度 < kafka分区数时,则:也会轮询把数据插入到kafka分区中,数据不会丢失。

因为如果指定key的情况下,则:producer 会按照 hash 规则,把数据hash到相应分区中,
也就是说————flink sink时,并行度之于kafka影响不大,不会存在数据丢失或者分区没有写入的情况。

  • 参考文献
  • 在 Apache Flink 中,disableOperatorChaining() 是一个用于全局禁止算子链式合并的方法。

disableChaining() 不同,disableChaining() 只是作用于某个具体的算子,而 disableOperatorChaining() 则会全局禁止链式合并,确保所有算子都以【独立的任务】执行。

  • 作用

env#disableOperatorChaining() 的主要作用是完全禁用 Flink 的算子链式优化机制
通常,Flink 默认会将多个连续的算子(operator)合并到一个算子链operator chain)中,以减少任务的调度通信的 开销,提高性能。
然而,disableOperatorChaining() 会禁用这个默认行为,确保所有算子都单独执行,从而提升【调试能力】或用于【特殊的性能调优需求】。

  • 全局禁用算子链:所有的算子都将以独立的任务运行,无法进行链式合并。
  • 细粒度的任务调度:每个算子都将独立调度和执行,允许开发者对每个算子的性能进行更细致的控制和监控。

适用于调试:便于观察各个算子的独立行为,分析每个算子对整体执行的影响。

  • 使用场景

调试与监控:在调试复杂的 Flink 应用时,为了更好地观察和分析每个算子的执行行为,可能需要禁用链式合并,从而能够独立监控每个算子的性能指标。
优化性能瓶颈:在某些场景下,如果多个算子被链式合并,某个算子可能会因为资源消耗或延迟影响到其他算子。通过禁用算子链,可以避免这种情况。
复杂计算:如果应用程序中包含复杂的算子链,可能会引发背压等问题,禁用算子链可以帮助解决这些性能问题,使得每个算子独立调度并执行。
任务隔离需求:有时为了优化资源的使用或减少任务之间的相互影响,可能需要将算子进行任务隔离,这时禁用链式合并可以实现。

  • 代码示例
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.datastream.DataStream;
 
public class DisableOperatorChainingExample {
    public static void main(String[] args) throws Exception {
        // 创建执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
 
        // 禁用全局的算子链式合并
        env.disableOperatorChaining();
 
        // 创建数据流
        DataStream<String> stream = env.fromElements("one", "two", "three", "four");
 
        // 每个算子将独立执行,不进行链式合并
        stream.map(value -> {
                    System.out.println("Map 1: " + value);
                    return value.toUpperCase();
                })
                .filter(value -> value.startsWith("T"))
                .map(value -> "Processed: " + value);
 
        // 执行作业
        env.execute("Disable Operator Chaining Example");
    }
}
  • 推荐文献

Q: Flink运行时报 java.lang.IllegalStateException: Buffer pool is destroyed.

问题描述

  • Flink运行时报java.lang.IllegalStateException: Buffer pool is destroyed.

且这个报错在日志中与"Could not forward element to next operator"同时存在。

java.lang.RuntimeException: Buffer pool is destroyed.
    at org.apache.flink.streaming.runtime.io.RecordWriterOutput.pushToRecordWriter(RecordWriterOutput.java:110) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.runtime.io.RecordWriterOutput.collect(RecordWriterOutput.java:89) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.runtime.io.RecordWriterOutput.collect(RecordWriterOutput.java:45) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.api.operators.AbstractStreamOperator$CountingOutput.collect(AbstractStreamOperator.java:718) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.api.operators.AbstractStreamOperator$CountingOutput.collect(AbstractStreamOperator.java:696) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.api.operators.StreamMap.processElement(StreamMap.java:41) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.runtime.tasks.OperatorChain$CopyingChainingOutput.pushToOperator(OperatorChain.java:579) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.runtime.tasks.OperatorChain$CopyingChainingOutput.collect(OperatorChain.java:554) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.runtime.tasks.OperatorChain$CopyingChainingOutput.collect(OperatorChain.java:534) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.api.operators.AbstractStreamOperator$CountingOutput.collect(AbstractStreamOperator.java:718) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.api.operators.AbstractStreamOperator$CountingOutput.collect(AbstractStreamOperator.java:696) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.api.operators.StreamSourceContexts$NonTimestampContext.collect(StreamSourceContexts.java:104) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at com.ucarinc.framework.flink.connectors.flexq.FlexQSource.run(FlexQSource.java:204) ~[flink-connector-flexq-1.8.500-20191206.054312-28.jar:1.8.500-SNAPSHOT]
    at org.apache.flink.streaming.api.operators.StreamSource.run(StreamSource.java:93) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.api.operators.StreamSource.run(StreamSource.java:57) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.runtime.tasks.SourceStreamTask.run(SourceStreamTask.java:97) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.runtime.tasks.StreamTask.invoke(StreamTask.java:300) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.runtime.taskmanager.Task.run(Task.java:711) [flink-runtime_2.11-1.8.1.jar:1.8.1]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_31]
Caused by: java.lang.IllegalStateException: Buffer pool is destroyed.
    at org.apache.flink.runtime.io.network.buffer.LocalBufferPool.internalRequestMemorySegment(LocalBufferPool.java:264) ~[flink-runtime_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.runtime.io.network.buffer.LocalBufferPool.requestMemorySegment(LocalBufferPool.java:240) ~[flink-runtime_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.runtime.io.network.buffer.LocalBufferPool.requestBufferBuilderBlocking(LocalBufferPool.java:218) ~[flink-runtime_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.runtime.io.network.api.writer.RecordWriter.requestNewBufferBuilder(RecordWriter.java:264) ~[flink-runtime_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.runtime.io.network.api.writer.RecordWriter.getBufferBuilder(RecordWriter.java:257) ~[flink-runtime_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.runtime.io.network.api.writer.RecordWriter.copyFromSerializerToTargetChannel(RecordWriter.java:177) ~[flink-runtime_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.runtime.io.network.api.writer.RecordWriter.emit(RecordWriter.java:162) ~[flink-runtime_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.runtime.io.network.api.writer.RecordWriter.emit(RecordWriter.java:128) ~[flink-runtime_2.11-1.8.1.jar:1.8.1]
    at org.apache.flink.streaming.runtime.io.RecordWriterOutput.pushToRecordWriter(RecordWriterOutput.java:107) ~[flink-streaming-java_2.11-1.8.1.jar:1.8.1]
    ... 18 more

问题分析

  • 一般为为任务network buffer不足。可以调整下任务的network buffer的大小。

解决方法

  • 方法1:清理/腾出运行的计算机内存资源,尔后重新提交运行 (亲测有效)

  • 方法2:高级参数中添加:taskmanager.memory.network.fraction 0.2 (默认值为0.1,可根据实际情况适当调整)

参考文献

  • 在 Apache Flink 中,sinkToaddSink 是两种用于将数据写入外部存储或系统的方式。它们的主要区别在于实现方式和适用场景。

addSink

  • addSink
  • addSink 是 Flink 中较早的输出算子,适用于自定义实现的场景。它需要用户实现 SinkFunction 接口,并重写 invoke() 方法来定义数据写入逻辑。
  • 示例代码
DataStream<String> dataStream = ...; // 数据流
dataStream.addSink(
    new SinkFunction<String>() {
		@Override
		public void invoke(String value, Context context) {
			// 自定义写入逻辑
			System.out.println("写入数据: " + value);
		}
	}
);
  • 特点:

灵活性高,适合自定义需求。
需要用户手动处理容错和事务一致性。
在 Flink 1.12 之前是主要的 Sink 实现方式。

sinkTo

  • sinkTo 是 Flink 1.12 引入的新方法,基于新的 Sink API 架构,提供了更高的抽象和易用性。它支持多种内置的 Sink,如 Kafka、文件系统、JDBC 等。

  • 示例代码:

DataStream<String> dataStream = ...; // 数据流
FileSink<String> fileSink = FileSink
  .forRowFormat(new Path("output/path"), new SimpleStringEncoder<>("UTF-8"))
  .build();

dataStream.sinkTo(fileSink);
  • 特点:
  • 提供了更简洁的接口,减少了用户的实现负担。
  • 支持高级功能,如分桶策略、滚动策略等。
  • 更适合与 Flink 的 Checkpoint 机制结合,支持精确一次语义。
在Flink中,Idle状态是指作业处于【空闲状态】,即没有输入数据可用于处理的状态。当Flink作业没有输入数据可供处理时,它将进入Idle状态。
理解Flink中的Idle状态可以从以下几个方面来考虑:

无输入数据:Idle状态表示当前作业没有输入数据可用于处理。这可能是因为输入源暂时没有可用的数据,或者数据流已经被处理完毕。
空闲等待:在Idle状态下,Flink作业会等待输入数据的到来。它会周期性地检查输入源是否有新的数据产生,一旦有新的数据产生,作业将从Idle状态转换为Active状态开始处理数据。
优化机会:Idle状态也给了Flink一些优化的机会。在空闲状态下,Flink可以进行一些优化操作,例如重新分配资源、调整并行度、合并任务等,以提高作业的性能和效率。

需要注意的是,Idle状态并不代表作业已经完成或者失败,它只是作业在等待输入数据的状态。一旦有新的输入数据到来,作业将离开Idle状态并开始处理数据。

Q: 提交Flink作业时,日志报java.net.BindException: Could not start actor system on any port in port range 6123

问题描述

  • 提交Flink作业时,日志报 java.net.BindException: Could not start actor system on any port in port range 6123

问题分析

  • 用户提交flink作业时,提示端口被占用。

解决思路

  • 确保之前的老作业被关闭(如:kill掉作业),再二次提交。

参考文献

X 参考文献

参考文献

推荐文献

posted @ 2025-07-30 14:23  千千寰宇  阅读(110)  评论(0)    收藏  举报