flink DataStream Operator算子操作举例


提示:flink流程序中常用的算子举例

一、DataStream Operator

1. Map

Map 算子的输入流是 DataStream,经过 Map 算子后返回的数据格式是 SingleOutputStreamOperator 类型,获取一个元素并生成一个元素,举个例子:

SingleOutputStreamOperator<Employee> map = employeeStream.map(new MapFunction<Employee, Employee>() {
    @Override
    public Employee map(Employee employee) throws Exception {
        employee.salary = employee.salary + 5000;
        return employee;
    }
});
map.print();

每个Employee对象的salary 值加5000后出来

2.FlatMap

FlatMap 算子的输入流是 DataStream,经过 FlatMap 算子后返回的数据格式是 SingleOutputStreamOperator 类型,获取一个元素并生成零个、一个或多个元素,举个例子:

SingleOutputStreamOperator<Employee> flatMap = employeeStream.flatMap(new FlatMapFunction<Employee, Employee>() {
    @Override
    public void flatMap(Employee employee, Collector<Employee> out) throws Exception {
        if (employee.salary >= 40000) {
            out.collect(employee);
        }
    }
});
flatMap.print();

每个Employee对象的salary 值大于40000的出来

3.Filter

对每个元素都进行判断,返回为 true 的元素,如果为 false 则丢弃数据,上面的代码也可以用filter来做:

SingleOutputStreamOperator<Employee> filter = employeeStream.filter(new FilterFunction<Employee>() {
    @Override
    public boolean filter(Employee employee) throws Exception {
        if (employee.salary >= 40000) {
            return true;
        }
        return false;
    }
});
filter.print();

4.KeyBy

KeyBy 在逻辑上是基于 key 对流进行分区,相同的 Key 会被分到一个分区,它返回 KeyedDataStream 数据流。举个例子:

KeyedStream<ProductEvent, Integer> keyBy = productStream.keyBy(new KeySelector<ProductEvent, Integer>() {
    @Override
    public Integer getKey(ProductEvent product) throws Exception {
        return product.shopId;
    }
});
keyBy.print();

KeyBy 有三个重载方法:

  • keyBy(int… fields)
    按照位置分区,比如Tuple类型(从0开始)
  • keyBy(String… fields)
    按照对象中的字段来分区,比如User对象中有个name字段,就可以 keyBy(“name”)按照姓名分区
  • keyBy(KeySelector<T, K> key)
    传入一个key选择器函数,上述实例就是用的这个

5.Reduce

归并操作, 并且操作每处理一个元素总是创建一个新值。即第一个元素和第二个元素处理得到一个新的元素,新的元素再和第三个元素做处理…以此类推。常用的方法有 average、sum、min、max、count,使用 Reduce 方法都可实现,比如:

SingleOutputStreamOperator<Employee> reduce = employeeStream.keyBy(new KeySelector<Employee, Integer>() {
    @Override
    public Integer getKey(Employee employee) throws Exception {
        return employee.shopId;
    }
}).reduce(new ReduceFunction<Employee>() {
    @Override
    public Employee reduce(Employee employee1, Employee employee2) throws Exception {
        employee1.salary = (employee1.salary + employee2.salary) / 2;
        return employee1;
    }
});
reduce.print();

上面先将数据流进行 keyby 操作,因为执行 Reduce 操作只能是 KeyedStream,然后将员工的工资做了一个求平均值的操作。

6.Aggregations

DataStream API 支持各种聚合,例如 min、max、sum 等。 这些函数可以应用于 KeyedStream 以获得 Aggregations 聚合。

KeyedStream.sum(0) 
KeyedStream.sum("key") 
KeyedStream.min(0) 
KeyedStream.min("key") 
KeyedStream.max(0) 
KeyedStream.max("key") 
KeyedStream.minBy(0) 
KeyedStream.minBy("key") 
KeyedStream.maxBy(0) 
KeyedStream.maxBy("key")

max 和 maxBy 之间的区别在于 max 返回流中的最大值,但 maxBy 返回具有最大值的键, min 和 minBy 同理。

7.Window

Window 函数允许按时间或其他条件对现有 KeyedStream 进行分组。 以下是以 10 秒的时间窗口聚合:

inputStream.keyBy(0).window(Time.seconds(10));

8.WindowAll

WindowAll 将元素按照某种特性聚集在一起,该函数不支持并行操作,默认的并行度就是 1,所以如果使用这个算子的话需要注意一下性能问题,以下是使用例子:

inputStream.keyBy(0).windowAll(TumblingProcessingTimeWindows.of(Time.seconds(10)));

9.Window Join

我们可以通过一些 key 将同一个 window 的两个数据流 join 起来。

inputStream.join(inputStream1)
           .where(0).equalTo(1)
           .window(Time.seconds(5))     
           .apply (new JoinFunction () {...});

以上示例是在 5 秒的窗口中连接两个流,其中第一个流的第一个属性的连接条件等于另一个流的第二个属性。

10.Split和Select

两者一般结合使用:
Split 算子将数据流拆分成多个数据流;
Select 算子则选择拆分后你需要的流;
注:该算子已经被废弃,官方推荐使用侧切流(侧边流)

11.Side Output

侧切流,即分流之后再收集,示例:
定义一个 OutputTag 来标识 Side Output,代表这个 Tag 是要收集哪种类型的数据

OutputTag<PileKafkaMessage> ykcOldlunengTag = new OutputTag<PileKafkaMessage>("ykc_old_ln") {
};
OutputTag<PileKafkaMessage> ykcOldShenruiTag = new OutputTag<PileKafkaMessage>("ykc_old") {
};
OutputTag<PileKafkaMessage> ykcNewTag = new OutputTag<PileKafkaMessage>("ykc_new") {
};
OutputTag<PileKafkaMessage> ykcShTag = new OutputTag<PileKafkaMessage>("ykc_sh") {
};
OutputTag<PileKafkaMessage> ykcYkrTag = new OutputTag<PileKafkaMessage>("device_ykr") {
};

定义好OutputTag之后用算子处理,处理过程中将不同类型的数据存到不同的OutputTag 中去:

SingleOutputStreamOperator<PileKafkaMessage> sideOutputData = normalStream.process(new ProcessFunction<PileKafkaMessage, PileKafkaMessage>() {
@Override
 public void processElement(PileKafkaMessage pileKafkaMessage, Context context, Collector<PileKafkaMessage> collector) throws Exception {
     String type = pileKafkaMessage.getProtocolType();
     switch (type) {
         case "ykc_old_ln":
             context.output(ykcOldlunengTag, pileKafkaMessage);
         case "ykc_old":
             context.output(ykcOldShenruiTag, pileKafkaMessage);
         case "ykc_new":
             context.output(ykcNewTag, pileKafkaMessage);
         case "ykc_sh":
             context.output(ykcShTag, pileKafkaMessage);
         case "device_ykr":
             context.output(ykcYkrTag, pileKafkaMessage);
         default:
             collector.collect(pileKafkaMessage);
     }
 }
});

最后用getSideOutput 方法来获取不同 OutputTag 的数据,比如:

DataStream<PileKafkaMessage> ykcOldlunengStream = sideOutputData.getSideOutput(ykcOldlunengTag);
DataStream<PileKafkaMessage> ykcOldShenruiStream = sideOutputData.getSideOutput(ykcOldShenruiTag);
DataStream<PileKafkaMessage> ykcNewStream = sideOutputData.getSideOutput(ykcNewTag);
DataStream<PileKafkaMessage> ykcShStream = sideOutputData.getSideOutput(ykcShTag);
DataStream<PileKafkaMessage> ykcYkrStream = sideOutputData.getSideOutput(ykcYkrTag);

二、DataSet Operator

上面介绍了 DataStream 的常用算子,其实上面也有一些算子也是同样适合于 DataSet 的,比如 Map、FlatMap、Filter 等(相同的就不再重复了);也有一些算子是 DataSet API 独有的,比如 DataStream 中分区使用的是 KeyBy,但是 DataSet 中使用的是 GroupBy。

1.First-n

DataSet<Tuple2<String, Integer>> in = 
// 返回 DataSet 中前 5 的元素
DataSet<Tuple2<String, Integer>> out1 = in.first(5);

// 返回分组后每个组的前 2 元素
DataSet<Tuple2<String, Integer>> out2 = in.groupBy(0)
                                          .first(2);

// 返回分组后每个组的前 3 元素(按照上升排序)
DataSet<Tuple2<String, Integer>> out3 = in.groupBy(0)
                                          .sortGroup(1, Order.ASCENDING)
                                          .first(3);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值