flink快速上手

本文详细介绍了如何使用Docker构建Flink1.18.1运行环境,包括启动jobmanager和taskmanager节点,以及如何配置网络、内存和并行度。还涵盖了Maven构建项目、数据源和转换、自定义函数、窗口和触发器等核心Flink操作。
摘要由CSDN通过智能技术生成

此处使用docker构建运行环境,当前flink最新版本为1.18.1。若有问题尝试使用指定版本sudo docker pull flink:1.18

//创建网络
sudo docker network create flink-network
//启动主节点
sudo docker run -idt --name=jobmanager -e TZ=Asia/Shanghai --network flink-network -p 8081:8081 --env FLINK_PROPERTIES="jobmanager.rpc.address: jobmanager" flink jobmanager

查看主节点ip

ubuntu@ubuntu:~$ sudo docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(sudo docker ps -aq)
/jobmanager - 172.18.0.2

启动子节点,可启动多个,改name就行,hosts的ip填入上述获取的ip

sudo docker run -idt --name=taskmanager1 -e TZ=Asia/Shanghai --network flink-network --env FLINK_PROPERTIES="jobmanager.rpc.address: jobmanager" --add-host jobmanager:172.18.0.2 flink taskmanager

访问8081,此处启动了两个task
在这里插入图片描述
其他常用的配置属性

jobmanager.memory.process.size:JobManager进程可使用到的全部内存
taskmanager.memory.process.size:TaskManager进程可使用到的全部内存
taskmanager.numberOfTaskSlots:TaskManager能够分配的Slot数量,默认为1
parallelism.default:Flink任务执行的并行度,默认为1

maven构建项目

mvn archetype:generate                \
  -DarchetypeGroupId=org.apache.flink   \
  -DarchetypeArtifactId=flink-quickstart-java \
  -DarchetypeVersion=1.18.1

项目生成后pom文件中mainCalss用于指定默认的启动作业类。上传作业时也可手动指定覆盖
在这里插入图片描述
执行环境、数据读入与数据转换

    public static void main(String[] args) throws Exception {
        /**
         * 执行环境与执行模式
         * 环境
         * getExecutionEnvironment 获取当前环境,自动根据当前运行环境获取,常用
         * createLocalEnvironment   获取本地环境
         * createRemoteEnvironment  获取集群环境
         * 处理模式
         * Streaming    流,默认常用
         * Batch        批
         * AutoMatic    自动
         *
         */
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        //设置处理模式,不设置默认也是STREAMING
        env.setRuntimeMode(RuntimeExecutionMode.STREAMING);
        /**
         * 源算子(数据源),返回DataStreamSource<T>
         * *从集合获取
         *          List<String> data = Arrays.asList("1", "@", "3");
         *          env.fromCollection(data);
         *
         * *直接生成
         *      env.fromElements("a", "b", "c");
         *
         * *从文件获取,pom需要额外引入flink-connector-files
         *          FileSource<String> fileSource = FileSource.forRecordStreamFormat(new TextLineInputFormat(), new Path("input/word.txt")).build();
         *          env.fromSource(fileSource, WatermarkStrategy.noWatermarks(), "file");
         *
         * *从socket读入
         *      env.socketTextStream("172.0.0.5", 6789);
         *
         * 从kafka读入,需要额外引入flink-connector-kafka
         *        KafkaSource<String> kafkaSource = KafkaSource.<String>builder()
         *                 .setBootstrapServers("127.0.0.1:9092")
         *                 .setTopics("topic")
         *                 .setGroupId("group")
         *                 .setStartingOffsets(OffsetsInitializer.latest())
         *                 .setValueOnlyDeserializer(new SimpleStringSchema())
         *                 .build();
         *
         *         env.fromSource(kafkaSource, WatermarkStrategy.noWatermarks(), "kafka");
         *
         * 从数据生成器读入,flink-connector-datagen
         *      略
         */
        DataStreamSource<String> source = env.fromElements("a", "b", "c");
        /**
         * ↑ 合流
         * union 合流,只能合并相同类型的流,可一次性合并多条
         * source.union(source1);
         *
         * connect流连接,只能两条连接,返回值变更为ConnectedStreams
         * source.connect(source);
         * 流连接可连接不同类型流,两条流数据分开处理,处理后返回一条流,
         * 双流数据匹配用集合存储,再反向匹配对向流。为保证连接流相同key在相同的slot中处理
         * 多并行度下需要根据匹配条件进行keyby,保证相同key数据到一起去才能匹配上
         *
         */


        /**
         * 转换算子(相当于sql体系处理数据而入库而后执行sum count group等操作)
         * 返回SingleOutputStreamOperator<T>
         * map/filter/flatMap为三个常用的转换算子,大致等价于数据入库。怎样入库,入库那些
         *
         * map(映射,全部入库)
         *          SingleOutputStreamOperator<Object> streamOperator = source.map(new MapFunction<String, Object>() {
         *             @Override
         *             public Object map(String value) throws Exception {
         *                 return value;
         *             }
         *         });
         *   上一段可lamb写法简化为 SingleOutputStreamOperator<Object> streamOperator = source.map((String v) -> v);
         *   依次输出a,b,c
         *
         * filter(过滤,选择那些入库)
         *  source.filter((String v) -> v.equals("a"));
         *  只会输出a
         *
         * flatMap(扁平映射)
         * 就是map的增强,map一次只能返回一个值,flatMap使用收集器Collector可以返回多个
         *  source.flatMap(((String value, Collector<String> out) -> {
         *             if ("c".equals(value)) {
         *                 out.collect("d");
         *                 out.collect("e");
         *                 out.collect("fgh");
         *                 out.collect("a");
         *                 out.collect("b");
         *             } else {
         *                 out.collect(value);
         *             }
         *         })).returns(String.class);
         *  依次输出a,b,d,e,fgh,a,b。使用lamb写法有泛型擦除问题,需要指定returns声明收集器返回类型
         *
         * process 的侧输出流使用,使用tag对数据进行分流
         */
        OutputTag<String> a = new OutputTag<>("a", Types.STRING);
        OutputTag<String> b = new OutputTag<>("b", Types.STRING);

        SingleOutputStreamOperator<String> streamOperator = source.process(new ProcessFunction<String, String>() {
            @Override
            public void processElement(String s, Context context, Collector<String> collector) throws Exception {
                //将a和b分流,其他的进入主流
                if ("a".equals(s)) {
                    context.output(a, s);
                } else if ("b".equals(s)) {
                    context.output(b, s);
                } else {
                    collector.collect(s);
                }
            }
        });
        //获取侧流
        streamOperator.getSideOutput(a).print();
        streamOperator.getSideOutput(b).print();


        //打印输出结果
        streamOperator.print();


        //启动执行,相当于thread.start();
        env.execute("Options Job");
    }

数据聚合、函数自定义等

    public static void main(String[] args) throws Exception {
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        //虚拟一组数据,三列
        DataStreamSource<Tuple3<String, Integer, Integer>> source = env.fromElements(
                Tuple3.of("1", 3, 3),
                Tuple3.of("1", 1, 1),
                Tuple3.of("2", 2, 2),
                Tuple3.of("4", 4, 4)
        );
        //转换
        SingleOutputStreamOperator<Tuple3<String, Integer, Integer>> streamOperator = source.map((Tuple3<String, Integer, Integer> value) -> value)
                .returns(Types.TUPLE(Types.STRING, Types.INT, Types.INT));


        /**
         * 聚合算子
         * keyBy 等价于sql group by ?
         * 以第一列为key
         */
        KeyedStream<Tuple3<String, Integer, Integer>, String> keyedStream = streamOperator.keyBy((Tuple3<String, Integer, Integer> value) -> value.f0);
        /**
         * 聚合算子,keyBy之后可用
         * min,max,sum  最小,最大,求和。入参int为下标,就是数组下标,从0开始为第一位,等价于sql table第一列。string为参数名,传入pojo时可使用,等价于sql table列名
         *  keyedStream.min(1).print();
         *  输出值为
         *  (1,3,3)
         *  (1,1,3)
         *  (2,2,2)
         *  (4,4,4)
         * 上述方法按照key group,只会识别指定的min字段,其他字段保留第一次的值
         * keyedStream.minBy(1).print();
         * 输出值为
         *  (1,3,3)
         *  (1,1,1)
         *  (2,2,2)
         *  (4,4,4)
         *  maxBy,minBy,带by的方法同时也会取其他字段的值
         *
         *  reduce(两两聚合)。一条一条处理,有相同key时触发进入操作
         *
         *          keyedStream.reduce(new ReduceFunction<Tuple3<String, Integer, Integer>>() {
         *             @Override
         *             public Tuple3<String, Integer, Integer> reduce(Tuple3<String, Integer, Integer> value1, Tuple3<String, Integer, Integer> value2) throws Exception {
         *                 System.out.println("value1:" + value1);
         *                 System.out.println("value2:" + value2);
         *                 return Tuple3.of(value1.f0, value1.f1 + value2.f1, value1.f2 + value2.f2);
         *             }
         *         }).print();
         *  输出结果
         *  (1,3,3)
         * value1:(1,3,3)
         * value2:(1,1,1)
         * (1,4,4)
         * (2,2,2)
         * (4,4,4)
         *
         */

        SingleOutputStreamOperator<Tuple3<String, Integer, Integer>> reduce = keyedStream.reduce(new ReduceFunction<Tuple3<String, Integer, Integer>>() {
            @Override
            public Tuple3<String, Integer, Integer> reduce(Tuple3<String, Integer, Integer> value1, Tuple3<String, Integer, Integer> value2) throws Exception {
                System.out.println("value1:" + value1);
                System.out.println("value2:" + value2);
                return Tuple3.of(value1.f0, value1.f1 + value2.f1, value1.f2 + value2.f2);
            }
        });


        /**
         * 自定义函数,此处使用富函数类
         * open,close为生命周期管理,每个子任务开始结束时运行。非正常退出close不一定调用
         * getRuntimeContext()可用于获取上下文信息,任务编号,名称等等等等
         *
         */
        reduce.map(new RichMapFunction<Tuple3<String, Integer, Integer>, String>() {

            @Override
            public void open(Configuration parameters) throws Exception {
                getRuntimeContext();
                super.open(parameters);
            }

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

            @Override
            public String map(Tuple3<String, Integer, Integer> value) throws Exception {
                return value.f1 + ":" + String.valueOf(value.f1 + value.f2);
            }
        }).print();


        env.execute();
    }

打包后上传作业
在这里插入图片描述
在这里插入图片描述
确认包名是要执行的作业全路径类名。无误提交
在这里插入图片描述
点击最后一个块。此处执行的预览根据作业的不同可能会有1~n个,点最后一个
在这里插入图片描述

此处表示任务由那个task节点处理,docker-flink输出会打印到控制台
在这里插入图片描述
查看对应的task日志获取打印结果

sudo docker logs -f taskmanager2

窗口,触发器,聚合

窗口

滚动滑动概念模型:统计某段时间地铁过闸人数
按小时统计地铁过闸人数,使用滚动窗口,滚动窗口时长为1小时
统计最近一小时地铁过闸人数,5秒更新一次,使用滑动窗口。窗口长度一小时,步长5秒

某人17:59:55过闸,因为网络不好数据发送到flink服务器时已经18:00:10
事件时间Event:指17:59:55过闸,实际业务产生的时间
处理时间Processing:指18:00:10,flink处理数据的时间

窗口在keyby之后获取了keyedStream,调用keyedStream.window()传入

keyedStream.window(SlidingProcessingTimeWindows.of(Time.days(1), Time.seconds(3)))

窗口类型:滑动(时间),滚动(时间),会话,计数,全局

窗口创建(区分事件时间与处理时间)
滑动 TumblingProcessingTimeWindows.of(Time.hours(1))/TumblingEventTimeWindows.of()
滚动 SlidingProcessingTimeWindows.of(Time.hours(1), Time.seconds(5))/SlidingEventTimeWindows.of()
会话 ProcessingTimeSessionWindows.withGap()/EventTimeSessionWindows.withGap()
全局 GlobalWindows.create()

计数窗口不需要显示创建,直接keyedStream.countWindow

会话窗口:首次接收数据时为开始时间,持续多久结束
计数窗口:接收多少个数据后结束
全局窗口:持续统计所有数据

聚合,实现AggregateFunction
implements AggregateFunction<传入数据,中间态数据,返回数据>
例如需要精确计算,传入string,使用bigdecimal计算,结果以string返回 implements AggregateFunction<String, BigDecimal,String>
createAccumulator初始化数据
add聚合操作
getResult返回数据
merge合并操作

触发器,继承Trigger
extends Trigger<入参类型, 窗口类型> -> extends Trigger<String, TimeWindow>
onElement数据进入触发
onProcessingTime处理时间触发
onEventTime事件时间触发
clear清理
非自定义的时间窗口自带了触发器
org.apache.flink.streaming.api.windowing.triggers.ProcessingTimeTrigger为例,数据每次进入处理时注册onProcessingTime的触发时间为窗口关闭时间
onProcessingTime内调用输出结果。业务逻辑就是窗口生存期内持续收集数据,结束时输出结果
返回值主要关注TriggerResult.CONTINUE 继续执行,TriggerResult.FIRE 输出结果(调用聚合方法的getResult)

自定义窗口,此处为时间窗口:extends WindowAssigner<入参, 窗口类型> -> extends WindowAssigner<Object, TimeWindow>
assignWindows创建窗口
getDefaultTrigger默认的触发器类型
getWindowSerializer窗口序列化
isEventTime事件窗口

主要关注assignWindows的返回值 -> return Collections.singletonList(new TimeWindow(开始时间, 结束时间));
时间为毫秒时间戳,其他方法可参照其他实现好的窗口复制↓,需要注意窗口类型(Processing,Event)

org.apache.flink.streaming.api.windowing.assigners.SlidingProcessingTimeWindows
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值