一 aggregate()函數
Flink 的AggregateFunction是一個基於中間計算結果狀態進行增量計算的函數。由於是迭代計算方式,所以,在窗口處理過程中,不用緩存整個窗口的數據,所以效率執行比較高。
該函數會將給定的聚合函數應用於每個窗口和鍵。 對每個元素調用聚合函數,以遞增方式聚合值,並將每個鍵和窗口的狀態保持在一個累加器中。
def aggregate[ACC: TypeInformation, R: TypeInformation](
aggregateFunction: AggregateFunction[T, ACC, R]): DataStream[R] = {
val accumulatorType: TypeInformation[ACC] = implicitly[TypeInformation[ACC]]
val resultType: TypeInformation[R] = implicitly[TypeInformation[R]]
asScalaStream(javaStream.aggregate(
clean(aggregateFunction), accumulatorType, resultType))
}
參數類型:AggregateFunction接口。該接口的繼承關係和方法如下:
AggregateFunction需要複寫的方法有:
- createAccumulator:創建一個新的累加器,開始一個新的聚合。累加器是正在運行的聚合的狀態。
- add:將給定的輸入添加到給定的累加器,並返回新的累加器值。
- getResult:從累加器獲取聚合結果。
- merge:合併兩個累加器,返回合併後的累加器的狀態。
二 案例
從SocketSource接收數據,時間語義採用ProcessingTime,通過Flink 時間窗口以及aggregate方法統計用戶在24小時內的平均消費金額。
代碼
package org.ourhome.streamapi
import org.apache.flink.api.common.functions.AggregateFunction
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time
/**
* @Author Do
* @Date 2020/4/24 22:51
*/
object WindowFunctionAggrectionTest {
def main(args: Array[String]): Unit = {
val env: StreamExecutionEnvironment = StreamExecutionEnvironment.getExecutionEnvironment
env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime)
env.setParallelism(1)
val socketData: DataStream[String] = env.socketTextStream("local", 9999)
socketData.print("input ")
socketData.map(line => {
ConsumerMess(line.split(",")(0).toInt, line.split(",")(1).toDouble)
})
.keyBy(_.userId)
.timeWindow(Time.hours(24))
.aggregate(new MyAggregrateFunction)
.print("output ")
env.execute()
}
case class ConsumerMess(userId:Int, spend:Double)
//<IN> The type of the values that are aggregated (input values)
//<ACC> The type of the accumulator (intermediate aggregate state).
//<OUT> The type of the aggregated result
class MyAggregrateFunction extends AggregateFunction[ConsumerMess, (Int, Double), Double] {
override def createAccumulator(): (Int, Double) = (0, 0)
override def add(value: ConsumerMess, accumulator: (Int, Double)): (Int, Double) = {
(accumulator._1 + 1, accumulator._2 + value.spend)
}
override def getResult(accumulator: (Int, Double)): Double = {
accumulator._2/accumulator._1
}
override def merge(a: (Int, Double), b: (Int, Double)): (Int, Double) = {
(a._1 + b._1, b._2 + a._2)
}
}
}
輸入
nc -lk 9999
123,666
123,456
123,12
123,3
123,46
123,666
輸出
input > 123,666
input > 123,456
output > 561.0
input > 123,12
input > 123,3
input > 123,46
input > 123,666
output > 181.75
根據輸出可見:
- 第一個窗口內,也就是第一個24小時,用戶“123”共有兩次消費(input開頭),第一次花費666元,第二次花費456元。在窗口觸發並關閉後,統計出平均每次消費金額:(666+456)/2=561.0元。
- 第二個窗口內,也就是第二個24小時,用戶“123”共有4次消費(input開頭),每次消費分別爲:12、3、46、666元。在窗口觸發並關閉後,統計出平均每次消費金額:(12+3+46+666)/4=181.75元。