Flink ProcessFunction

23.flink processFunction(高级报警功能必会) flink的processFunction是实现高级功能的重要模块,所以flink也是单独列出了一节来讲解,不仅如此市面上的flink开发的书籍基本上也是会把processFunction单独列出一个小节来讲解。另外理解本小节的代码,需要读者理解状态,以及flink三种时间:event time,ingestion time,processing time.下面的例子用的是process time. 本节内容不适合新手小白,适合对flink有一定了解以及有一定编程经验的人。}}}} 阅读详情

Flink ProcessFunction

之前使用的转换算子是无法访问事件的时间戳信息和水位线信息的,在某些应用场景下,我们需要这些信息。因此,DataStream API提供了一系列的Low-Level转换算子。可以访问时间戳、watermark以及注册定时事件。还可以输出特定的一些事件(比如将筛选数据、超时事件等)。ProcessFunction用来构建事件驱动的应用以及实现自定义的业务逻辑,之前使用的window函数和转换算子无法实现。比如,FlinkSQL就是使用ProcessFunction实现的。

Flink提供了8个Process Function:

  • ProcessFunction
  • KeyedProcessFunction
  • CoProcessFunction
  • ProcessJoinFunction
  • BroadcastProcessFunction
  • KeyedBroadcastProcessFunction
  • ProcessWindowFunction
  • ProcessAllWindowFunction

其中最常用的是KeyedProcessFunction。

一、 KeyedProcessFunction

KeyedProcessFunction用来操作KeyedStream。KeyedProcessFunction会处理流的每一个元素,输出为0个、1个或者多个元素。所有的Process Function都继承自RichFunction接口,所以都有open()、close()和getRuntimeContext()等方法。而KeyedProcessFunction<K, I, O>还额外提供了两个方法:

  • processElement(I value, Context ctx, Collector<O> out),流中的每一个元素都会调用这个方法,调用结果将会放在Collector数据类型中输出。Context可以访问元素的时间戳,元素的 key ,以及TimerService 时间服务。 Context 还可以将结果输出到别的流(side outputs)。
  • onTimer(long timestamp, OnTimerContext ctx, Collector<O> out),是一个回调函数。当之前注册的定时器触发时调用。参数timestamp 为定时器所设定的触发的时间戳。Collector 为输出结果的集合。OnTimerContext和processElement的Context 参数一样,提供了上下文的一些信息,例如定时器触发的时间信息(事件时间或者处理时间)。

测试代码:

设置一次获取数据后第5s给出提示信息的定时器

package com.root.process;

import com.root.SensorReading;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.java.tuple.Tuple;
import org.apache.flink.configuration.Configuration;
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.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.util.Collector;

/**
 * @author Kewei
 * @Date 2022/3/7 9:27
 */

public class ProcessTest1_KeyedProcessFunction {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        DataStreamSource<String> inputStream = env.socketTextStream("localhost", 7777);

        SingleOutputStreamOperator<SensorReading> dataStream = inputStream.map(line -> {
            String[] fields = line.split(",");
            return new SensorReading(fields[0], new Long(fields[1]), new Double(fields[2]));
        });

        SingleOutputStreamOperator<Long> resultStream = dataStream.keyBy("id").process(new MyKeyedProcess());

        resultStream.print();

        env.execute();
    }

    public static class MyKeyedProcess extends KeyedProcessFunction<Tuple, SensorReading, Long>{
        ValueState<Long> tsTemp;

        @Override
        public void open(Configuration parameters) throws Exception {
            tsTemp = getRuntimeContext().getState(new ValueStateDescriptor<Long>("ts",Long.class));
        }

        @Override
        public void close() throws Exception {
            tsTemp.clear();
        }

        @Override
        public void processElement(SensorReading value, KeyedProcessFunction<Tuple, SensorReading, Long>.Context context, Collector<Long> out) throws Exception {
            out.collect((long) value.getId().length());

            context.getCurrentKey();
            context.timestamp();

            context.timerService().currentProcessingTime();
            context.timerService().currentWatermark();

            context.timerService().registerProcessingTimeTimer(context.timerService().currentProcessingTime() + 5000L);
            tsTemp.update(context.timerService().currentProcessingTime() + 1000L);

            context.timerService().deleteProcessingTimeTimer(tsTemp.value());
        }

        @Override
        public void onTimer(long timestamp, KeyedProcessFunction<Tuple, SensorReading, Long>.OnTimerContext ctx, Collector<Long> out) throws Exception {
            System.out.println(timestamp + " 定时器触发");
            ctx.getCurrentKey();

            ctx.timeDomain();
        }
    }
}

输出

8
1646622646820 定时器触发

二、TimerService和定时器(Timers)

Context 和OnTimerContext 所持有的TimerService 对象拥有以下方法:

  • long currentProcessingTime() 返回当前处理时间
  • long currentWatermark() 返回当前watermark 的时间戳
  • void registerProcessingTimeTimer( long timestamp) 会注册当前key的processing time的定时器。当processing time 到达定时时间时,触发timer。
  • void registerEventTimeTimer(long timestamp) 会注册当前key 的event time 定时器。当Watermark水位线大于等于定时器注册的时间时,触发定时器执行回调函数。
  • void deleteProcessingTimeTimer(long timestamp) 删除之前注册处理时间定时器。如果没有这个时间戳的定时器,则不执行。
  • void deleteEventTimeTimer(long timestamp) 删除之前注册的事件时间定时器,如果没有此时间戳的定时器,则不执行。

当定时器timer触发时,会调用回调函数onTimer()。注意定时器timer只能在keyedStreams上面使用。

测试代码:

监控温度传感器的温度值,如果温度值在10秒钟之内连续上升,则报警。

package com.root.process;

import com.root.SensorReading;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.common.time.Time;
import org.apache.flink.configuration.Configuration;
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.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.util.Collector;

/**
 * @author Kewei
 * @Date 2022/3/7 9:51
 */

public class ProcessTest2_ApplicationCase {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        DataStreamSource<String> inputStream = env.socketTextStream("localhost", 7777);
        SingleOutputStreamOperator<SensorReading> dataStream = inputStream.map(line -> {
            String[] fields = line.split(",");
            return new SensorReading(fields[0], new Long(fields[1]), new Double(fields[2]));
        });

        // KeyBy之前使用“id”可以分组,这这里无法运行,必须使用方法引用SensorReading::getId
        // 使用process可以做获取当前的id,运行时间戳、以及水位watermark
        dataStream.keyBy(SensorReading::getId)
                .process(new MyKeyedProcess(Time.seconds(10).toMilliseconds()))
                .print();

        env.execute();
    }

    public static class MyKeyedProcess extends KeyedProcessFunction<String, SensorReading,String>{
        // 设置预警的时间
        private Long interval;
        public MyKeyedProcess(Long interval){
            this.interval = interval;
        }

        // 设置值状态,保存上一次的温度
        ValueState<Double> lastTemp;

        // 设置值状态,保存最近一次预警的时间
        ValueState<Long> recentTimerTimeStamp;

        // 设置预警时调用的函数
        @Override
        public void onTimer(long timestamp, KeyedProcessFunction<String, SensorReading, String>.OnTimerContext ctx, Collector<String> out) throws Exception {
            // getCurrentKey()获取当前key,out.collect设置输出内容
            out.collect("传感器 :"+ctx.getCurrentKey()+"温度值连续"+interval+"ms上升");

            // 预警之后清楚最近一次预警的时间
            recentTimerTimeStamp.clear();
        }

        @Override
        public void open(Configuration parameters) throws Exception {
            // 初始化两个状态
            lastTemp = getRuntimeContext().getState(new ValueStateDescriptor<Double>("double",Double.class));
            recentTimerTimeStamp = getRuntimeContext().getState(new ValueStateDescriptor<Long>("long",Long.class));
        }

        @Override
        public void close() throws Exception {
            // 清除两个状态的值
            lastTemp.clear();
            recentTimerTimeStamp.clear();
        }

        @Override
        public void processElement(SensorReading value, KeyedProcessFunction<String, SensorReading, String>.Context ctx, Collector<String> out) throws Exception {
            // 获取当前时间的温度
            Double curTemp = value.getTemperature();
            // 获取前一次的温度
            Double lastTemper = lastTemp.value();

            // 判断前一次的温度是否存在,若不存在使用当前温度,若存在使用本身,便于之后的比较
            lastTemper = lastTemper==null?curTemp:lastTemper;

            // 获取最近一次预警的时间
            Long timerTime = recentTimerTimeStamp.value();

            // 判断当前温度是否大于前一次温度,并且最近一次预警的时间为空(说明还没有预警)
            // 若都满足,设置一个预警时间,注册一个触发器,并更新最近一次预警时间
            if (curTemp>lastTemper && null == timerTime){
                long warningTimer = ctx.timerService().currentProcessingTime() + interval;
                ctx.timerService().registerProcessingTimeTimer(warningTimer);
                recentTimerTimeStamp.update(warningTimer);
            }

            // 若当前温度小于等于前一次温度,以及前一次预警时间不为空(说明已经预警了)
            // 若都满足,就是说温度下降了,需要把触发器删除,以及清除预警时间
            if (curTemp <= lastTemper && timerTime != null){
                ctx.timerService().deleteProcessingTimeTimer(timerTime);
                recentTimerTimeStamp.clear();
            }

            // 更新上次温度
            lastTemp.update(curTemp);
        }
    }
}

输出

传感器 :sensor_1温度值连续10000ms上升

三、侧输出流(SideOutput)

一个数据可以被多个window包含,只有其不被window包含的时候(包含该数据的所有window都关闭之后),才会被丢到侧输出流。

如果一个数据被丢到侧输出流,那么所有包含该数据的window都由于已经超过了“允许的迟到的时间”而关闭了,进而新来的迟到数据只能被丢到测输出流!


  • 大部分的DataStream API 的算子的输出是单一输出,也就是某种数据类型的流。除了split 算子,可以将一条流分成多条流,这些流的数据类型也都相同。
  • processfunction 的side outputs 功能可以产生多条流,并且这些流的数据类型可以不一样。
  • 一个side output 可以定义为OutputTag[X]对象,X 是输出流的数据类型。
  • processfunction 可以通过Context 对象发射一个事件到一个或者多个side outputs。

测试代码:

将温度≥30放入高温输出,反正放入低温测流输出。

package com.root.process;

import com.root.SensorReading;
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.apache.flink.streaming.api.functions.ProcessFunction;
import org.apache.flink.util.Collector;
import org.apache.flink.util.OutputTag;

/**
 * @author Kewei
 * @Date 2022/3/7 10:47
 */

public class ProcessTest3_SideOutCase {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(1);

        DataStreamSource<String> inputStream = env.socketTextStream("localhost", 7777);
        SingleOutputStreamOperator<SensorReading> dataStream = inputStream.map(line -> {
            String[] fields = line.split(",");
            return new SensorReading(fields[0], new Long(fields[1]), new Double(fields[2]));
        });

        // 定义一个OutputTag,用来表示侧输出流低温流
        // An OutputTag must always be an anonymous inner class
        // OutputTag必须始终是匿名的内部类
        // so that Flink can derive a TypeInformation for the generic type parameter.
        // 这样Flink就可以导出泛型类型参数的类型信息
        OutputTag<SensorReading> outputTag = new OutputTag<SensorReading>("lowTemp") {
        };

        SingleOutputStreamOperator<SensorReading> resultStream = dataStream.keyBy(SensorReading::getId)
                .process(new ProcessFunction<SensorReading, SensorReading>() {
                    @Override
                    public void processElement(SensorReading value, ProcessFunction<SensorReading, SensorReading>.Context ctx, Collector<SensorReading> out) throws Exception {
                        Double curTemp = value.getTemperature();

                        // 若温度大于30,正常输出,否则将数据写道测流中,context.output
                        if (curTemp > 30.0) {
                            out.collect(value);
                        } else {
                            ctx.output(outputTag, value);
                        }
                    }
                });

        resultStream.print("high-temp");

        // 获取测流的数据
        resultStream.getSideOutput(outputTag).print("low-temp");

        env.execute();
    }
}

输出

high-temp> SensorReading{id='sensor_1', timestamp=1547718209, temperature=32.8}
low-temp> SensorReading{id='sensor_1', timestamp=1547718209, temperature=29.5}

四、CoProcessFunction

  • 对于两条输入流,DataStream API 提供了CoProcessFunction 这样的low-level操作。CoProcessFunction 提供了操作每一个输入流的方法: processElement1()和processElement2()。
  • 类似于ProcessFunction,这两种方法都通过Context 对象来调用。这个Context对象可以访问事件数据,定时器时间戳,TimerService,以及side outputs。
  • CoProcessFunction 也提供了onTimer()回调函数。
Flink处理函数(ProcessFunction、KeyedProcessFunction、ProcessWindowFunction、 ProcessAllWindowFunction) /定义一小时窗口@Overridecount++; 阅读详情

相关推荐

Flink处理函数实战之二:ProcessFunction类

;

2401_89172995的博客 783

Flink Process Function

处理函数ProcessFunction的用法

959 2064

大数据Flink(四十二):​​​​​​​BroadcastState

在开发过程中,如果遇到需要下发/广播配置、规则等低吞吐事件流到下游所有 task 时,就可以使用 Broadcast State。Broadcast State 是 Flink 1.5 引入的新特性。下游的 task 接收这些配置、规则并保存为 BroadcastState, 将这些配置应用到另一个数据流的计算中。

Lansonli(蓝深李)的博客 3708

Flink-处理函数

在处理函数中,我们直面的就是数据流中最基本的元素:数据事件(event)、状态(state) 以及时间(time)。这就相当于对流有了完全的控制权。处理函数比较抽象,没有具体的操作, 所以对于一些常见的简单应用(比如求和、开窗口)会显得有些麻烦;不过正是因为它不限定 具体做什么,所以理论上我们可以做任何事情,实现所有需求。所以可以说,处理函数是我们 进行 Flink 编程的“大招”,轻易不用,一旦放出来必然会扫平一切。

m0_63475429的博客 1593

Flink ProcessFunction 使用总结

Flink ProcessFunction 使用总结

congge_study的博客 6543

保姆级教程:用GROMACS的FEP方法计算小分子结合自由能(附完整mdp配置)

本文提供了一份详细的GROMACS自由能微扰(FEP)计算教程,涵盖从系统准备、参数配置到结果分析的全流程。通过具体案例演示如何计算小分子结合自由能,包括λ窗口设计、动力学参数优化及并行化执行技巧,帮助研究者克服FEP计算中的常见问题,获得可靠的自由能数据。

weixin_30598225的博客 352

Flink ProcessFunction详解

前言 我们之前学习的转换算子是无法访问事件的时间戳信息和水位线信息的。而这.在一.些应用场景下,极为重要。例如MapFunction这样的map转换算子就无法访问时间戳或者当前事件的事件时间。 基于此,DataStreamAPI提供了一系列的Low-Level转换算子。可以访问时间戳、watermark以及注册定时事件。还可以输出特定的-一些事件,例如超时事件等。ProcessFunction用来构建事件驱动的应用以及实现自定义的业务逻辑(使用之前的window函数和转换算子无法实现)。例如,Flink

菜鸟也学大数据的博客 3365

Qt5开发从入门到精通——第一篇概述

Qt是一个跨平台的C++图形用户界面应用程序框架。它为应用程序开发者提供建立艺术级图形用户界面所需的所有功能。它是完全面向对象的,很容易扩展,并且允许真正的组件编程。在Qt程序开发过程中,除可以通过手写代码实现软件开发功能外,还可以通过Qt的GUI界面设计器(QtDesigner)进行界面的绘制和布局。该工具提供了Qt的基本可绘制窗口部件,如QWidget、QLabel、QPushButton和QVBoxLayout等。•对象查看器•属性编辑器。.........

weixin_44759598的博客 9506

Flink ProcessFunction 介绍使用

目录 实现功能 代码 测试 问题 官网描述:https://ci.apache.org/projects/flink/flink-docs-release-1.10/zh/dev/stream/operators/process_function.html TheProcessFunctionis a low-level stream processing operation, ...

jast 2169

高分7(GF7)卫星数据制作平原地区DSM/DEM(三)——可信度分割

李国春(接上)

副组长的博客 1713

BLDC方波控制 vs. FOC:技术选型指南与未来演进

本文深入对比BLDC电机方波控制与FOC技术的核心差异,分析两者在成本、性能和应用场景上的权衡。方波控制凭借低成本和易实现性,在电动工具和家电领域优势明显;而FOC则以平滑高效控制见长,适用于高端精密应用。文章为工程师提供了实用的技术选型指南,并展望了未来技术融合的发展趋势。

yhn4567890的博客 1171

Flink的ProcessFunction API

1 ProcessFunction    ProcessFunction是一个低阶的流处理操作,可以访问事件(event)(流元素),状态(state)(容错性,一致性,仅在keyed stream中),定时器(timers)(event time和processing time, 仅在keyed stream中)。也就是说可以访问普通的转换算子无法访问事件的时间戳信息和Watermark的。    ProcessFunction可以看作是一个具有keyed state 键控状态和 timers定时器访问权

fighting 1498

Pytorch实现EdgeCNN(基于PyTorch实现)

本专栏整理了《图神经网络代码实战》,内包含了不同图神经网络的相关代码实现(PyG以及自实现),理论与实践相结合,如GCN、GAT、GraphSAGE等经典图网络,每一个代码实例都附带有完整的代码。

优快云 精品推荐 1万+

Flink之ProcessFunction

目录Flink Process FunctionProcess Function 我们之前学习的转换算子是无法访问事件的时间戳信息和水位线信息的。而这在一些应 用场景下,极为重要。例如 MapFunction 这样的map 转换算子就无法访问时间戳或者当前事 件的事件时间。 基于此,DataStream API 提供了一系列的 Low-Level 转换算子。可以访问时间戳、 watermark 以及注册定时事件。还可以输出特定的一些事件,例如超时事件等。Process Function用来构建事件驱动的应

蔡政洁的博客 931

RT-DETR目标检测模型性能评价指标详解,涉及混淆矩阵、F1-Score、IoU、mAP、参数量、计算量等,一文打尽所有评价指标

本文主要讲解的是计算机视觉网络模型的性能评价指标,这是每一篇论文中都需要展示的内容。熟悉每一个评价指标也能够帮助我们在训练和改进模型过程中,更好的观察模型的变化效果。

Limiiiing的博客 3357

RK3588更新NPU驱动的方法

想要在 RK3588 上跑一下 deepseek 的大模型,发现目前的内核提供的 NPU 驱动比较低,无法支持新的大模型,所以需要更新一下我们的 NPU 驱动本次更新的NPU驱动仅适用于RK3588和RK3676系列。

荻夜的博客 2151

Flink ProcessFunction onTimer 延迟处理数据

ProcessFunction和CoProcessFunction 说明 DataStream与KeyedStreamd都有Process方法, DataStream接收的是ProcessFunction,而KeyedStream接收的是KeyedProcessFunction(原本也支持ProcessFunction,现在已被废弃) 0.AbstractRichFunction介绍 1....

resemble的博客 8431

古风仙侠少女三视图提示词,一键生成角色设定

3D中国写实CG风格,CG渲染质感,精细3D建模,PBR物理材质,细腻服装纹理,电影级灯光,丰富表情,高精度场景,次世代引擎画质。平视正交视角,统一光照,纯白背景,无文字无水印,避免强透视和动态变形。3D中国写实CG风格,CG渲染质感,精细3D建模,PBR物理材质,细腻服装纹理,电影级灯光,丰富表情,高精度场景,次世代引擎画质。3D中国写实CG风格,CG渲染质感,精细3D建模,PBR物理材质,细腻服装纹理,电影级灯光,丰富表情,高精度场景,次世代引擎画质。少女,古风仙侠少女气质,灵动、温婉、仙气。

欢迎来到鸾姝淡月博客,专为程序员和编程爱好者打造的技术博客。我们分享编程语言趋势、开发工具、编程哲学、职业发展等,助你掌握新技术,优化技能,激发编程热情。 1015

详解 Flink 的 ProcessFunction API

相较于 map、filter 和 window 等特定的具体的操作而言,Flink 在底层 API 中提炼出一个统一通用的 process 操作,它是所有转换算子的一个概括性的表达,可以在对应的接口中自定义处理逻辑,而这一层接口就被叫作“处理函数”(ProcessFunction)

weixin_44480009的博客 1678

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Flink 如何使用ProcessFunction

1. ProcessFunction ProcessFunction 函数是低阶流处理算子,可以访问流应用程序所有(非循环)基本构建块: 事件 (数据流元素) 状态 (容错和一致性) 定时器 (事件时间和处理时间) ProcessFunction 可以被认为是一种提供了对 KeyedState 和定时器访问的 FlatMapFunction。每在输入流中接收到一个事件,就会调用来此函数来处理。...

9951

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

聊聊flink的ProcessFunction

序 本文主要研究一下flink的ProcessFunction 实例 import org.apache.flink.api.common.state.ValueState; import org.apache.flink.api.common.state.ValueStateDescriptor; import org.apache.flink.api.java.tuple.Tuple2; im...

weixin_33860737的博客 531

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Flink底层API之ProcessFunction

一、Flink转换算子是无法访问事件的时间戳和watermark,因此DataStream提供了一套底层API,用于访问事件时间戳,watermark和注册的定时事件。Flink SQL是基于ProcessFunction实现的。 Flink提供了8个ProcessFunction ProcessFunction KeyedProcessFunction CoProcessFunction...

勇心在馨 597

Flink DataStream ProcessFunction

目录 1.ProcessFunction 2.低阶Join(Low-level Joins) 3.示例 4.定时器(Timers) 5.容错(Fault Tolerance) 6.定时器合并(Timer Coalescing) 该专栏内容与 flink-notes 同步,源码与 flink-advanced 同步。 本节内容对应官方文档 1.ProcessFunction ProcessF...

2.wa 1557

Flink processFunction API

文章目录Flink processFunction API1 KeyedProcessFunction2 TimerService 和 定时器(Timers) Flink processFunction API 1 KeyedProcessFunction KeyedProcessFunction 用来操作 KeyedStream。KeyedProcessFunction 会处理流的每一个元素,输出为 0 个、1 个或者多个元素。所有的 Process Function 都继承自RichFunction 接

qq_27242695的博客 345
上一篇: Flink 状态
下一篇: Flink Table API及Flink SQL
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力生活的黄先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值