当前位置: 首页 > news >正文

Flink系列二:DataStream API中的Source,Transformation,Sink详解(^_^)

在上面篇文章中已经对flink进行了简单的介绍以及了解了Flink API 层级划分,这一章内容我们主要介绍DataStream API

 

流程图解:

 

3908739ad07d4f6b987e0e15b257e0ac.png

 

一、DataStream API Source

Flink 在流处理和批处理上的 source 大概有 4 类:

(1)基于本地集合的 source

(2)基于文件的 source

(3)基于网络套接字的 source,具体来说就是从远程服务器或本地端口上的套接字连接中接收数据,比如上一篇文章中的入门案例就属于这一种。

(4)自定义的 source。自定义的 source 常见的有 Apache kafka、Amazon Kinesis Streams、RabbitMQ、Twitter Streaming API、Apache NiFi 等,当然你也可以定义自己的 source,灵活度较高,看个人需求。

 

下面就是纯代码演示了,具体细节会在注释中说明

1、本地集合的source

import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;import java.util.ArrayList;public class Demo1ListSource {public static void main(String[] args) throws Exception{//创建flink执行环境StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//创建集合ArrayList<String> arrayList = new ArrayList<>();arrayList.add("java");arrayList.add("java");arrayList.add("java");arrayList.add("java");arrayList.add("java");/**基于集合的Source ----- 属于有界流*/DataStream<String> listDS = env.fromCollection(arrayList);listDS.print();//启动Flink作业执行env.execute();}
}

结果:

16787a01a3a847538ef32dcecc29f79f.png

在这解释一下结果图中的数字前缀,这个前缀的主要目的是不同并行实例的输出。什么都不设置的话取决于你电脑的内存了,比如我电脑是16G的内存,那么当数据较多时默认分配给该作业分了16个task。

 

2、本地文件的source

注意:同一个File数据源,既能有界读取,也能无界读取

2.1 有界读取

/**流批统一:* 1、同一套算子代码既能作流处理也能做批处理* 2、同一个File数据源,既能有界读取,也能无界读取*/
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.connector.file.src.FileSource;
import org.apache.flink.connector.file.src.reader.TextLineInputFormat;
import org.apache.flink.core.fs.Path;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;public class Demo2FileSource1 {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();/**有界读取*///老版本方式:简单但不灵活DataStream<String> lineDS = env.readTextFile("flink/data/student.csv");
//        lineDS.print();//新版本方式:复杂一点但更灵活,使用这种既能有界读取,也能无界读取//构建fileSourceFileSource<String> fileSource = FileSource.forRecordStreamFormat(//指定编码new TextLineInputFormat("UTF-8")//指定路径, new Path("flink/data/student.csv")).build();//使用fileSourceDataStream<String> fileDS = env.fromSource(fileSource, WatermarkStrategy.noWatermarks(), "fileSource");fileDS.print();env.execute();}
}

 

2.1 无界读取

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.connector.file.src.FileSource;
import org.apache.flink.connector.file.src.reader.TextLineInputFormat;
import org.apache.flink.core.fs.Path;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;public class Demo2FileSource2 {public static void main(String[] args) throws Exception {/**使用无界流读取文件数,很简单,其实就是对上面的代码修改运行模式并加个参数就可以了*/StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//修改运行模式env.setRuntimeMode(RuntimeExecutionMode.STREAMING);//构建fileSourceFileSource<String> fileSource = FileSource.forRecordStreamFormat(new TextLineInputFormat("UTF-8"),new Path("spark/data/student.csv")).build();//使用fileSourceDataStreamSource<String> linesDS = env.fromSource(fileSource, WatermarkStrategy.noWatermarks(), "fileSource");linesDS.print();env.execute();}
}

 

3、本地端口的source

上一篇文章中的入门案例就属于这一种,后面在代码中也会用到,在此不在赘述了。

 

4、自定义的 source

举例:使用自定义source读取mysql中的数据

/*实现方式:* 1、实现SourceFunction或ParallelSourceFunction接口来创建自定义的数据源。* 2、然后使用env.addSource(new CustomSourceFunction())或DataStreamSource.fromSource添加你自定义的数据源。*/
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.SourceFunction;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;public class Demo3MysqlSource {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//使用自定义的sourceDataStream<Student> studentDSSource = env.addSource(new MysqlSource());//统计学生表每个班级的人数//取出每一行的班级列并加上人数后缀1DataStream<Tuple2<String, Integer>> clazzKvDS = studentDSSource.map(line -> Tuple2.of(line.getClazz(), 1), Types.TUPLE(Types.STRING, Types.INT));//分组,将相同的键发送给同一个task中KeyedStream<Tuple2<String, Integer>, String> keyByDS = clazzKvDS.keyBy(kv -> kv.f0);//求和SingleOutputStreamOperator<Tuple2<String, Integer>> clazzSum = keyByDS.sum(1);//输出clazzSum.print();env.execute();}}/*** 自定义source读取mysql中的数据*/
class MysqlSource implements SourceFunction<Student> {/*** run()方法会在任务启动的时候执行一次*/@Overridepublic void run(SourceContext ctx) throws Exception {//1、加载mysq驱动Class.forName("com.mysql.jdbc.Driver");//2、创建数据库连接//注意:如果报连不上的错误,将参数补全(useUnicode=true&allowPublicKeyRetrieval=true&characterEncoding=utf8&useSSL=false)Connection conn = DriverManager.getConnection("jdbc:mysql://master:3306/bigdata29?useSSL=false", "root", "123456");//3、编写sql查询PreparedStatement sql = conn.prepareStatement("select * from students");//4、执行查询ResultSet resultSet = sql.executeQuery();//5、遍历查询出的数据while (resultSet.next()) {int id = resultSet.getInt("id");String name = resultSet.getString("name");int age = resultSet.getInt("age");String gender = resultSet.getString("gender");String clazz = resultSet.getString("clazz");//将数据发送到下游/** collect():从 DataStream 收集所有的元素,并将它们作为列表或其他集合类型返回给客户端*/ctx.collect(new Student(id, name, age, gender, clazz));}//6、释放资源sql.close();conn.close();}@Overridepublic void cancel() {/** cancel(),它用于在任务完成后执行清理操作*/}
}/*** 这里使用了lombok插件(小辣椒)* 这个插件的作用可以在代码编译的时候增加方法(相当于scala中的case class),就不用我们自己手动添加get、set、toString等方法了。* 使用方法:加@就行了*/
@Data
@AllArgsConstructor
class Student {private int id;private String name;private int age;private String gender;private String clazz;
}

 

 

二、DataStream API Transformation

Transformation:数据流转换。

常见算子有 Map / FlatMap / Filter /KeyBy / Reduce / Fold / Aggregations / Window / WindowAll / Union / Window join / Split / Select / Project 等,操作很多,可以将数据转换计算成你想要的数据形式。

其实这些算子在功能上与scala或spark中的基本相同,只是形式和细节上会有些差别。

1、map

DataStream → DataStream    输入一个元素同时输出一个元素


import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;public class Demo1Map {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();//使用nc -lk 8888 模拟实时数据的产生DataStreamSource<String> source = env.socketTextStream("master", 8888);//方式1:匿名内部类形式/** 观察源码map发现:* MapFunction<T, O> 是一个函数接口,用于对流中的每个元素的处理* 这个接口定义了一个 map 方法,该方法接受一个输入元素(类型为 T)并返回一个输出元素(类型为 O)。*/DataStream<String> map1DS = source.map(new MapFunction<String, String>() {@Overridepublic String map(String word) throws Exception {return word.toUpperCase();}});
//        map1DS.print();//方式2:lambda表达式形式(更简洁常用)source.map(String::toUpperCase).print();    //是对source.map(word -> word.toUpperCase())的更简写env.execute();}
}

结果:

45374801cb354dccbc098f73af160b18.png

 

 

2、flatMap

DataStream → DataStream

输入一个元素转换为一个或多个元素输出

/**flatMap 方法用于将输入流中的每个元素转换成一个或多个输出元素*/import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;public class Demo2FaltMap {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();DataStream<String> source = env.socketTextStream("master", 8888);//方式1:匿名内部类//看源码,这个方法接受一个FlatMapFunction<T, R>类型的参数,其中T是输入元素的类型,R是输出元素的类型DataStream<String> out2DS = source.flatMap(new FlatMapFunction<String, String>() {@Overridepublic void flatMap(String line, Collector<String> out) throws Exception {for (String word : line.split(",")) {//循环将数据发送到下游out.collect(word);}}});//        out2DS.print();//方式2:lambda表达式DataStream<String> out1DS = source.flatMap((line, out) -> {for (String word : line.split(",")) {//循环将数据发送到下游out.collect(word);}}, Types.STRING);out1DS.print();env.execute();}
}

结果:

c82a409f9f824b6eba5ee6263c064070.png

 

3、filter

DataStream → DataStream 

为每个元素执行一个布尔 function,并保留那些 function 输出值为 true 的元素

import org.apache.flink.api.common.functions.FilterFunction;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;public class Demo3Filter {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();DataStream<String> source = env.readTextFile("spark/data/student.csv");//需求:过滤出文科一班的学生的信息//方式一:匿名内部类source.filter(new FilterFunction<String>() {@Overridepublic boolean filter(String lines) throws Exception {return "文科一班".equals(lines.split(",")[4]);}}); //.print();//方式2:lambda表达式source.filter(lines->"文科一班".equals(lines.split(",")[4])).print();env.execute();}
}

结果:

d3e93c6a422c4b5abbd8ce6822799a0d.png

 

4、keyBy

作用为:分组

DataStream → KeyedStream

在逻辑层面将流划分为不相交的分区。具有相同 key 的记录都分配到同一个分区。在内部, keyBy() 是通过哈希分区实现的。有多种指定 key 的方式。

39272837e4da48c3b64487f8a16d2766.png

import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;public class Demo4KeyBy {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();DataStreamSource<String> source = env.socketTextStream("master", 8888);//方式1:匿名内部类/** public <K> KeyedStream<T, K> keyBy(KeySelector<T, K> key)* 其中 T 是输入元素的类型,K 是键的类型*/source.map(word-> Tuple2.of(word,1), Types.TUPLE(Types.STRING,Types.INT)).keyBy(new KeySelector<Tuple2<String, Integer>, String>() {@Overridepublic String getKey(Tuple2<String, Integer> kv) throws Exception {return kv.f0;}});//.print();//方式2:lambda表达式source.map(word-> Tuple2.of(word,1), Types.TUPLE(Types.STRING,Types.INT)).keyBy(kv->kv.f0).print();env.execute();}
}

结果: 可以看出的确作了分区

48db7796907641b9b7c8480b6561ab4b.png

 

5、reduce

作用为:聚合

KeyedStream → DataStream

在相同 key 的数据流上“滚动”执行 reduce。将当前元素与最后一次 reduce 得到的值组合然后输出新值。

import org.apache.flink.api.common.functions.ReduceFunction;
import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;public class Demo5Reduce {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();DataStream<String> source = env.socketTextStream("master", 8888);//方式1:匿名内部类source.map(word -> Tuple2.of(word, 1), Types.TUPLE(Types.STRING, Types.INT)).keyBy(kv -> kv.f0).reduce(new ReduceFunction<Tuple2<String, Integer>>() {@Overridepublic Tuple2<String, Integer> reduce(Tuple2<String, Integer> kv1,Tuple2<String, Integer> kv2) throws Exception {//kv1和kv2的key是一样的String word = kv1.f0;int counts = kv1.f1 + kv2.f1;return Tuple2.of(word,counts);}}).print();env.execute();}
}

结果:从结果来看说明reduce是一个有状态算子。 

6003735a7c804c60b0a2652d4f664ee0.png

 

6、Window

KeyedStream → WindowedStream 

可以在已经分区的 KeyedStreams 上定义 Window,Window 根据某些特征(例如,最近 5 秒内到达的数据)对每个 key Stream 中的数据进行分组。

窗口算子有很多,以后会专门出一章具体说明,下面写一个滑动窗口的案例。

import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.*;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.SlidingProcessingTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;public class Demo6Window {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();/** 每隔5秒统计最近15秒每个单词的数量 --- 滑动窗口*/DataStream<String> wordsDS = env.socketTextStream("master", 8888);//转换成kvDataStream<Tuple2<String, Integer>> kvDS = wordsDS.map(word -> Tuple2.of(word, 1), Types.TUPLE(Types.STRING, Types.INT));//按照单词分组KeyedStream<Tuple2<String, Integer>, String> keyByDS = kvDS.keyBy(kv -> kv.f0);//划分窗口//SlidingEventTimeWindows:滑动的处理时间窗口//前一个参数为窗口大小(window size),后一个参数为滑动大小(window slide)WindowedStream<Tuple2<String, Integer>, String, TimeWindow> windowDS = keyByDS.window(SlidingProcessingTimeWindows.of(Time.seconds(30), Time.seconds(10)));//统计单词的数量DataStream<Tuple2<String, Integer>> countDS = windowDS.sum(1);countDS.print();env.execute();}
}

 

7、Union

DataStream→ DataStream

将两个或多个数据流联合来创建一个包含所有流中数据的新流。注意:如果一个数据流和自身进行联合,这个流中的每个数据将在合并后的流中出现两次。

import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;public class Demo7Union {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();DataStream<String> source1 = env.socketTextStream("master", 8888);DataStream<String> source2 = env.socketTextStream("master", 9999);/** 合并两个DataStream* 注意:在数据层面并没有合并,只是在逻辑层面合并了*/DataStream<String> unionDS = source1.union(source2);unionDS.print();env.execute();}
}

结果:

5e95fe6957a1463abe7dde2dce4af9d9.png

 

8、process

DataStream→ DataStream

process算子是flink的底层算子,可以用来代替map、faltMap、filter等算子

import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.ProcessFunction;
import org.apache.flink.util.Collector;public class Demo8Process {public static void main(String[] args) throws Exception {/** process算子是flink的底层算子,可以用来代替map、faltMap、filter等算子** public <R> SingleOutputStreamOperator<R> process(ProcessFunction<T, R> processFunction)* 其中 T 是输入数据的类型,R 是输出数据的类型*/StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();DataStream<String> source = env.socketTextStream("master", 8888);DataStream<Tuple2<String, Integer>> processDS = source.process(new ProcessFunction<String, Tuple2<String, Integer>>() {/** processElement:在当前代码中相当于flatMap,每一条数据执行一次,可以返回一条或多条数据* ctx:上下文对象(代表flink执行环境)* out:输出,用于将数据发送到下游*/@Overridepublic void processElement(String line, ProcessFunction<String, Tuple2<String, Integer>>.Context ctx,Collector<Tuple2<String, Integer>> out) throws Exception {//这里的逻辑与flatMap的逻辑相同for (String word : line.split(",")) {out.collect(Tuple2.of(word, 1));}}});env.execute();/** 注意:该算子不能用lambda表达式改写,因为ProcessFunction它包含了一些生命周期方法和状态管理的方法,* 这些方法使得它不适合直接简化为lambda表达式的形式。** 在底层代码层面来说,ProcessFunction是一个抽象类,该类还有许多复杂的方法,使得它无法直接用lambda表达式来改写* 因为 lambda 表达式只能表示简单的函数接口(即那些只包含一个抽象方法的接口)* public abstract class ProcessFunction<I, O> extends AbstractRichFunction*/}
}

 

三、DataStream API Sink

Flink 将转换计算后的数据发送的地点 。

Flink 常见的 Sink 大概有如下几类:

(1)打印在控制台、写入文件。

(2)写入 socket(具体指的是将数据发送到网络套接字(例如端口))。

(3)自定义的 sink :常见的有 Apache kafka、RabbitMQ、MySQL、ElasticSearch、Apache Cassandra、Hadoop FileSystem 等,当然你也可以根据需求定义自己的 sink。

 

1、写入文件

对于写入文件,是否要将所有数据写入同一个文件?由于是流式写入,该文件就一直处于正在写入的状态,而且可能会造成文件过大的问题,所以DataStream API提供了滚动策略的方式来解决这样的问题。

9a5ed8bc0f1c42a4a16f59c8bcf80165.png

 


import org.apache.flink.api.common.serialization.SimpleStringEncoder;
import org.apache.flink.configuration.MemorySize;
import org.apache.flink.connector.file.sink.FileSink;
import org.apache.flink.core.fs.Path;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.sink.filesystem.rollingpolicies.DefaultRollingPolicy;
import java.time.Duration;public class Demo1FileSink {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();DataStream<String> source = env.socketTextStream("master", 8888);//创建fileSink/**public static <IN> DefaultRowFormatBuilder<IN> forRowFormat(*        final Path basePath, final Encoder<IN> encoder)}**<IN> : The type of the elements that are being written by the sink.*/FileSink<String> fileSink = FileSink.forRowFormat(new Path("flink/data/words"), new SimpleStringEncoder<String>("UTF-8")).withRollingPolicy(DefaultRollingPolicy.builder()//每10秒进行一次滚动(生成文件).withRolloverInterval(Duration.ofSeconds(10))//当延迟超过10秒进行一次滚动.withInactivityInterval(Duration.ofSeconds(5))//文件大小达到1MB进行一次滚动.withMaxPartSize(MemorySize.ofMebiBytes(1)).build()).build();//使用fileSink,将读取的数据写入另一到文件夹中source.sinkTo(fileSink);env.execute();}
}

结果: 

d9cfbc1a28b94ad79646683db279d104.png

 

2、自定义的 sink

举例:使用自定义sink将数据存到mysql中

import org.apache.flink.api.common.typeinfo.Types;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStream;
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.sink.RichSinkFunction;
import org.apache.flink.streaming.api.functions.sink.SinkFunction;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;public class Demo3MySqlSInk {public static void main(String[] args) throws Exception {StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();DataStream<String> wordsDS = env.socketTextStream("master", 8888);//统计单词的数量DataStream<Tuple2<String, Integer>> countDS = wordsDS.map(word -> Tuple2.of(word, 1), Types.TUPLE(Types.STRING, Types.INT)).keyBy(kv -> kv.f0).sum(1);//将统计结果保存到数据countDS.addSink(new MySQlSink());env.execute();}
}/*** 自定义sink将数据保存到mysql* RichSinkFunction:多了open和close方法,用于打开和关闭连接* SinkFunction*/
class MySQlSink extends RichSinkFunction<Tuple2<String, Integer>> {Connection con;PreparedStatement stat;/*** invoke方法每一条数据执行一次*/@Overridepublic void invoke(Tuple2<String, Integer> kv, Context context) throws Exception {stat.setString(1, kv.f0);stat.setInt(2, kv.f1);//执行sqlstat.execute();}/*** open方法会在任务启动的时候,每一个task中执行一次*/@Overridepublic void open(Configuration parameters) throws Exception {System.out.println("创建数据库连接");//1、加载启动Class.forName("com.mysql.jdbc.Driver");//2、创建数据库连接con = DriverManager.getConnection("jdbc:mysql://master:3306/bigdata29", "root", "123456");//3、编写保存数据的sql//replace into 替换插入,如果没有就插入,如果有就更新,表需要有主键stat = con.prepareStatement("replace into word_count values(?,?)");}/*** close方法会在任务取消的时候,每一个task中执行一次*/@Overridepublic void close() throws Exception {//4、关闭数据库连接stat.close();con.close();}

 

 

 

 

 

---------------------------------------------------------------------------------------------------------------------------------

代码注意提示:

如果在写flink代码的过程中出现了以下错误,大概率就是有些算子使用没有写数据类型,与spark不同,spaark底层由scala编写,scala提供了自动类型推断机制,所以不写参数类型也不会报错,但是flink底层是java编写的,java没有这种机制。

23ddb4c8d57549598c2cdb15b640badf.png

 

 

基础的算子到这结束,其他算子后续也会写,以上内容具体详情皆参考apache flink官网,官网详细说明了各种算子的使用,网址贴在下面了:

https://nightlies.apache.org/flink/flink-docs-release-1.19/zh/docs/dev/datastream/operators/overview/

个人感觉写的很详细了,看不懂建议直接打死作者(^_^)

 

相关文章:

Flink系列二:DataStream API中的Source,Transformation,Sink详解(^_^)

在上面篇文章中已经对flink进行了简单的介绍以及了解了Flink API 层级划分&#xff0c;这一章内容我们主要介绍DataStream API 流程图解&#xff1a; 一、DataStream API Source Flink 在流处理和批处理上的 source 大概有 4 类&#xff1a; &#xff08;1&#xff09;基于本…...

最好的电脑数据恢复软件是什么

由于硬件故障、恶意软件攻击或意外删除而丢失文件可能会造成巨大压力。数据丢失会扰乱日常运营&#xff0c;造成宝贵的业务时间和资源损失。在这些情况下&#xff0c;数据恢复软件是检索丢失或损坏数据的最简单方法。 数据恢复软件何时起作用&#xff1f; 对于 Windows 数据恢…...

机器学习模型调试学习总结

1.学习内容 模型调试方法&#xff1a;冻结部分层&#xff0c;训练剩余层 实践&#xff1a;在一个预训练的 BERT 模型上冻结部分层&#xff0c;并训练剩余的层 模型调试方法&#xff1a;线性探测&#xff08;Linear Probe&#xff09; 实践&#xff1a;在一个预训练的 BERT …...

文明互鉴促发展——2024“国际山地旅游日”主题活动在法国启幕

5月29日&#xff0c;2024“国际山地旅游日”主题活动在法国尼斯市成功举办。中国驻法国使领馆、法国文化旅游部门、地方政府、国际组织、国际山地旅游联盟会员代表、旅游机构、企业、专家、媒体等围绕“文明互鉴的山地旅游”大会主题和“气候变化与山地旅游应对之策”论坛主题展…...

【C++进阶】深入STL之string:掌握高效字符串处理的关键

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;C模板入门 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀STL之string &#x1f4d2;1. STL基本…...

一、初识Qt 之 Hello world

一、初识Qt 之 Hello world 提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 初识Qt 之 Hello world文章目录 一、Qt 简介二、Qt 获取安装三、Qt 初步使用四、Qt 之 Hello world1.新建一个项目 总结 一、Qt 简介 C &#xf…...

nginx搭建简单负载均衡demo(springboot)

目录 1 安装nignx 1.1 执行 brew install nginx 命令&#xff08;如果没安装brew可百度搜索如何安装brew下载工具。类似linux的yum命令工具&#xff09;。 1.2 安装完成会有如下提示&#xff1a;可以查看nginx的配置文件目录。 1.3 执行 brew services start nginx 命令启动…...

SpringBoot的第二大核心AOP系统梳理

目录 1 事务管理 1.1 事务 1.2 Transactional注解 1.2.1 rollbackFor 1.2.2 propagation 2 AOP 基础 2.1 AOP入门 2.2 AOP核心概念 3. AOP进阶 3.1 通知类型 3.2 通知顺序 3.3 切入点表达式 execution切入点表达式 annotion注解 3.4 连接点 1 事务管理 1.1 事务…...

react、vue动态form表单

需求在日常开发中反复写form 是一种低效的开发效率&#xff0c;布局而且还不同这就需要我们对其封装 为了简单明了看懂代码&#xff0c;我这里没有组件&#xff0c;都放在一起&#xff0c;简单抽离相信作为大佬的你&#xff0c;可以自己完成&#xff0c; 一、首先我们做动态f…...

halcon程序如何导出C#文件

1.打开halcon文件&#xff1b; 2.写好需要生成C#文件的算子或函数&#xff1b; 3.找到档案-输出&#xff0c;如下图&#xff1b; 4.点击输出&#xff0c;弹出如下窗口 &#xff08;1&#xff09;可以修改导出文件的存储路径 &#xff08;2&#xff09;选择C#-HALCON/.NET &…...

RabbitMQ三、springboot整合rabbitmq(消息可靠性、高级特性)

一、springboot整合RabbitMQ&#xff08;jdk17&#xff09;&#xff08;创建两个项目&#xff0c;一个生产者项目&#xff0c;一个消费者项目&#xff09; 上面使用原生JAVA操作RabbitMQ较为繁琐&#xff0c;很多的代码都是重复书写的&#xff0c;使用springboot可以简化代码的…...

第八十九周周报

学习目标&#xff1a; 论文 学习时间&#xff1a; 2024.05.25-2024.05.31 学习产出&#xff1a; 一、论文 SAN: INDUCING METRIZABILITY OF GAN WITH DISCRIMINATIVE NORMALIZED LINEAR LAYER 将GAN与切片最优输运联系起来&#xff0c;提出满足方向最优性、可分离性和单射…...

Centos升级Openssh版本至openssh-9.3p2

一、启动Telnet服务 为防止升级Openssh失败导致无法连接ssh&#xff0c;所以先安装Telnet服务备用 1.安装telnet-server及telnet服务 yum install -y telnet-server* telnet 2.安装xinetd服务 yum install -y xinetd 3.启动xinetd及telnet并做开机自启动 systemctl enable…...

茉莉香飘,奶茶丝滑——周末悠闲时光的绝佳伴侣

周末的时光总是格外珍贵&#xff0c;忙碌了一周的我们&#xff0c;终于迎来了难得的闲暇。这时&#xff0c;打开喜欢的综艺&#xff0c;窝在舒适的沙发里&#xff0c;再冲泡一杯香飘飘茉莉味奶茶&#xff0c;一边沉浸在剧情的海洋中&#xff0c;一边品味着香浓丝滑的奶茶&#…...

揭秘:Java字符串对象的内存分布原理

先来看看下面寄到关于String的真实面试题&#xff0c;看看你废不废&#xff1f; String str1 "Hello"; String str2 "Hello"; String str3 new String("Hello"); String str4 new String("Hello");System.out.println(str1 str2)…...

Vue.js - 生命周期与工程化开发【0基础向 Vue 基础学习】

文章目录 Vue 的生命周期Vue 生命周期的四个阶段Vue 生命周期函数&#xff08;钩子函数 工程化开发 & 脚手架 Vue CLI**开发 Vue 的两种方式&#xff1a;**脚手架目录文件介绍项目运行流程组件化开发 & 根组件App.vue 文件&#xff08;单文件组件&#xff09;的三个组成…...

Element-UI 快速入门指南

Element-UI 快速入门指南 Element-UI 是一套基于 Vue.js 的桌面端组件库,由饿了么前端团队开发和维护。它提供了丰富的 UI 组件,帮助开发者快速构建美观、响应式的用户界面。本篇文章将详细介绍 Element-UI 的安装、配置和常用组件的使用方法,帮助你快速上手并应用于实际项…...

2024华为OD机试真题-整型数组按个位值排序-C++(C卷D卷)

题目描述 给定一个非空数组(列表),其元素数据类型为整型,请按照数组元素十进制最低位从小到大进行排序, 十进制最低位相同的元素,相对位置保持不变。 当数组元素为负值时,十进制最低位等同于去除符号位后对应十进制值最低位。 输入描述 给定一个非空数组,其元素数据类型…...

善听提醒遵循易经原则。世界大同只此一路。

如果说前路是一个大深坑&#xff0c;那必然是你之前做的事情做的不太好&#xff0c;当坏的时候&#xff0c;坏的结果来的时候&#xff0c;是因为你之前的行为&#xff0c;你也就不会再纠结了&#xff0c;会如何走出这个困境&#xff0c;是好的来了&#xff0c;不骄不躁&#xf…...

CrossOver有些软件安装不了 用CrossOver安装软件后如何运行

CrossOver为用户提供了三种下载软件的方式分别是&#xff1a;搜索、查找分类、导入。如果【搜索】和【查找分类】提供的安装资源不能成功安装软件&#xff0c;那么我们可以通过多种渠道下载安装包&#xff0c;并将安装包以导入的方式进行安装。这里我们以QQ游戏为例&#xff0c…...

在vue中如何使用leaflet图层展示地图

在vue中如何使用leaflet <template><div id"map" class"map"></div> </template><script> export default {data () {return {};},mounted(){this.initMaps()},methods: {initMaps () {const map L.map(map, {zoomControl…...

mybatisplus 字段存的是json 在查询的时候怎么映射成对象

数据库交互对象 TableName(value "表名", autoResultMap true)TableField(typeHandler JacksonTypeHandler.class, value "user_info")private User user;autoResultMap 是一个 MyBatis-Plus 中的注解属性&#xff0c;用于控制是否自动生成结果映射。…...

Python 学习笔记【1】

此笔记仅适用于有任一编程语言基础&#xff0c;且对面向对象有一定了解者观看 文章目录 数据类型字面量数字类型数据容器字符串列表元组 type()方法数据类型强转 注释单行注释多行注释 输出基本输出连续输出&#xff0c;中间用“,”分隔更复杂的输出格式 变量定义del方法 标识符…...

Git系列:rev-parse 使用技巧

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

【Java数据结构】详解LinkedList与链表(一)

&#x1f512;文章目录&#xff1a; 1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; 2.ArrayList的缺陷 3.链表的概念及结构 4.无头单向非循环链表的实现 4.1成员属性 4.2成员方法 createList display——打印链表 addFirst——头插 addLast…...

PDF高效编辑器革新:一键智能转换PDF至HTML,轻松开启文件处理全新时代!

信息爆炸的时代&#xff0c;PDF文件因其跨平台、不易修改的特性&#xff0c;成为了商务、教育、出版等领域不可或缺的文件格式。然而&#xff0c;PDF文件的固定性也带来了诸多不便&#xff0c;特别是在需要对其内容进行编辑或格式转换时。这时&#xff0c;一款高效、易用的PDF编…...

JDBC知识

JDBC是什么? 这工作中我们针对数据库的操作,实际上很少会用到SQL语句,通过命令行/图形化来操作数据库,更多的是通过主流的编程语言来对数据库进行操作,即使通过代码来操作数据,我们还是会使用到SQL语句,所以掌握SQL语句也是很重要的. 如何通过代码操作数据库? 通过代码操作…...

C++操纵符用法

C中的操纵符&#xff08;Manipulators&#xff09;是用于格式化输入输出的特殊工具。它们可以在输出流中控制各种格式&#xff0c;如设置字段宽度、精度、填充字符等。以下是一些常用的操纵符及其用法&#xff1a; setw(int width): 设置字段宽度为width个字符。 cout <<…...

【一步一步了解Java系列】:子类继承以及代码块的初始化

看到这句话的时候证明&#xff1a;此刻你我都在努力 加油陌生人 个人主页&#xff1a;Gu Gu Study专栏&#xff1a;一步一步了解Java 喜欢的一句话&#xff1a; 常常会回顾努力的自己&#xff0c;所以要为自己的努力留下足迹 喜欢的话可以点个赞谢谢了。 作者&#xff1a;小闭 …...

探索Expect Python用法:深入解析与实战挑战

探索Expect Python用法&#xff1a;深入解析与实战挑战 在自动化和脚本编写领域&#xff0c;Expect Python已经成为了一种强大的工具组合。它结合了Expect的交互式会话处理能力和Python的编程灵活性&#xff0c;为开发者提供了一种全新的方式来处理复杂的自动化任务。然而&…...

【PostgreSQL17新特性之-explain命令新增选项】

EXPLAIN是一个用于显示语句执行计划的命令&#xff0c;可用于显示以下语句类型之一的执行计划&#xff1a; - SELECT - INSERT - UPDATE - DELETE - VALUES - EXECUTE - DECLARE - CREATE TABLE AS - CREATE MATERIALIZED VIEWPostgreSQL17-beta1版本近日发布了&#xff0c;新…...

JAVA实现人工智能,采用框架SpringAI

文章目录 JAVA实现人工智能,采用框架SpringAISpring AI介绍使用介绍项目前提项目结构第一种方式采用openai1. pom文件&#xff1a; 2. application.yml 配置3.controller 实现层 项目测试 JAVA实现人工智能,采用框架SpringAI Spring AI介绍 Spring AI是AI工程师的一个应用框架…...

基础—SQL—DQL(数据查询语言)分组查询

一、引言 分组查询的关键字是&#xff1a;GROUP BY。 二、DQL—分组查询 1、语法 SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后过滤条件 ]; 注意&#xff1a; 1、[ ] 里的内容可以有可以没有。 2、这条SQL语句有两块指定条件的地方&#…...

从CSV到数据库(简易)

需求&#xff1a;客户上传CSV文档&#xff0c;要求CSV文档内容查重/插入/更新相关数据。 框架&#xff1a;jdbcTemplate、commons-io、 DB&#xff1a;oracle 相关依赖&#xff1a; 这里本来打算用的2.11.0&#xff0c;无奈正式项目那边用老版本1.3.1&#xff0c;新版本对类型…...

K210视觉识别模块学习笔记3:内存卡写入拍摄图片_LED三色灯的操作_按键操作_定时器的配置使用

今日开始学习K210视觉识别模块: LED三色灯的操作_按键操作_定时器的配置使用_内存卡写入拍摄图片 亚博智能的K210视觉识别模块...... 固件库版本: canmv_yahboom_v2.1.1.bin 本文最终目的是编写一个按键拍照的例程序&#xff1a; 为以后的专用场景的模型训练做准备&#xf…...

如何定义“智慧校园”这个概念

在信息爆炸的时代&#xff0c;教育面临着前所未有的挑战&#xff1a;如何让每个学生在海量知识中找到属于自己的路径&#xff1f;如何让教师的智慧与科技的力量相得益彰&#xff1f;如何让校园成为培养创新思维的摇篮&#xff1f;智慧校园&#xff0c;这一概念的提出&#xff0…...

OpenSSL自签名证书

文章目录 生成1. 生成根证书的私钥&#xff08;root_private_key.pem&#xff09;2. 创建根证书的CSR和自签名证书&#xff08;root_csr.pem&#xff09;3. 生成服务器证书的私钥&#xff08;server_private_key.pem&#xff09;4. 创建服务器证书的CSR&#xff08;server_priv…...

QtCreator调试运行工程报错,无法找到相关库的的解决方案

最新在使用国产化平台做qt应用开发时&#xff0c;总是遇到qtcreator内调试运行 找不到动态库的问题&#xff0c;为什么会出现这种问题呢&#xff1f;明明编译的时候能够正常通过&#xff0c;运行或者调试的时候找不到相关的库呢&#xff1f;先说结论&#xff0c;排除库本身的问…...

【Python系列】Python 元组(Tuple)详解

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

特征融合篇 | YOLOv8 引入动态上采样模块 | 超过了其他上采样器

1. 介绍 本篇介绍了一种将动态上采样模块引入 YOLOv8 目标检测算法的新方法&#xff0c;该方法在 COCO 数据集上获得了 55.7% 的 mAP&#xff0c;超越了其他上采样器。该方法将动态上采样模块引入到 YOLOv8 的特征融合阶段&#xff0c;能够根据输入图像的特征分辨率动态调整上…...

​​​​​​​Beyond Compare 3密钥被撤销的解决办法

首先&#xff0c;BCompare3的链接如下 链接&#xff1a;https://pan.baidu.com/s/1vuSxY0cVQCt0-8CpFzUhvg 提取码&#xff1a;8888 --来自百度网盘超级会员V7的分享 1.问题现象 激活之后在使用过程中有时候会出现密钥被撤销的警告&#xff0c;而且该工具无法使用&#xff…...

知识见闻 - 人和动物的主要区别

人类和动物的主要区别之一确实在于理性&#xff0c;但这只是众多区别中的一个方面。以下是一些更全面的比较&#xff0c;突出人类和动物之间的主要区别&#xff1a; 理性和抽象思维&#xff1a; 人类&#xff1a;人类具有高度发展的理性能力&#xff0c;可以进行抽象思维、逻辑…...

Javaweb基础之工程路径

大家好&#xff0c;这里是教授.F 引入&#xff1a; 工程路径有一个知识点需要注意&#xff1a;就是相对路径。所谓相对路径就是依赖当前位置&#xff1a; 相对路径的定位依赖于当前位置或参考位置。 使用相对路径来解决&#xff0c; 一个非常重要的规则&#xff1a;页面所有的…...

国际荐酒师(香港)协会受邀出席广州意大利国庆晚宴

2024年5月30日&#xff0c;意大利驻广州总领事馆举办的2024年意大利国庆招待会及晚宴&#xff0c;庆祝意大利共和国成立。此次晚宴旨在促进中意两国之间的文化交流与合作。国际荐酒师&#xff08;香港&#xff09;协会受主办方邀请参与了这一重要活动。 国际荐酒师&#xff08;…...

让驰骋BPM系统插上AI的翅膀

让驰骋BPM系统插上AI的翅膀 在当今日益复杂多变的商业环境中&#xff0c;业务流程管理&#xff08;BPM&#xff09;系统的应用愈发广泛&#xff0c;成为企业提高效率、优化流程、降低成本的重要工具。驰骋BPM系统凭借其出色的性能和丰富的功能&#xff0c;赢得了众多企业的青睐…...

排队论 | 基于排队机制实现智能仓储机器人巡逻及避碰

研究背景: 智能仓储机器人在现代物流行业中扮演着重要的角色,能够提高仓库的运作效率和准确性。然而,仓储机器人在巡逻过程中可能会遇到其他机器人或障碍物,这就需要解决排队和避碰问题,以确保安全和高效的运作。 研究路线: 背景调研:了解智能仓储机器人的发展和应用…...

Node.js和npm常用命令

一、Node.js简介 Node.js是一个免费、开源、跨平台的JavaScript运行时环境&#xff0c;允许开发人员创建服务器、web应用程序、命令行工具和脚本。 点击查看node.js中文官网 点击查看node.js英文官网 二、npm简介 npm(Node Package Manager)是Node.js的软件包管理器&#xff0…...

pytest +allure在测试中的应用

一、allure配置&#xff1a; 1、安装allure库 pip install allure-pytest2、代码中导入 import allure3、常用命令&#xff1a; 1)、 pytest --alluredir报告目录 测试脚本.py比如&#xff1a;pytest --alluredir./allure_report &#xff08;未指定执行所有&#xff09; 2&…...

004 CentOS 7.9 mongodb7.0.11安装及配置

https://www.mongodb.com/try/download/shell https://www.mongodb.com/try/download/community 文章目录 /etc/mongod.conf在 /etc/systemd/system/ 目录下创建一个名为 mongod.service 的文件重新加载 systemd 配置&#xff1a;启用服务&#xff1a;现在&#xff0c;可以手动…...

Docker安装Redis(云服务器)

准备&#xff1a; 在云服务器中开启6370端口号 docker run -d --name redis -p 6379:6379 redis 这条命令使用docker运行一个名为"redis"的容器&#xff0c;映射容器的6379端口到主机的6379端口&#xff0c;并且使用redis镜像来运行容器。REDIS是一个开源的内存数据…...