Flink API - 基本转换算子

基本转换算子会针对流中的每一个单独的事件做处理,也就是说每一个输入数据会产生一个输出数据。单值转换,数据的分割,数据的过滤,都是基本转换操作的典型例子。

基本转换算子 - Map

map 算子通过调用 DataStream.map() 来指定。map 算子的使用将会产生一条新的数据流。它会将每一个输入的事件传送到一个用户自定义的 mapper,这个 mapper 只返回一个输出事件,这个输出事件和输入事件的类型可能不一样。下图展示了一个 map 算子,这个 map 将每一个正方形转化成了圆形。
在这里插入图片描述
MapFunction 的类型与输入事件和输出事件的类型相关,可以通过实现 MapFunction 接口来定义。接口包含 map() 函数,这个函数将一个输入事件恰好转换为一个输出事件。

为了更好的演示,我们自定义一个读取点击事件的数据源

public class Event {

    public String user;
    public String url;
    public Long timestamp;


    public Event() {
    }

    public Event(String user, String url, Long timestamp) {
        this.user = user;
        this.url = url;
        this.timestamp = timestamp;
    }

    @Override
    public String toString() {
        return "Event{" +
                "user='" + user + '\'' +
                ", url='" + url + '\'' +
                ", timestamp=" + new Timestamp(timestamp) +
                '}';
    }



}

public class ClickSource implements SourceFunction<Event> {
    private boolean running = true;
    private String[] userArr = {"Mary", "Bob", "Alice", "Liz"};
    private String[] urlArr = {"./home", "./cart", "./fav", "./prod?id=1", "./prod?id=2"};
    private Random random = new Random();

    @Override
    public void run(SourceContext<Event> ctx) throws Exception {
        while (running) {
            // collect方法,向下游发送数据
            ctx.collect(
                    new Event(
                            userArr[random.nextInt(userArr.length)],
                            urlArr[random.nextInt(urlArr.length)],
                            Calendar.getInstance().getTimeInMillis()
                    )
            );
            Thread.sleep(100L);
        }
    }

    @Override
    public void cancel() {
        running = false;
    }
}

下面通过利用 Map 算子将 Event 中的 user 字段提取出来

public class MapOperator {

    // 提取出 Event user

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

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        //map: 针对流中的每一个元素,输出一个元素

        DataStreamSource<Event> eventDataStreamSource = env
                .addSource(new ClickSource());

        SingleOutputStreamOperator<String> mapStream = eventDataStreamSource
                // 第一个泛型:输入类型,流中数据类型
                // 第二个泛型:输出类型
                .map(new MapFunction<Event, String>() {
                    @Override
                    public String map(Event value) throws Exception {
                        return value.user;
                    }
                });

        // 打印到控制台
        mapStream
                .print();

        // 执行
        env.execute("Map Operator Job");
    }

}

res:
12> Liz
13> Alice
14> Alice
15> Bob
16> Mary
1> Mary
2> Mary
3> Alice
4> Mary

Flink 程序与数据流

https://blog.csdn.net/weixin_44760145/article/details/129076879[程序与数据流]
所有的 Flink 程序都是由三部分组成的:Source 、Transformation 和 Sink

创建流执行环境

在程序中,第一步是创建流执行环境,执行环境决定了程序是运行在单机还是集群上,在 DataStream API中,程序的执行环境是通过 StreamExecutionEnvironment 设置,通过调用静态getExecutionEnvironment() 方法来获取执行环境。这个方法根据调用方法的上下文,返回一个本地的或者远程的环境。如果这个方法是一个客户端提交到远程集群的代码调用的,那么这个方法将会返回一个远程的执行环境。否则,将返回本地执行环境。

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

读取输入流 Source

一旦执行环境设置好,就该写业务逻辑了。StreamExecutionEnvironment 提供了创建数据源的方法,这些方法可以从数据流中将数据摄取到程序中。数据流可以来自消息队列或者文件系统,也可能是实时产生的(例如 socket )。
在例子中我们通过读取自定义的点击时间流:

DataStreamSource<Event> eventDataStreamSource = env
                .addSource(new ClickSource());

转换算子 Transformation

一旦我们有一条 DataStream,我们就可以在这条数据流上面使用转换算子了。转换算子有很多种。一些转换算子可以产生一条新的 DataStream,当然这个DataStream 的类型可能是新类型。还有一些转换算子不会改变原有 DataStream 的数据,但会将数据流分区或者分组。业务逻辑就是由转换算子串起来组合而成的。
在例子中,我们通过 map() 转换算子将点击事件流中的 user 字段提取出来:

 SingleOutputStreamOperator<String> mapStream = eventDataStreamSource
         // 第一个泛型:输入类型,流中数据类型
         // 第二个泛型:输出类型
         .map(new MapFunction<Event, String>() {
             @Override
             public String map(Event value) throws Exception {
                 return value.user;
             }
         });

输出结果 Sink

流处理程序经常将它们的计算结果发送到一些外部系统中去,例如:Apache Kafka,文件系统,或者数据库中。Flink 提供了一个维护的很好的 sink 算子的集合,这些 sink 算子可以用来将数据写入到不同的系统中去。我们也可以实现自己的 sink 算子。也有一些 Flink 程序并不会向第三方外部系统发送数据,而是将数据存储到 Flink 系统内部,然后可以使用 Flink 的可查询状态的特性来查询数据。
在例子中我们将提取的事件流通过标准输出 print() 打印控制台:

 // 打印到控制台
 mapStream
         .print();

执行

当应用程序完全写好时,我们可以调用StreamExecutionEnvironment.execute()来执行应用程序。

 // 执行
 env.execute("Map Operator Job");

基本转换算子 - Filter

filter 转换算子通过在每个输入事件上对一个布尔条件进行求值来过滤掉一些元素,然后将剩下的元素继续发送。一个 true 的求值结果将会把输入事件保留下来并发送到输出,而如果求值结果为 false,则输入事件会被抛弃掉。通过调用 DataStream.filter() 来指定流的 filter 算子,filter 操作将产生一条新的流,其类型和输入流中的事件类型是一样的。下图展示了只产生白色方框的 filter 操作。
在这里插入图片描述
布尔条件可以使用函数、FilterFunction 接口或者匿名函数来实现。FilterFunction 中的泛型是输入事件的类型。定义的 filter() 方法会作用在每一个输入元素上面,并返回一个布尔值。
下面的例子展示如何通过 filter 来从点击流中过滤出 Mary 用户的点击事件:

public class FilterOperator {

    // 过滤 Mary 的 点击行为

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

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();


        //filter: 针对流中的每一个元素,输出零个或一个元素

        env
                .addSource(new ClickSource())
                .filter(new FilterFunction<Event>() {
                    @Override
                    public boolean filter(Event value) throws Exception {
                        return "Mary".equals(value.user);
                    }
                })
                .print();

        env.execute();
    }
}

基本转换算子 - FlatMap

flatMap 算子和 map 算子很类似,不同之处在于针对每一个输入事件 flatMap 可以生成 0 个、1 个或者多个输出元素。事实上,flatMap 转换算子是 filter 和 map 的泛化。所以 flatMap 可以实现 map 和 filter 算子的功能。下图展示了 flatMap 如何根据输入事件的颜色来做不同的处理。如果输入事件是白色方框,则直接输出。输入元素是黑框,则复制输入。灰色方框会被过滤掉。
在这里插入图片描述
flatMap 算子将会应用在每一个输入事件上面。对应的 FlatMapFunction 定义了 flatMap() 方法,这个方法返回 0 个、1 个或者多个事件到一个 Collector 集合中,作为输出结果。
下面的例子展示如何通过 flatMap 来从点击流中过滤出 Mary 和 Alice 用户的点击事件,并且 Mary 的 点击行为 复制 2 次,Alice 的 点击行为不处理:

public class FlatMapOperator {

    //  Mary 的 点击行为 复制 2 次
    // Alice 的 点击行为 复制 1 次
    // 其余过滤掉

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

        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        env.setParallelism(1);


        // DataStream → DataStream
        // flatMap:针对流中的每一个元素,输出零个、一个或多个 元素
        // flatMap 是 map 与 filter 的泛化  可以实现 map 与 filter 的功能

        env
                .addSource(new ClickSource())
                .flatMap(new FlatMapFunction<Event, Event>() {
                    @Override
                    public void flatMap(Event value, Collector<Event> out) throws Exception {
                        if ("Mary".equals(value.user)) {
                            out.collect(value);
                            out.collect(value);
                        } else if ("Alice".equals(value.user)) {
                            out.collect(value);
                        }
                    }
                })
                .print();

        env.execute();
    }
}

类型

Flink 程序所处理的流中的事件一般是对象类型。操作符接收对象输出对象。所以 Flink 的内部机制需要能够处理事件的类型。在网络中传输数据,或者将数据写入到状态后端、检查点和保存点中,都需要我们对数据进行序列化和反序列化。为了高效的进行此类操作,Flink 需要流中事件类型的详细信息。Flink 使用了 Type Information 的概念来表达数据类型,这样就能针对不同的数据类型产生特定的序列化器,反序列化器和比较操作符。
Flink 也能够通过分析输入数据和输出数据来自动获取数据的类型信息以及序列化器和反序列化器。尽管如此,在一些特定的情况下,例如匿名函数或者使用泛型的情况下,我们需要明确的提供数据的类型信息,来提高我们程序的性能。
支持的数据类型
通过对转换算子 API 的体会,我们已经知道 Flink 可以支持 POJO 和 Java 中的基本数据类型。
实际上 Flink 支持 Java 提供的所有普通数据类型。最常用的数据类型可以做以下分类:

  • Primitives(原始数据类型:8 种基本数据类型、Array、HashMap、Enum 等 )
  • Flink 专门为 Java 实现的 Tuples(元组)
  • POJO 类型
  • 一些特殊的类型(Hadoop Writable types)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值