文章目录
1. 基本转换算子
1.1 映射 MAP
1.1.1 自定义mapper
自定义类mapper,实现MapFunction接口
public static class MyMapper implements MapFunction<Event, String > {
@Override
public String map(Event value) throws Exception {
return value.user;
}
}
对source进行转换:
SingleOutputStreamOperator<String> result = stream.map(new MyMapper());
1.1.2 匿名类
//匿名类
SingleOutputStreamOperator<String> result2 = stream.map(new MapFunction<Event, String>() {
@Override
public String map(Event event) throws Exception {
return event.user;
}
});
1.1.3 lambda表达式
//3.lambda表达式
SingleOutputStreamOperator<String> result3 = stream.map(data -> data.user);
result3.print();
1.2 过滤 filter
1.2.1 自定义filter 方法
public static class MyFilter implements FilterFunction<Event>{
@Override
public boolean filter(Event event) throws Exception {
return event.user.equals("GENGYUNZHE");
}
}
1.2.2 匿名类
// 2. 匿名类
SingleOutputStreamOperator<Event> result2 = stream.filter(new FilterFunction<Event>() {
@Override
public boolean filter(Event event) throws Exception {
return event.user.equals("GENGYUNZHE");
}
});
1.2.3 LAMBDA表达式
// 3. lambda表达式
SingleOutputStreamOperator<Event> result3 = stream.filter(data -> data.user.equals("GENGYUNZHE"));
1.3 flatMap 扁平映射
1.3.1 自定义方法
数据流的整体拆开打散,每个元素map转换,不是一对一的,可一对多。
public static class MyFlatMap implements FlatMapFunction<Event,String>{
@Override
public void flatMap(Event o, Collector<String> out) throws Exception {
out.collect(o.user);
out.collect(o.url);
out.collect(o.timestamp.toString());
}
}
1.3.2 lambda表达式
//2 lambda
stream.flatMap((Event value,Collector<String> out) ->{
if (value.user.equals("GENGYUNZHE")){
out.collect(value.user);
}
else {
out.collect(value.user);
out.collect(value.url);
out.collect(value.timestamp.toString());
}
}).returns(new TypeHint<String>() {}).print();
1.3.3 泛型擦除
2. 聚合算子 aggregation
2.1 按键分区(keyby)
2.1.1 简单聚合
- max返回最大值
- maxBy 把最大值对应的元素全部返回
// 按键分组后聚合,提取最近一次访问的用户
stream.keyBy(new KeySelector<Event, String>() {
@Override
public String getKey(Event event) throws Exception {
return event.user;
}
}).max("timestamp").print("max:");
流式处理,来一条处理一条。
lambda表达式
stream.keyBy(data -> data.user).maxBy("timestamp").print();
2.1.2 规约聚合 reduce
当前最活跃用户
//1.统计每个用户的访问频次
SingleOutputStreamOperator<Tuple2<String, Long>> clickByUser = stream.map(new MapFunction<Event, Tuple2<String, Long>>() {
@Override
public Tuple2<String, Long> map(Event event) throws Exception {
return Tuple2.of(event.user, 1L);
}
}).keyBy(data -> data.f0).reduce(new ReduceFunction<Tuple2<String, Long>>() {
@Override
public Tuple2<String, Long> reduce(Tuple2<String, Long> stringLongTuple2, Tuple2<String, Long> t1) throws Exception {
return Tuple2.of(stringLongTuple2.f0, stringLongTuple2.f1 + t1.f1);
}
});
// 2.根据当前个数选取最活跃用户
SingleOutputStreamOperator<Tuple2<String, Long>> result = clickByUser.keyBy(data -> "key").reduce(new ReduceFunction<Tuple2<String, Long>>() {
@Override
public Tuple2<String, Long> reduce(Tuple2<String, Long> stringLongTuple2, Tuple2<String, Long> t1) throws Exception {
return stringLongTuple2.f1 > t1.f1 ? stringLongTuple2 : t1;
}
});
3. UDF 自定义函数
flink datastream API编程风格一致的:基于datastream调用一个方法:表示转换操作,方法需要传参,都需要实现一个接口——
源算子需要实现 sourcefunction接口, map算子需要实现 mapfunction接口
reduce算子需要实现reducefunction接口。
SAM 抽象方法接口:
- 函数类
- 匿名类
- lambda表达式
- 富函数类
a. 获取运行环境的上下文,生命周期方法(对于并行子任务只会调用一次),实际工作方法,每一条数据都会触发一次。
b. open() close()
c.因为生命周期方法等,无法使用lambda表达式
//自定义富函数类
public static class MyRichMapper extends RichMapFunction<Event, Integer>{
@Override
public void open(Configuration parameters) throws Exception {
super.open(parameters);
System.out.println("open生命周期调用"+ getRuntimeContext().getIndexOfThisSubtask()+"号任务启动");
}
@Override
public void close() throws Exception {
super.close();
System.out.println("open生命周期调用"+ getRuntimeContext().getIndexOfThisSubtask()+"号任务结束");
}
@Override
public Integer map(Event event) throws Exception {
return event.url.length();
}
}
4. 物理分区
4.1 轮询分区
keyby是逻辑分区,物理分区控制分区策略,将流中的数据重新分配,
应用场景:数据倾斜,负载不够均衡
//1. shuffle随机分区,均匀分布
stream.shuffle().print().setParallelism(4);
//2. 轮询分区(顺序1234,打印顺序不是)
stream.rebalance().print().setParallelism(4);
4.2 rescale 重缩放分区
上游两个并行子任务,下游4个并行子任务,分成几组在组内进行轮询发牌
优点:相较于rebalance,避免了跨 taskmanager减少网络传输消耗。rebalance上下游会建立笛卡尔积链接。
rebalance()和rescale()的根本区别在于任务之间连接的机制不同。rebalance()将会针对所有发送者任务和所有接收者任务之间建立通信通道,而rescale()仅仅针对每一个任务和下游算子的一部分子并行任务之间建立通信通道。
// 3. rescale重缩放分区
env.addSource(new RichParallelSourceFunction<Integer>() {
@Override
public void run(SourceContext<Integer> sourceContext) throws Exception {
for (int i=0;i<10;i++){
//子任务索引号发对应奇偶数到并行分区
if(i % 2==getRuntimeContext().getIndexOfThisSubtask()){
sourceContext.collect(i);
}
}
}
@Override
public void cancel() {
}
}).setParallelism(2).rescale().print().setParallelism(4);
4.3 广播
把当前的一个数据分发到下游所有的并行子任务。
并行度4
stream.broadcast().print().setParallelism(4);
每个并行子任务都会执行
4.4 全局分区
作用:将所有的输入流数据都发送到下游算子的第一个并行任务中去。这个操作需要很谨慎,因为将所有数据发送到同一个task,将会对应用程序造成很大的压力。
stream.global().print().setParallelism(4);
并行度失效。
4.5 自定义重分区
作用:使用自定义分区策略实现分区逻辑以及定义针对流的哪个字段或者key进行分区。