Flink Operators算子

Operators

运营商将一个或多个DataStream转换为新的DataStream。程序可以将多种转换组合成复杂的数据流拓扑。

本节描述了基本转换,应用这些转换后的有效物理分区以及对Flink运算符链接的见解。

DataStream Transformations

Map

DataStream → DataStream

取一个元素并产生一个元素。一个映射函数,将输入流的值加倍:

DataStream<Integer> dataStream = //...
dataStream.map(new MapFunction<Integer, Integer>() {
    @Override
    public Integer map(Integer value) throws Exception {
        return 2 * value;
    }
});

FlatMap

DataStream → DataStream

取一个元素并产生零个,一个或多个元素。平面图功能可将句子拆分为单词:

dataStream.flatMap(new FlatMapFunction<String, String>() {
    @Override
    public void flatMap(String value, Collector<String> out)
        throws Exception {
        for(String word: value.split(" ")){
            out.collect(word);
        }
    }
});

Filter

DataStream → DataStream

为每个元素评估一个布尔函数,并保留该函数返回true的布尔函数。筛选出零值的筛选器:

dataStream.filter(new FilterFunction<Integer>() {
    @Override
    public boolean filter(Integer value) throws Exception {
        return value != 0;
    }
});

KeyBy

DataStream → KeyedStream

从逻辑上将流划分为不相交的分区。具有相同键的所有记录都分配给同一分区。在内部,keyBy()是通过哈希分区实现的。有多种指定密钥的方法。此转换返回一个KeyedStream,除其他事项外,还需要使用keyed state。

dataStream.keyBy("someKey") // Key by field "someKey"
dataStream.keyBy(0) // Key by the first element of a Tuple

Reduce

KeyedStream → DataStream

对键控数据流进行“滚动”压缩。将当前元素与最后一个减小的值合并,并发出新值。 reduce函数创建部分和流:

keyedStream.reduce(_ + _)
keyedStream.reduce(new ReduceFunction<Integer>() {
    @Override
    public Integer reduce(Integer value1, Integer value2)
    throws Exception {
        return value1 + value2;
    }
});
package com.baizhi.jsy.api
import org.apache.flink.streaming.api.scala._
object FlinkWordCountReduce {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //2.创建DataStream - 细化
    val text = env.socketTextStream("Centos",9999)
    text.flatMap(_.split("\\s+"))
        .map((_,1))
        .keyBy("_1")
        .reduce((v1,v2)=>(v1._1,v1._2+v2._2))
        .print()
    env.execute("Window Stream WordCount")
  }
}

Union

DataStream* → DataStream

两个或多个数据流的并集,创建一个包含所有流中所有元素的新流。注意:如果将数据流与其自身并集,则在结果流中每个元素将获得两次。

dataStream.union(otherStream1, otherStream2, ...);	
package com.baizhi.jsy.api
import org.apache.flink.streaming.api.scala._
object FlinkWordCountUnionStream {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //2.创建DataStream - 细化
    val text1 = env.socketTextStream("Centos",9999)
    val text2 = env.socketTextStream("Centos",8888)
    //流连接
    text1.union(text2)
      //两个数据类型需要保持一致
      .flatMap((line:String)=>line.split("\\s+"))
        .map((_,1))
        .keyBy(0)
        .sum(1)
        .print("总数")   
    env.execute("Window Stream WordCount")
  } 
}

Connect

DataStream,DataStream → ConnectedStreams

“连接”两个保留其类型的数据流。连接允许两个流之间共享状态。

DataStream<Integer> someStream = //...
DataStream<String> otherStream = //...

ConnectedStreams<Integer, String> connectedStreams = someStream.connect(otherStream);

CoMap, CoFlatMap

ConnectedStreams → DataStream

与连接的数据流上的map和flatMap相似

connectedStreams.map(
 (_ : Int) => true,
 (_ : String) => false
)
connectedStreams.flatMap(
 (_ : Int) => true,
 (_ : String) => false
)
package com.baizhi.jsy.api
import org.apache.flink.streaming.api.scala._
object FlinkWordCountConnectionStream {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //2.创建DataStream - 细化
    val text1 = env.socketTextStream("Centos",9999)
    val text2 = env.socketTextStream("Centos",8888)
    //流连接
    text1.connect(text2)
      //connect的两个数据类型可以不一致
      .flatMap((line:String)=>line.split("\\s+"),(line:String)=>line.split("\\s+"))
        .map((_,1))
        .keyBy(0)
        .sum(1)
        .print("总数")    
    env.execute("Window Stream WordCount")
  }
}

Split

DataStream → SplitStream

根据某种标准将流分成两个或多个流

SplitStream<Integer> split = someDataStream.split(new OutputSelector<Integer>() {
    @Override
    public Iterable<String> select(Integer value) {
        List<String> output = new ArrayList<String>();
        if (value % 2 == 0) {
            output.add("even");
        }
        else {
            output.add("odd");
        }
        return output;
    }
});
package com.baizhi.jsy.api
import org.apache.flink.streaming.api.scala._
object FlinkWordCountSplit {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //2.创建DataStream - 细化
    val text1 = env.socketTextStream("Centos",9999)
    val splitStream = text1.split(line => {
      if (line.contains("error")) {
        List("error")
      } else {
        List("info")
      }
    })
    splitStream.select("error").printToErr("错误")
    splitStream.select("info").print("信息")
    splitStream.select("error","info").print("All")    
    env.execute("Window Stream WordCount")
  }
}

在这里插入图片描述

ProcessFunction

一般的来说,更多的使用ProcessFunction完成流的分支

package com.baizhi.jsy.api
import org.apache.flink.streaming.api.functions.ProcessFunction
import org.apache.flink.streaming.api.scala._
import org.apache.flink.util.Collector
object FlinkWordCountProcess {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //2.创建DataStream - 细化
    val text = env.socketTextStream("Centos",9999)
    val errorTag = new OutputTag[String]("error")
    val allTag = new OutputTag[String]("all")
    val infoStream = text.process(new ProcessFunction[String, String] {
      override def processElement(i: String,
                                  context: ProcessFunction[String, String]#Context,
                                  collector: Collector[String]): Unit = {
        if (i.contains("error")) {
          context.output(errorTag, i) //边输出
        } else {
          collector.collect(i) //正确信息正常输出
        }
        context.output(allTag, i)
      }
    })
    infoStream.getSideOutput(errorTag).printToErr("错误")
    infoStream.getSideOutput(allTag).printToErr("所有")
    infoStream.print("正常")
    env.execute("Window Stream WordCount")
  }
}

Select

SplitStream → DataStream

从拆分流中选择一个或多个流。

SplitStream<Integer> split;
DataStream<Integer> even = split.select("even");
DataStream<Integer> odd = split.select("odd");
DataStream<Integer> all = split.select("even","odd");

Fold

KeyedStream → DataStream

带有初始值的键控数据流上的“滚动”折叠。将当前元素与上一个折叠值组合在一起并发出新值。 折叠函数,应用于序列(1,2,3,4,5)时,会发出序列“ start-1”,“ start-1-2”,“ start-1-2-3”,…。 …

DataStream<String> result =
  keyedStream.fold("start", new FoldFunction<Integer, String>() {
    @Override
    public String fold(String current, Integer value) {
        return current + "-" + value;
    }
  });
package com.baizhi.jsy.api
import org.apache.flink.streaming.api.scala._
object FlinkWordCountFold {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //2.创建DataStream - 细化
    val text = env.socketTextStream("Centos",9999)
    text.flatMap(_.split("\\s+"))
        .map((_,1))
        .keyBy("_1")
         .fold((null:String,0))((z,v)=>(v._1:String,v._2+z._2))
        .print()
    env.execute("Window Stream WordCount")
  }
}

Aggregations

聚合窗口的内容。min和minBy之间的区别是min返回最小值,而minBy返回在此字段中具有最小值的元素(与max和maxBy相同)。

    windowedStream.sum(0);
    windowedStream.sum("key");
    windowedStream.min(0);
    windowedStream.min("key");
    windowedStream.max(0);
    windowedStream.max("key");
    windowedStream.minBy(0);
    windowedStream.minBy("key");
    windowedStream.maxBy(0);
    windowedStream.maxBy("key");
package com.baizhi.jsy.api
import org.apache.flink.streaming.api.scala._
object FlinkWordCountAggregate {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //2.创建DataStream - 细化
    val text = env.socketTextStream("Centos",9999)
    text.flatMap(_.split("\\s+"))
        .map((_,1))
        .keyBy("_1")
      .sum("_2")
        .print()
    env.execute("Window Stream WordCount")
  }
}

max

package com.baizhi.jsy.api
import org.apache.flink.streaming.api.scala._
case class Employee(name:String,dept:String,salary:Double)
object FlinkWordCountMax {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //zhangsan 研发部 1000
    //lisi 研发部 5000
    //wangwu 销售部 8000
    //2.创建DataStream - 细化
    val text = env.socketTextStream("Centos",9999)
    text.map(_.split(" "))
      .map(ts=>Employee(ts(0),ts(1),ts(2).toDouble))
      .keyBy("dept")
      .max("salary")//如果使⽤时max,则返回的是Emp(zhangsan,研发部,5000.0)
      //.maxBy("salary")//Emp(lisi,研发部,5000.0)
      .print()
    env.execute("Window Stream WordCount")
  }
}

Physical partitioning

Flink还通过以下功能对转换后的DataStream分区进行了低级控制(如果需要)。

(flink.apache.org/visualizer/)

package com.baizhi.jsy.physical
import org.apache.flink.streaming.api.scala._
object FlinkWordCountPartition {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //2.创建DataStream - 细化
    env.socketTextStream("Centos",9999)
        .print()
        .setParallelism(2)
    println(env.getExecutionPlan)
    env.execute("Window Stream WordCount")
  } 
}

在这里插入图片描述

Rebalancing (Round-robin partitioning):

分区元素轮循,从⽽为每个分区创建相等的负载。在存在数据偏斜的情况下对性能优化有⽤。

package com.baizhi.jsy.physical
import org.apache.flink.streaming.api.scala._
object FlinkWordCountPartition {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //2.创建DataStream - 细化
    env.socketTextStream("Centos",9999)
      .flatMap(_.split("\\s+"))
      .setParallelism(4)
      .rescale
      .map((_,1))
      .setParallelism(2)
      .print()
      .setParallelism(2)
    println(env.getExecutionPlan)
    env.execute("Window Stream WordCount")
  }
}

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

Custom partitioning

Selects a subset of fields from the tuples

dataStream.partitionCustom(partitioner, "someKey")
dataStream.partitionCustom(partitioner, 0)
package com.baizhi.jsy.physical
import org.apache.flink.api.common.functions.Partitioner
import org.apache.flink.streaming.api.scala._
object FlinkWordCountPartitionCustom {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //2.创建DataStream - 细化
    env.socketTextStream("Centos",9999)
      .map((_,1))
      .partitionCustom(new Partitioner[String] {
        override def partition(key: String, numPartitions: Int): Int = {
          key.hashCode & Integer.MAX_VALUE % numPartitions
        }
      },_._1)
        .print()
    println(env.getExecutionPlan)
    env.execute("Window Stream WordCount")
  }
}

在这里插入图片描述

Task chaining and resource groups

链接两个后续转换意味着将它们共同定位在同一线程内以提高性能。如果可能的话,默认情况下Flink链接运算符(例如,两个后续的映射转换)。如果需要,API可以对链接进行细粒度的控制:

StreamExecutionEnvironment.disableOperatorChaining()如果要在整个作业中禁用链接,请使用。为了获得更精细的控制,可以使用以下功能。请注意,这些函数只能在DataStream转换后使用,因为它们引用的是先前的转换。例如,您可以使用someStream.map(…).startNewChain(),但不能使用someStream.startNewChain()。

资源组是Flink中的插槽,请参阅 slot。如果需要,您可以手动将操作员隔离在单独的插槽中。

env.disableOperatorChaining()//禁用chain行为,但是不推荐。

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

startNewChain

someStream.filter(…).map(…).startNewChain().map(…)

package com.baizhi.jsy.chains
import org.apache.flink.streaming.api.scala._
object FlinkWordCountChains1 {
  def main(args: Array[String]): Unit = {
    //1.创建流计算执⾏行行环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    //env.disableOperatorChaining()
    //2.创建DataStream - 细化
    val text = env.socketTextStream("Centos",9999)
    //3.执⾏行行DataStream的转换算⼦子
    val counts = text.flatMap(line=>line.split("\\s+"))
      .map(word=>(word,1))
      .startNewChain()
      .keyBy(0)
      .sum(1)
    //4.将计算的结果在控制打印
    counts.print()
    //5.执⾏行行流计算任务
    env.execute("Window Stream WordCount")
  }
}

在这里插入图片描述

将第一map算子和filter算子进行隔离

disableChaining

someStream.map(...).disableChaining()

slotSharingGroup

设置操作的slot共享组。 Flink会将具有相同slot共享组的operator放在同一个Task slot中,同时将没有slot

共享组的operator保留留在其他Task slot中。这可以用来隔离Task Slot。下游的操作符会自动继承上游资源组。默认情况下,所有的输入算子的资源组的名字是 default ,因此当用户不对程序进行资源划分的情况下,一个job所需的资源slot,就等于最大并行度的Task。

someStream.filter(...).slotSharingGroup("name")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值