【Flink】【第五章 流处理API】Tramsform算子

在这里插入图片描述

5.3 Transform

在这里插入图片描述
在Spark中,算子分为转换算子和行动算子,转换算子的作用可以通过算子方法的调用将一个RDD转换另外一个RDD,Flink中也存在同样的操作,可以将一个数据流(stream)转换为其他的数据流(stream)。转换过程中,数据流的类型会发生变化,数据流中的数据的类型也会发生变化,那么到底Flink支持什么样的数据类型呢,其实我们常用的数据类型,Flink都是支持的。比如:Long, String, Integer, Int, 元组,样例类,List, Map等。

5.3.1 map

  • 映射:将数据流中的数据进行转换, 形成新的数据流,消费一个元素并产出一个元素
  • 参数:lambda表达式或MapFunction实现类
  • 返回:DataStream

在这里插入图片描述

source = env.readTextFile("D:\\IdeaProjects\\bigdata\\flink\\src\\main\\resources\\sensort.txt");
        SingleOutputStreamOperator<SensorReading> map = source.map(new MyMapFunction());
        map.print();

public class MyMapFunction implements MapFunction<String, SensorReading> {
    @Override
    public SensorReading map(String s) throws Exception {
        String[] split = s.split(",");
        String name = split[0].trim();
        long ts = Long.parseLong(split[1].trim());
        double temp = Double.parseDouble(split[2].trim());

        return new SensorReading(name,ts,temp);
    }
}

5.3.2 flatMap

  • 扁平映射:将数据流中的整体拆分成一个一个的个体使用,消费一个元素并产生零到多个元素
  • 参数:lambda表达式或FlatMapFunction实现类
  • 返回:DataStream
    在这里插入图片描述
SingleOutputStreamOperator<Integer> resultDS = inputDS.flatMap(
    new FlatMapFunction<List<Integer>, Integer>() {
        @Override
        public void flatMap(List<Integer> value, Collector<Integer> out) throws Exception {
            for (Integer element : value) {
                out.collect(element);
            }
        }
    }
);

5.3.3 filter

  • 过滤:根据指定的规则将满足条件(true)的数据保留,不满足条件(false)的数据丢弃
  • 参数:Scala匿名函数或FilterFunction实现类
  • 返回:DataStream
    在这里插入图片描述

SingleOutputStreamOperator<Integer> filterDS = inputDS.filter(new FilterFunction<Integer>() {
            @Override
            public boolean filter(Integer value) throws Exception {
                return value > 5;
            }
        }
);

5.3.4 keyBy

  • 分流:根据指定的Key的hashcode % 并行度决定将元素发送到不同的分区,相同的Key会被分到一个分区(这里分区指的就是下游算子多个并行节点的其中一个)。

  • 参数:Scala匿名函数或POJO属性或元组索引,不能使用数组

  • DataStream -> KeyedStream,输入DataStream,输出KeyedStream

  • KeyBy后,会存在多个Key分到同一个分区中,但基于KeyedStream的操作都是基于Key的

  • KeyBy不能算转换算子,只能算传输算子,其定义数据发往下游算子的哪个分区
    在这里插入图片描述

  • keyBy算子返回值是KeyedStream<T,Key>,泛型中,key在后面,元素在前面

@Test
    public void keyBy()  {

        SingleOutputStreamOperator<SensorReading> map = source.map(new MyMapFunction());
        //1.keyBy 中写索引下标 只能用于元组类型

        KeyedStream<SensorReading, String> sensorReadingStringKeyedStream = map.keyBy(new KeySelector<SensorReading, String>() {
            @Override
            public String getKey(SensorReading sensorReading) throws Exception {
                return sensorReading.getName();
            }
        });

        //select max(temp) from xx group by name
        SingleOutputStreamOperator<SensorReading> name = sensorReadingStringKeyedStream.max("temp");
        name.print();

    }

5.3.5 基于KeyedStream的滚动聚合算子

  • Flink作为计算框架,主要应用于数据计算处理上, 所以在keyBy对数据进行分流后,可以对数据进行相应的统计分析

  • 滚动聚合算子 -> Rolling Aggregation

  • 滚动聚合算子可以针对KeyedStream的每一个支流做聚合。执行完成后,会将聚合的结果合成一个流返回,所以结果都是DataStream

sum()

在这里插入图片描述

min()

在这里插入图片描述

minBy()

max()

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
特点: max字段输出当前组内的最大值,非分组字段和组内第一个保持一致

maxBy()

在这里插入图片描述
特点:

  • maxBy字段为组内最大值,非分组字段和组内最大值保持一致。

  • maxBy和max的区别在于 非聚合字段和谁保持一致,maxBy和组内最大值保持一致,max和组内第一条数据保持一致。

Reduce()

  • KeyedStream → DataStream
  • 对一个分组数据流的聚合操作,合并当前的元素和上次聚合的结果,产生一个新的值,返回的流中包含每一次聚合的结果。
  • 特点:聚合结果的数据类型和待聚合的数据的数据类型必须一致;其实reduce()可以实现上面所有滚动算子的功能;

演示
需求:使用reduce聚合,取最小的温度值,并输出当前的时间戳

在这里插入图片描述

    @Test
    public void reduce(){
        SingleOutputStreamOperator<SensorReading> map = source.map(new MyMapFunction());
        //1.keyBy 中写索引下标 只能用于元组类型

        KeyedStream<SensorReading, String> sensorReadingStringKeyedStream = map.keyBy(new KeySelector<SensorReading, String>() {
            @Override
            public String getKey(SensorReading sensorReading) throws Exception {
                return sensorReading.getName();
            }
        });


        //取最小的温度值,并输出当前的时间戳
        SingleOutputStreamOperator<SensorReading> reduce = sensorReadingStringKeyedStream.reduce(new ReduceFunction<SensorReading>() {
            /**
             *
             * @param s1 : 以前聚合的结果
             * @param s2 : 新来的一条数据
             * @return
             * @throws Exception
             */
            @Override
            public SensorReading reduce(SensorReading s1, SensorReading s2) throws Exception {
                return new SensorReading(s2.getName(), s2.getTs(), Math.min(s1.getTemp(), s2.getTemp()));

            }
        });

        reduce.print();
    }

5.3.10 process

上面所介绍的所有算子除了定义传输过程的算子比如keyBy,都可以用process实现

package No06_Process;

import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.KeyedProcessFunction;
import org.apache.flink.streaming.api.functions.ProcessFunction;
import org.apache.flink.util.Collector;

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


        DataStreamSource<String> source = env.readTextFile("E:\\work\\bigdata\\flink\\src\\main\\resources\\hello.txt");


        //todo 1.process实现flatMap
        KeyedStream<String, String> keyedStream = source.process(new ProcessFunction<String, String>() {
            @Override
            public void processElement(String value, Context ctx, Collector<String> out) throws Exception {
                String[] fields = value.split(" ");
                for (String field : fields) {
                    out.collect(field);
                }
            }
        }).keyBy(new KeySelector<String, String>() {
            @Override
            public String getKey(String value) throws Exception {
                return value;
            }
        });

        //todo 2.process 实现sum()
        // 由于没学状态编程,所以这里用一个变量,这里会存在问题;就是变量是基于
        //并行度的,而非key
        SingleOutputStreamOperator<Tuple2<String, Integer>> res = keyedStream.process(new KeyedProcessFunction<String, String, Tuple2<String, Integer>>() {

            ValueState<Integer> cnt;

            @Override
            public void open(Configuration parameters) throws Exception {
                cnt = getRuntimeContext().getState(new ValueStateDescriptor<Integer>("cnt", Integer.class, 0));
            }

            @Override
            public void processElement(String value, Context ctx, Collector<Tuple2<String, Integer>> out) throws Exception {
                Integer count = cnt.value();
                cnt.update(count + 1);

                out.collect(new Tuple2<>(value, count + 1));
            }
        });

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


5.3.5 split & select

split

  • DataStream → SplitStream
  • 根据某些特征把一个DataStream拆分成两个或者多个DataStream。
  • 需要注意的是,SplitStream仍然是一个Stream,其内部包含了两个DataStream
    在这里插入图片描述

select

  • 将数据流进行split后,如何从SplitStream流中将不同标记的DataStream取出呢,这时就需要使用select算子了。
  • SplitStream→DataStream
  • 从一个SplitStream中获取一个或者多个DataStream。
    在这里插入图片描述

    //需求:传感器数据按照温度高低(以30度为界),拆分成两个流
    @Test
    public void Split()  {

        SingleOutputStreamOperator<SensorReading> map = source.map(new MyMapFunction());


        SplitStream<SensorReading> splitStream = map.split(new OutputSelector<SensorReading>() {
            @Override
            public Iterable<String> select(SensorReading sensorReading) {
                return (sensorReading.getTemp() > 30) ?
                        Collections.singletonList("high") : Collections.singletonList("low");
            }
        });


        //  从splitStream中选出DataStream
        DataStream<SensorReading> highTempStream = splitStream.select("high");
        DataStream<SensorReading> lowTempStream = splitStream.select("low");
        DataStream<SensorReading> allTempStream = splitStream.select("high", "low");


        highTempStream.print("high");
        lowTempStream.print("low");
        allTempStream.print("all");

    }

说明:split和select进行分流、选流的操作已经过时了;现在建议用测输出流

5.3.6 Connect & CoMap

Connect

  • DataStream → ConnectedStreams
  • 在某些情况下,我们需要将两个不同来源的数据流进行连接,实现数据匹配,比如订单支付和第三方交易信息,这两个信息的数据就来自于不同数据源,连接后,将订单支付和第三方交易信息进行对账,此时,才能算真正的支付完成。
  • Flink中的connect算子可以连接两个保持他们类型的数据流,两个数据流被Connect之后,只是被放在了一个同一个流中,内部依然保持各自的数据和形式不发生任何变化,两个流相互独立。
    在这里插入图片描述

基于ConnectedStream的算子CoMap,CoFlatMap

  • ConnectedStreams → DataStream
  • 作用于ConnectedStreams上,功能与map和flatMap一样,对ConnectedStreams中的每一个Stream分别进行map和flatMap处理。
    在这里插入图片描述
 @Test
    public void connect(){


            SingleOutputStreamOperator<SensorReading> map = source.map(new MyMapFunction());


            SplitStream<SensorReading> splitStream = map.split(new OutputSelector<SensorReading>() {
                @Override
                public Iterable<String> select(SensorReading sensorReading) {
                    return (sensorReading.getTemp() > 30) ?
                            Collections.singletonList("high") : Collections.singletonList("low");
                }
            });


             
            DataStream<SensorReading> highTempStream = splitStream.select("high");
            DataStream<SensorReading> lowTempStream = splitStream.select("low");


            SingleOutputStreamOperator<Tuple3<String, Long, Double>> high = highTempStream.map(new MapFunction<SensorReading, Tuple3<String, Long, Double>>() {
                @Override
                public Tuple3<String, Long, Double> map(SensorReading s) throws Exception {
                    return new Tuple3<>(s.getName(), s.getTs(), s.getTemp());
                }
            });
            
            
            //todo 连接   一个流中有两个独立的流 二者可以不同类型
            ConnectedStreams<Tuple3<String, Long, Double>, SensorReading> connectedStreams = high.connect(lowTempStream);

            //todo 
            SingleOutputStreamOperator<Object> map1 = connectedStreams.map(new CoMapFunction<Tuple3<String, Long, Double>, SensorReading, Object>() {
                @Override
                public Object map1(Tuple3<String, Long, Double> tuple3) throws Exception {
                    return new Tuple2<>(tuple3, "tuple");
                }

                @Override
                public Object map2(SensorReading sensor) throws Exception {
                    return new Tuple2<>(sensor, "sensor");
                }
            });

         map1.print();
    }

额外说明:

  1. connect可以将两个不同类型的流合并的一起,join也可以。
  2. CoMap和FlatMap作用在ConnectedStreams中的每一个Stream
  3. CoMap的意义就是统一两个流的数据类型,其实各自返回任意类型也可

5.3.7 Union

  • DataStream → DataStream
  • 对两个或者两个以上的DataStream进行union操作,产生一个包含所有DataStream元素的新DataStream。
    在这里插入图片描述
DataStream<SensorReading> unionStream =
 highTempStream.union(lowTempStream);

Connect与 Union 区别:
1.Union的两个流的类型必须是一样,Union后的流还是DataStream;也就是说Union后,两个流真的合并成了一个流
2. Connect可以不一样,Connect只是形成ConnectedStream,在其内部仍有两个各自独立的流;在之后的coMap中可以调整成为一样的,也可以各自输出各自的
3. Connect只能操作两个流,Union可以操作多个。

5.3.8 join

  • Join和Connect一样,支持两个数据类型不同的流进行合并
  • Join的两个流必须是k-v类型的
  • Flink的Join操作分为两大类:window join和interval join

WindowJoin

windowJoin 就是批次join

1. 滚动窗口Join

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

interval join

在这里插入图片描述
在这里插入图片描述
intervaljoin 只能作用于两个KeyedStream!

Flink中流的转换

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值