la gestion de l'État Flink

rôle de l'Etat

état Flink est un état d'une tâche de programme de temps / opérateur, des données d'état à un moment de la production de données de fonctionnement . Tout d' abord de faire la distinction entre l' État et le concept de point de contrôle peut être compris comme point de contrôle est de rendre les données d'état stocké de manière permanente, par point de contrôle par défaut JoManager stocké dans la mémoire. emploi Flink un point de contrôle représente un instantané de l'état global d'un moment particulier dans le temps, la récupération de données facile en cas d'échec de la tâche.

stockage de valeur d'état de l' Etat ( le point de contrôle stocké sur HDFS )

env.setStateBackend(new FsStateBackend("hdfs:///user/flink/app_statistics/checkpoint"))

état de checkpoint de stockage de données, l'état de récupération de données au redémarrage

    //设置checkpoint, job失败重启可以恢复数据, 默认是CheckpointingMode.EXACTLY_ONCE
    //flink-conf.yaml配置文件中配置了默认的重启策略: fixed-delay(4, 10s)
    env.enableCheckpointing(60000)
    //不希望因为checkpoint的失败而导致task失败
    env.getCheckpointConfig.setFailOnCheckpointingErrors(false)
    //设置checkpoint的存储管理
    env.setStateBackend(new FsStateBackend("hdfs:///user/flink/app_statistics/checkpoint"))

applications État

  • State-> KeyedState (le plus courant)

KeyedState est basé sur l' état KeyedStream, cet état est de se lier avec une touche particulière et chaque touche a un flux correspondant sur KeyedStream d'Etat . Etat ne peut détrompeur être utilisé sur la base Keystream les Fonctions riches .

Cas I: Flink: État détrompeur, en cherchant à atteindre Monte Carlo simulation Pi

Méthodes de carte rewrite

// 定义一个MonteCarlo类
case class MonteCarloPoint(x: Double, y: Double) {

  def pi = if (x * x + y * y <= 1) 1 else 0
}

object MonteCarko extends App {

// 自定义一个Source,实现随机坐标点的生成
class MonteCarloSource extends RichSourceFunction[MonteCarloPoint] {


  val env = StreamExecutionEnvironment.getExecutionEnvironment


  // state 需要在RichFunction中实现
  val myMapFun = new RichMapFunction[(Long, MonteCarloPoint), (Long, Double)] {

    // 定义原始状态
    var countAndPi: ValueState[(Long, Long)] = _

    override def map(value: (Long, MonteCarloPoint)): (Long, Double) = {

      // 通过 ValueState.value获取状态值
      val tmpCurrentSum = countAndPi.value

      val currentSum = if (tmpCurrentSum != null) {
        tmpCurrentSum
      } else {
        (0L, 0L)
      }

      val allcount = currentSum._1 + 1
      val picount = currentSum._2 + value._2.pi

      // 计算新的状态值
      val newState: (Long, Long) = (allcount, picount)

      // 更新状态值
      countAndPi.update(newState)

      //输出总样本量和模拟极速那的Pi值
      (allcount, 4.0 * picount / allcount)

    }

    override def open(parameters: Configuration): Unit = {
      countAndPi = getRuntimeContext.getState(
        new ValueStateDescriptor[(Long, Long)]("MonteCarloPi", createTypeInformation[(Long, Long)])
      )
    }

  }
    
    // 添加数据源
   val dataStream: DataStream[MonteCarloPoint] = env.addSource(new MonteCarloSource)

  // 转换成KeyedStream
 
   val keyedStream= dataStream.map((1L, _)).keyBy(0)

  // 调用定义好的RichFunction并打印结果
   keyedStream.map(myMapFun).print()

   env.execute("Monte Carko Test")

}

Cas II: site officiel de cas (méthode de réécriture flatmap)

import java.lang

import org.apache.flink.api.common.functions.{RichFlatMapFunction, RichMapFunction}
import org.apache.flink.api.common.state.{ListState, ListStateDescriptor, ValueState, ValueStateDescriptor}
import org.apache.flink.streaming.api.scala.{StreamExecutionEnvironment, _}
import org.apache.flink.util.Collector
import org.apache.flink.configuration.Configuration
import scala.collection.JavaConverters._


class CountWindowAverage extends RichFlatMapFunction[(Long, Long), (Long, Long)] {

  private var sum: ValueState[(Long, Long)] = _

  override def flatMap(input: (Long, Long), out: Collector[(Long, Long)]): Unit = {

    // access the state value
    val tmpCurrentSum = sum.value

    // If it hasn't been used before, it will be null
    val currentSum = if (tmpCurrentSum != null) {
      tmpCurrentSum
    } else {
      (0L, 0L)
    }

    // update the count
    val newSum = (currentSum._1 + 1, currentSum._2 + input._2)

    // update the state
    sum.update(newSum)

    // if the count reaches 2, emit the average and clear the state
    if (newSum._1 >= 2) {
      out.collect((input._1, newSum._2 / newSum._1))
      sum.clear()
    }
  }

  override def open(parameters: Configuration): Unit = {
    sum = getRuntimeContext.getState(
      new ValueStateDescriptor[(Long, Long)]("average", createTypeInformation[(Long, Long)])
    )
  }
}



object ExampleCountWindowAverage extends App {
  val env = StreamExecutionEnvironment.getExecutionEnvironment

  env.fromCollection(List(
    (1L, 3L),
    (1L, 5L),
    (1L, 7L),
    (1L, 4L),
    (1L, 2L)
  )).keyBy(_._1)
    .flatMap(new CountWindowAverage()).print()
  // the printed output will be (1,4) and (1,5)

  env.execute("ExampleManagedState")
}

Cas n ° 3: calculer le plus populaire top3 des produits de base

ProcessFunction est une API de bas niveau Flink fourni pour des fonctions plus avancées. Il fournit principalement la fonction de minuterie minuterie (prise en charge EVENTTIME ou ProcessingTime). Dans ce cas , nous allons utiliser la minuterie pour déterminer quand la réception de toutes les données de trafic pour tous les produits sous une fenêtre. Parce que le progrès Watermark est global, dans la méthode processElement, chaque fois que les données reçoivent une ItemViewCount, nous allons enregistrer une minuterie windowEnd + 1 ( cadre Flink ignorera automatiquement l' enregistrement en double du même temps) . Lorsque la minuterie windowEnd + 1 est déclenché, ce qui signifie reçu windowEnd + filigrane 1, qui est la valeur statistique de tous les biens réception de toutes les fenêtres de la windowEnd. Nous () sont rassemblés dans le processus ontimer toutes sortes de marchandises et le trafic élu TopN, les classements et informations sorties formatées dans une chaîne.

Ici , nous avons également utilisé le ListState <ItemViewCount> ItemViewCount pour stocker chaque message reçu , pour que , en cas d'échec, sans perdre le statut et la cohérence des données. ListState Flink est similaire à celui fourni Java API Liste Interface Etat, qui intègre un poste de contrôle cadre institutionnel fait automatiquement pour assurer la sémantique exactement-une fois de.

import com.sun.jmx.snmp.Timestamp
import org.apache.flink.api.common.functions.AggregateFunction
import org.apache.flink.api.common.state.{ListState, ListStateDescriptor}
import org.apache.flink.api.java.tuple.Tuple
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
import org.apache.flink.api.scala._
import org.apache.flink.configuration.Configuration
import org.apache.flink.streaming.api.functions.KeyedProcessFunction
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala.function.WindowFunction
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
import org.apache.flink.util.Collector

import scala.collection.mutable.ListBuffer


case class UserBehavior(userId: Long, itemId: Long, categoryId: Int,
                        behavior: String, timestamp: Long)
case class ItemViewCount(itemId: Long, windowEnd: Long, count: Long)

object UserBehaviorAnalysis {
  def main(args: Array[String]): Unit = {
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    env.setParallelism(1)

    val value: DataStream[UserBehavior] = env.readTextFile("D:\\projects\\flinkStudy\\src\\userBehavior.csv").
      map(line => {
        val linearray = line.split(",")
        UserBehavior(linearray(0).toLong, linearray(1).toLong, linearray(2).toInt, linearray(3), linearray(4).toLong)
      })
    val watermarkDataStream: DataStream[UserBehavior] = value.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[UserBehavior]
    (Time.milliseconds(1000)) {
      override def extractTimestamp(element: UserBehavior): Long = {
        return element.timestamp
      }
    })
    val itemIdWindowStream: DataStream[ItemViewCount] = watermarkDataStream.filter(_.behavior == "pv").
      keyBy("itemId").
      timeWindow(Time.minutes(60),Time.minutes(5))
      //按照每个窗口进行聚合
      .aggregate(new CountAgg(), new WindowResultFunction())

    itemIdWindowStream.keyBy("windowEnd").process(new TopNHotItems(3)).print()




    env.execute("Hot Items Job")
  }
}

class CountAgg extends AggregateFunction[UserBehavior, Long, Long] {
  override def createAccumulator(): Long = 0L
  override def add(userBehavior: UserBehavior, acc: Long): Long = acc + 1
  override def getResult(acc: Long): Long = acc
  override def merge(acc: Long, acc1: Long): Long = acc1+acc
}

// 用于输出窗口的结果
class WindowResultFunction extends WindowFunction[Long, ItemViewCount, Tuple, TimeWindow] {
  override def apply(key: Tuple, window: TimeWindow, input: Iterable[Long], out: Collector[ItemViewCount]): Unit = {
    var itemId=key.asInstanceOf[Tuple1[Long]]._1
    var count=input.iterator.next()
    out.collect(ItemViewCount(itemId, window.getEnd, count))
  }
}


class TopNHotItems(topSize: Int) extends KeyedProcessFunction[Tuple, ItemViewCount, String] {
  private var itemState : ListState[ItemViewCount] = _

  override def open(parameters: Configuration): Unit = {
    super.open(parameters)
    // 命名状态变量的名字和状态变量的类型
    val itemsStateDesc = new ListStateDescriptor[ItemViewCount]("itemState-state", classOf[ItemViewCount])
    // 定义状态变量
    itemState = getRuntimeContext.getListState(itemsStateDesc)
  }

  override def processElement(input: ItemViewCount, context: KeyedProcessFunction[Tuple, ItemViewCount, String]#Context, collector: Collector[String]): Unit = {
    // 每条数据都保存到状态中
    itemState.add(input)
    // 注册 windowEnd+1 的 EventTime Timer, 当触发时,说明收齐了属于windowEnd窗口的所有商品数据
    // 也就是当程序看到windowend + 1的水位线watermark时,触发onTimer回调函数
    context.timerService.registerEventTimeTimer(input.windowEnd + 1)
  }

  override def onTimer(timestamp: Long, ctx: KeyedProcessFunction[Tuple, ItemViewCount, String]#OnTimerContext, out: Collector[String]): Unit = {
    // 获取收到的所有商品点击量
    val allItems: ListBuffer[ItemViewCount] = ListBuffer()
    import scala.collection.JavaConversions._
    for (item <- itemState.get) {
      allItems += item
    }
    // 提前清除状态中的数据,释放空间
    itemState.clear()
    // 按照点击量从大到小排序
    val sortedItems = allItems.sortBy(_.count)(Ordering.Long.reverse).take(topSize)
    // 将排名信息格式化成 String, 便于打印
    val result: StringBuilder = new StringBuilder
    result.append("====================================\n")
    result.append("时间: ").append(new Timestamp(timestamp - 1)).append("\n")

    for(i <- sortedItems.indices){
      val currentItem: ItemViewCount = sortedItems(i)
      // e.g.  No1:  商品ID=12224  浏览量=2413
      result.append("No").append(i+1).append(":")
        .append("  商品ID=").append(currentItem.itemId)
        .append("  浏览量=").append(currentItem.count).append("\n")
    }
    result.append("====================================\n\n")
    // 控制输出频率,模拟实时滚动结果
    Thread.sleep(1000)
    out.collect(result.toString)
  }
}

 

Publié 159 articles originaux · louange gagné 75 · vues 190 000 +

Je suppose que tu aimes

Origine blog.csdn.net/xuehuagongzi000/article/details/104328893
conseillé
Classement