Flink——原理與實戰:AggregateFunction

一 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元。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章