本篇文章简单介绍一下Flink中的数据类型与累加器以及计数器的使用。
一、数据类型
在Flink的DataSet以及DataStream中对可使用的元素添加了一些约束条件,目的是为了能够有效的分析这些类型的执行策略以及选择不同的序列化方式。
以下列举7种Flink种常用的数据类型:
- Java/Scala Tuple
- Java POJO和Scala case class
- 基本类型(Integer,Double等)
- 通用类
- value值
- Hadoop Writables
- 特殊类型
Tuple类型
它是包含固定数量各种类型字段的复合类。在Flink中总共提供了Tuple1-Tuple25(后面的数字代表字段的数量),可以通过下面两种方式来访问tuple属性:
- 属性名(f0,f1…)
- getFiled(int pos)
简单使用如下:
Java pojo类
什么是pojo类,可以这样说,一个pojo类没有继承任何类,就一个单一的类,它的属性必须是public或者private,但是需要提供相应的get与set方法,如有多个参数必须显式的给出它的无参构造器。
比如下面是一个简单的pojo类:
public static class WC{
Integer freq;
String word;
public WC() {
}
public WC(Integer freq, String word) {
this.freq = freq;
this.word = word;
}
public Integer getFreq() {
return freq;
}
public void setFreq(Integer freq) {
this.freq = freq;
}
public String getWord() {
return word;
}
public void setWord(String word) {
this.word = word;
}
@Override
public String toString() {
return "WC{" +
"freq=" + freq +
", word='" + word + '\'' +
'}';
}
}
在Flink中可以这样使用:
DataStream<WC> input=env.fromElements(new WC(1,"HELLO"));
input.keyBy("word");
scala case类
它实质上和java的pojo类差不多,具体使用如下:
case class WordCount(word: String, count: Int)
val input = env.fromElements( WordCount("hello", 1), WordCount("world", 2))
input.keyBy("word")
val input2 = env.fromElements(("hello", 1), ("world", 2))
input2.keyBy(0, 1)
基本类型
当然,flink也支持java以及scala中的所有基本数据类型,包括Integer,Long,Double等等。
一般通用类
- Flink支持大多数的Java,Scala类(API和自定义)。包含不能序列化字段的类在增加一些限制后也可支持。遵循Java Bean规范的类一般都可以使用。
- 所有不能视为POJO的类Flink都会当做一般类处理。这些数据类型被视作黑箱,其内容是不可见的。通用类使用Kryo进行序列/反序列化。
值类型Values
Flink也支持和Hadoop中类型的Value类型,具体说明如下:
- 通过实现org.apache.flinktypes.Value接口的read和write方法提供自定义代码来进行序列化/反序列化,而不是使用通用的序列化框架。
- Flink预定义的值类型与原生数据类型是一一对应的(例如:ByteValue, ShortValue, IntValue,
LongValue, FloatValue, DoubleValue, StringValue, CharValue, BooleanValue)。这些值类型作为原生数据类型的可变变体,他们的值是可以改变的,允许程序重用对象从而缓解GC的压力。
特殊类型
除了上面介绍的几种数据类型外,Flink还提供了一些特殊的类,它们并不常使用。如下所示:
- Scala的 Either、Option和Try
- Java ApI也有自己的Either实现
二、序列化
在Flink中,它以一种独特的方式去处理序列化和数据类型,包括自己的类型描述器,泛型抽取和类型序列化框架。
在一般情况下,flink会试图自动推断分布式计算过程中的交换和存储数据的类型的信息,在大多数情况下,Flink会自动推断出必要的信息。
一般情况下,Flink会首先进行类型推断,然后使用Flink自带的类型系统进行序列化和反序列化,如果不行,会使用Kryo
的方式去处理,这种方式也处理不了的话就会使用其他方式,比如Arvo等方式。
三、累加器Accumulator和计数器Counter
Flink和Spark一样,都提供了累加器供我们使用,它们大多用于一些计数,计算一些指标的场景。
计数器也是一种累加器,它是最简单的累加器,作计数功能使用。在Flink类部,内置了很多计数器,比如IntCounter,LongCounter和DoubleCounter等等。
那么如何使用累加器呢?主要分为下面的几部:
- 第一步:在自定义的转换操作里创建累加器对象:
private IntCounter numLines = new IntCounter();
- 第二步:注册累加器对象,通常是在
rich function的open()
方法中。这里你还需要定义累加器的名字getRuntimeContext().addAccumulator(“num-lines”, this.numLines);
- 第三步:在operator函数的任何地方使用累加器,包括在open()和close()方法中
this.numLines.add(1);
- 第四步:结果存储在JobExecutionResult里:
JobExecutionResult jobExecutionResult =env.execute("Accumulator"); jobExecutionResult .getAccumulatorResult("num-lines");
一个简单示例如下:
public class CounterFunction extends RichFlatMapFunction{
private IntCounter numLines=new IntCounter();
@Override
public void open(Configuration parameters) throws Exception {
getRuntimeContext().addAccumulator("num-lines", this.numLines);
super.open(parameters);
}
@Override
public void close() throws Exception {
super.close();
}
@Override
public void flatMap(Object o, Collector collector) throws Exception {
}
}
接着在执行execute方法的时候拿到其返回值,就可以获取计数器的结果了:
JobExecutionResult jobExecutionResult =env.execute("Accumulator");
jobExecutionResult .getAccumulatorResult("num-lines");
如果我们需要在更加复杂的场景下用到累加器,可以实现自定义累加器,通过实现Accumulator
接口或者SimpleAccumulator
接口即可。