Flink从入门到放弃—Stream API—常用算子(map和flatMap)

导航

观看本文章之前,请先看之前文章
(十)Flink Datastream API 编程指南 算子-1 (转换算子、物理分区、任务链、资源组 、算子和作业)等基本介绍

常用算子基本使用

效果

本次主要是阅读几个常用算子的源码。

阅读算子列表

  • map
  • flatmap
  • keyby
  • filter
  • reduce
  • window
  • windowAll
  • 其它

Map()

首先说一下这个算子是one to one的,通俗的讲就是进一条数据 经过逻辑处理后 必出一条数据。

用户代码

package com.stream.samples;

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

/**
 * @author DeveoperZJQ
 * @since 2022/11/12
 */
public class MapOperator {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 消费socket数据源
        DataStreamSource<String> dataStream = env.socketTextStream("192.168.112.147", 7777);

// map => String::length 是传入的虚函数map的逻辑
        SingleOutputStreamOperator<Integer> map = dataStream.map(String::length);

// print输出
        map.print();

        env.execute(MapOperator.class.getSimpleName());
    }
}

上面的逻辑非常简单,目的只是为了追踪map的入口,你可以使用debug模式,并且在map上打上断点,可以一层一层的往下看。

两种变形

public <R> SingleOutputStreamOperator<R> map(MapFunction<T, R> mapper) {
// 根据传入函数获取返回值类型
       TypeInformation<R> outType = TypeExtractor.getMapReturnTypes((MapFunction)this.clean(mapper), this.getType(), Utils.getCallLocationName(), true);
       // 调用下面的map方法
       return this.map(mapper, outType);
   }

// 带返回的输出类型参数,可以看到传入的operator默认名字就叫Map
   public <R> SingleOutputStreamOperator<R> map(MapFunction<T, R> mapper, TypeInformation<R> outputType) {
       return this.transform("Map", outputType, (OneInputStreamOperator)(new StreamMap((MapFunction)this.clean(mapper))));
   }

在这里插入图片描述
上面两个重载方法刚好对应上面截图中的两个方法。

 @PublicEvolving
    public <R> SingleOutputStreamOperator<R> transform(String operatorName, TypeInformation<R> outTypeInfo, OneInputStreamOperator<T, R> operator) {
    // 抽象工厂
        return this.doTransform(operatorName, outTypeInfo, SimpleOperatorFactory.of(operator));
    }

上面代码有一个抽象工厂的简单 算子工厂类,传入的是OneInputStreamOperator类型的算子,然后把用户代码的operator转换成了StreamOperatorFactory operatorFactory, 工厂类是真的多啊,如果设计模式关于工厂方法和抽象工厂没学牢固的同学,可以来这里细品。

// protected 只能包能调用
protected <R> SingleOutputStreamOperator<R> doTransform(String operatorName, TypeInformation<R> outTypeInfo, StreamOperatorFactory<R> operatorFactory) {
// 我理解这里调用这个就是为了抛出异常的返回类型,如果可用,typeUsed则标识为true
        this.transformation.getOutputType();
        // 构建OneInputTransformation
        OneInputTransformation<T, R> resultTransform = new OneInputTransformation(this.transformation, operatorName, operatorFactory, outTypeInfo, this.environment.getParallelism());
        SingleOutputStreamOperator<R> returnStream = new SingleOutputStreamOperator(this.environment, resultTransform);
        // 这里讲用户逻辑封装到resultTransform,全部装填到List<Transformation<?>> 为构建流图StreamGraph做准备
        this.getExecutionEnvironment().addOperator(resultTransform);
        return returnStream;
    }

// 添加算子到Transformations<>集合中
@Internal
    public void addOperator(Transformation<?> transformation) {
        Preconditions.checkNotNull(transformation, "transformation must not be null.");
        this.transformations.add(transformation);
    }

env.execute(MapOperator.class.getSimpleName());这里开始执行,物理执行图部署成功,这个就开始了消费数据。

当然通过上面说的,你可以尝试着自定义map算子或者其它算子,只要使用transform(String operatorName, TypeInformation outTypeInfo, OneInputStreamOperator<T, R> operator)得当,就会实现你的功能,但是一般不建议入门选手这样做。

这里面,咱们还看到很多使用clean() 方法的地方,后面章节会拿出一章说明这个。

flatMap()

可以看出,flatMap的源码和map非常类似。有一个地方值得咱们看一下,就是传入的函数和map是不同 ,flatmap可以一进多出,一进不出,但是map做不到,单纯从算子性能上说flatMap的开销是要比map大的。

来看一下下面两者的区别:new StreamMap((MapFunction) vs new StreamFlatMap((FlatMapFunction)

StreamMap

public StreamMap(MapFunction<IN, OUT> mapper) {
        super(mapper);
        this.chainingStrategy = ChainingStrategy.ALWAYS;
    }

单从这个各自的构造器上看不出什么,传入的函数不一样,可以点击进去

@Public
@FunctionalInterface
public interface MapFunction<T, O> extends Function, Serializable {
    O map(T value) throws Exception;
}

map方法是有返回值的。

再来看下具体处理方法:

public void processElement(StreamRecord<IN> element) throws Exception {
        this.output.collect(element.replace(((MapFunction)this.userFunction).map(element.getValue())));
    }

从上面可以看出map直接把数据通过collect回收了,等于没有对用户暴露,那么flatMatp呢?看下面的源码。

StreamFlatMap

public StreamFlatMap(FlatMapFunction<IN, OUT> flatMapper) {
        super(flatMapper);
        this.chainingStrategy = ChainingStrategy.ALWAYS;
    }

点击进去,是下面的接口类,里面有一个flatMap方法,继承的类也是一样的。

@Public
@FunctionalInterface
public interface FlatMapFunction<T, O> extends Function, Serializable {

    void flatMap(T value, Collector<O> out) throws Exception;
}

flatMap没有返回值,它是利用Collector out返回数据的。

再来看下具体处理方法:

public void open() throws Exception {
        super.open();
        this.collector = new TimestampedCollector(this.output);
    }

 public void processElement(StreamRecord<IN> element) throws Exception {
        this.collector.setTimestamp(element);
        ((FlatMapFunction)this.userFunction).flatMap(element.getValue(), this.collector);
    }

把this.collector作为参数封装到flatMap里了,用户可以通过collector进行操作。

那么现在是不是明白了,为啥map是一对一,flatMap是一对多和一对0了吧。

结语

本文章是系列文章中的一篇,如果有错误的地方,欢迎批评指正!欢迎同行交流!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
关于Flink入门到精通的学习路径可以分为以下几个步骤: 1. 学习Flink的基本概念和原理:了解Flink的核心概念,如数据流、转换操作、窗口、状态管理等。可以通过官方文档、教程和书籍进行学习。 2. 安装和配置Flink环境:学习如何在本地或集群环境中安装和配置Flink,并了解各种配置选项和参数的含义和用法。 3. 编写和运行Flink应用程序:学习如何使用FlinkAPI编写和提交Flink应用程序,包括数据流的处理逻辑、转换操作和窗口计算等。 4. 理解Flink的核心特性和功能:深入学习Flink的一些核心特性和功能,如事件时间处理、容错机制、状态管理和水位线等。 5. 优化和调优Flink应用程序:学习如何优化和调优Flink应用程序,包括调整并行度、内存管理、网络通信和容错机制等方面的优化。 6. 实践应用场景和案例:通过实践应用场景和案例,如流式处理、实时数据分析和机器学习等,来巩固和应用所学的知识。 7. 探索Flink的高级特性和扩展:了解Flink的高级特性和扩展功能,如CEP(复杂事件处理)、表格API、图处理等,并根据需求选择合适的扩展。 总结来说,从入门到精通Flink需要学习基本概念和原理、掌握环境配置和应用开发、理解核心特性和功能、进行优化和调优、实践应用场景和案例,并深入探索高级特性和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

京河小蚁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值