【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种自定义

相关推荐
百度Geek说7 小时前
TDS数据治理深度实践:从标准化到智能化的演进之路
大数据
liang_jy7 小时前
Java volatile
android·java·面试
CYRUS_STUDIO7 小时前
别再手工写 Hook 了!Python + Frida 一网打尽 SO 层动态注册 JNI 调用
android·c++·逆向
shinelord明8 小时前
【大数据技术实战】Flink+DS+Dinky 自动化构建数仓平台
大数据·运维·分布式·架构·flink·自动化
IT果果日记8 小时前
Flink+Dinky实现UDF自定义函数
大数据·后端·flink
字节跳动数据平台8 小时前
火山引擎多模态数据湖落地深势科技,提升科研数据处理效能
大数据
用户Taobaoapi20148 小时前
多店铺数据采集效率低?京东API批量调用接口支持千级商品详情批量拉取
大数据·数据挖掘·数据分析
武子康9 小时前
大数据-87 Spark 实现圆周率计算与共同好友分析:Scala 实战案例
大数据·后端·spark
BYSJMG9 小时前
计算机大数据毕业设计选题:基于Spark+hadoop的全球香水市场趋势分析系统
大数据·vue.js·hadoop·python·spark·django·课程设计