【Flink】DataStream API:UDF和物理分区算子

目录

用户自定义函数(UDF)

用户自定义函数(user-defined function,UDF),即用户可以根据自身需求,重新实现算子的逻辑。

用户自定义函数分为:函数类、匿名函数、富函数类

函数类

Flink暴露了所有UDF函数的接口,具体实现方式为接口或者抽象类,例如MapFunction、FilterFunction、ReduceFunction等。所以用户可以自定义一个函数类,实现对应的接口。

需求:用来从用户的点击数据中筛选包含"sensor_1"的内容:

方式一:实现FilterFunction接口

java 复制代码
package udf;

import env.WaterSensor;
import org.apache.flink.api.common.functions.FilterFunction;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

public class TransFunctionUDF {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<WaterSensor> source = env.fromElements(
                new WaterSensor("sensor_1", 1L, 1),
                new WaterSensor("sensor_1", 2L, 2),
                new WaterSensor("sensor_2", 2L, 2),
                new WaterSensor("sensor_3", 3L, 3)
        );

        SingleOutputStreamOperator<WaterSensor> filter = source.filter(new UserFilter());
        filter.print();
        env.execute();

    }

    public static class UserFilter implements FilterFunction<WaterSensor>{

        @Override
        public boolean filter(WaterSensor value) throws Exception {
            return "sensor_1".equals(value.id);
        }
    }
}

富函数类

"富函数类"也是DataStream API提供的一个函数类的接口,所有的Flink函数类都有其Rich版本。富函数类一般是以抽象类的形式出现的。例如:RichMapFunction、RichFilterFunction、RichReduceFunction等。

与常规函数类的不同主要在于,富函数类可以获取运行环境的上下文,并拥有一些生命周期方法,所以可以实现更复杂的功能。

Rich Function有生命周期的概念。典型的生命周期方法有:

  1. open()方法,是Rich
    Function的初始化方法,也就是会开启一个算子的生命周期。当一个算子的实际工作方法例如map()或者filter()方法被调用之前,open()会首先被调用。
  2. close()方法,是生命周期中的最后一个调用的方法,类似于结束方法。一般用来做一些清理工作。
    需要注意的是,这里的生命周期方法,对于一个并行子任务来说只会调用一次;而对应的,实际工作方法,例如RichMapFunction中的map(),在每条数据到来后都会触发一次调用。
java 复制代码
package transform;

import org.apache.flink.api.common.functions.RichMapFunction;
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;

public class RichFunctionDemo {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        env.setParallelism(2);

        DataStreamSource<Integer> source = env.fromElements(1, 2, 3, 4);

        SingleOutputStreamOperator<Integer> map = source.map(new RichMapFunction<Integer, Integer>() {
            @Override
            public Integer map(Integer value) {
                return value + 10;
            }

            @Override
            public void open(Configuration parameters) throws Exception {

                super.open(parameters);
                System.out.println("当前子任务运行编号是:"+getRuntimeContext().getIndexOfThisSubtask()+" "+"当前子任务名称是:" +getRuntimeContext().getTaskNameWithSubtasks()+" open");

            }

            @Override
            public void close() throws Exception {
                super.close();
                System.out.println("当前子任务运行编号是:"+getRuntimeContext().getIndexOfThisSubtask()+" "+"当前子任务名称是:" +getRuntimeContext().getTaskNameWithSubtasks()+" close");

            }
        });
        map.print();
        env.execute();
    }
}

RichXXXFunction:富函数

多了生命周期管理方法:

java 复制代码
open():每个子任务在启动时,调用一次
close():每个子任务在结束时,调用一次
如果是flink程序异常挂掉,不会调用close
如果是正常调用cancel命令,可以close

多了一个运行时上下文

可以获取一些运行时的环境信息,比如,子任务编号、名称等

物理分区算子

常见的物理分区策略有:随机分配(Random)、轮询分配(Round-Robin),重缩放(Rescale)和广播(Broadcast)

随机分区

最简单的重分区方式就是直接"洗牌"。通过调用DataStream的.shuffle()方法,将数据随机地分配到下游算子的并行任务中去。

随机分区服从均匀分布(uniform distribution),所以可以把流中的数据随机打乱,均匀地传递到下游任务分区。因为是完全随机的,所以对于同样的输入数据, 每次执行得到的结果也不会相同。

经过随机分区之后,得到的依然是一个DataStream。

我们可以做个简单测试:将数据读入之后直接打印到控制台,将输出的并行度设置为2,中间经历一次shuffle。执行多次,观察结果是否相同。


java 复制代码
package partition;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
public class PartitionDemo {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        env.setParallelism(2);

        DataStreamSource<String> source = env.socketTextStream("master", 7777);

        source.shuffle().print();

        env.execute();
    }
}

轮询分区

轮询,简单来说就是"发牌",按照先后顺序将数据做依次分发。通过调用DataStream的.rebalance()方法,就可以实现轮询重分区。rebalance使用的是Round-Robin负载均衡算法,可以将输入流数据平均分配到下游的并行任务中去。


重缩放分区(rescale)

重缩放分区和轮询分区非常相似。当调用rescale()方法时,其实底层也是使用Round-Robin算法进行轮询,但是只会将数据轮询发送到下游并行任务的一部分中。rescale的做法是分成小团体,发牌人只给自己团体内的所有人轮流发牌。

java 复制代码
stream.rescale()

广播(broadcast)

发送给下游所有的子任务

这种方式其实不应该叫做"重分区",因为经过广播之后,数据会在不同的分区都保留一份,可能进行重复处理。可以通过调用DataStream的broadcast()方法,将输入数据复制并发送到下游算子的所有并行任务中去。

java 复制代码
stream.broadcast()

全局分区(global)

全局分区也是一种特殊的分区方式。这种做法非常极端,通过调用.global()方法,会将所有的输入流数据都发送到下游算子的第一个并行子任务中去。这就相当于强行让下游任务并行度变成了1,所以使用这个操作需要非常谨慎,可能对程序造成很大的压力。

java 复制代码
stream.global()


自定义分区

当Flink提供的所有分区策略都不能满足用户的需求时,我们可以通过使用partitionCustom()方法来自定义分区策略。

1)自定义分区器

java 复制代码
public class MyPartitioner implements Partitioner<String> {

    @Override
    public int partition(String key, int numPartitions) {
        return Integer.parseInt(key) % numPartitions;
    }
}

使用自定义分区


java 复制代码
package partition;

import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

public class MyPartitionDemo {

    public static void main(String[] args) throws Exception {

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        env.setParallelism(2);


        DataStreamSource<String> source = env.socketTextStream("master", 7777);

        source.partitionCustom(new MyPartitioner(),a -> a ).print();


        env.execute();


    }
}

keyby:按指定key去发送,相同key发往同一个子任务

one-to-one:Forward分区器

总结:Flink提供了7种分区器 + 1种自定义

相关推荐
岁岁种桃花儿4 分钟前
Flink从入门到上天系列第二十五篇:Flink和Kafka连接时的精准一次性
大数据·flink·kafka
皮皮宋吖4 分钟前
皮皮宋渗透日记 11|文件包含漏洞全解析:LFI/RFI/ 伪协议 / 绕过 / 防御
android·安全
旗讯数字6 分钟前
服装吊牌智能识别+结构化抽取+国标合规审查|旗讯数字解决方案
大数据·人工智能
阿拉斯攀登10 分钟前
【无人售货柜・RK+YOLO】篇 6:安卓端落地!RK3576 + 安卓系统,YOLO RKNN 模型实时推理保姆级教程
android·人工智能·yolo·目标跟踪·瑞芯微·嵌入式驱动
素心如月桠18 分钟前
Classification Codes-2 填写规范(Ariba/cXML/ 工业采购场景专属)
大数据
只能是遇见34 分钟前
ERROR 1524 (HY000) Plugin ‘mysql_native_password‘ is not loaded
android·数据库·mysql
赵谨言34 分钟前
基于YOLOv5的海棠花花朵检测识别:文献综述与研究展望
大数据·开发语言·经验分享·python
jkyy201434 分钟前
健康有益健康监测座舱:以科技之力,定义出行健康新标杆
大数据·人工智能·物联网·健康医疗
weixin1997010801640 分钟前
“迷你京东”全栈架构设计与实现
java·大数据·python·数据库架构
菜鸡儿齐44 分钟前
MapReduce-源码学习
大数据·学习·mapreduce