如何基于状态变化事件以分布式方式计算有多少“客户端”处于flink状态?我需要有状态的对象

时间:2016-06-15 09:45:50

标签: java apache-flink flink-streaming stream-processing

我正在使用kafka开发java中的poc项目 - > flink - >弹性搜索。

在kafka上将会产生不可预测数量的事件,从0到数千事件/秒,例如特定主题。

{"gid":"abcd-8910-2ca4227527f9", "state":"stateA", "timestamp:1465566255, "other unusefull info":"..."} 

Flink将消耗此事件,并应每秒吸收弹性搜索每个州的事件数量ex:

{"stateA":54, "stateB":100, ... "stateJ":34}

我有10个州:[Created, ... , Deleted],平均生命周期为15分钟。国家每秒可以改变两次。理论上可以添加新的州。

为了每秒吸收一次流,我正在考虑使用flink的时间窗https://flink.apache.org/news/2015/12/04/Introducing-windows.html

问题是我需要有关guid->previous-statestateX->count信息的有状态对象,以便能够在新事件发生时递增/递减计数。

我找到了有关有状态蒸汽处理的草稿文件https://cwiki.apache.org/confluence/display/FLINK/Stateful+Stream+Processing

我是flink和stream处理的新手,我还没有深入研究flink状态流处理。对于第一阶段,我正在考虑使用静态对象,但是当几个flink实例将被启动时,这种方法将不起作用。

我想问你:

  1. 您如何看待这种方法?
  2. flink适合这种流处理吗?
  3. 解决这个问题的方法是什么?
  4. 此外,我还要了解窗口状态流解决方案(或其他解决方案)的一些代码段。

    谢谢,

1 个答案:

答案 0 :(得分:1)

如下所示:

它使用15分钟的窗口,之后将清理窗口状态。它还使用自定义触发器每秒评估一次窗口。就窗口操作而言,有一个ReduceFunction,它只保留每个guid的最新状态,以及一个发出(state,1)元组的WindowFunction。然后我们按照这个状态键入并总结。我认为这应该会给你你正在寻找的结果。

val env = StreamExecutionEnvironment.getExecutionEnvironment()
val stream = env.addSource(new FlinkKafkaProducer(...))

val results = stream
  .keyBy(_.guid)
  .timeWindow(Time.minutes(15))
  .trigger(ProcessingTimeTriggerWithPeriodicFirings(1000))
  .apply(
    (e1, e2) => e2,
    (k, w, i, c: Collector[(String, Long)]) => {
      if (i.head != null) c.collect((i.head.state, 1))
    }
  )
  .keyBy(0)
  .timeWindow(Time.seconds(1))
  .sum(1)
  .addSink(new ElasticsearchSink<>(...))

env.execute("Count States")

ProcessingTimeTriggerWithPeriodicFirings定义如下:

object ProcessingTimeTriggerWithPeriodicFirings {
  def apply(intervalMs: Long) = {
    new ProcessingTimeTriggerWithPeriodicFirings(intervalMs)
  }
}

class ProcessingTimeTriggerWithPeriodicFirings(intervalMs: Long)
  extends Trigger[Event, TimeWindow] {

  private val startTimeDesc =
    new ValueStateDescriptor[Long]("start-time", classOf[Long], 0L)

  override def onElement(element: Event, timestamp: Long, window: TimeWindow, ctx: TriggerContext): TriggerResult = {
    val startTime = ctx.getPartitionedState(startTimeDesc)
    if (startTime.value == 0) {
      startTime.update(window.getStart)
      ctx.registerProcessingTimeTimer(window.getEnd)
      ctx.registerProcessingTimeTimer(System.currentTimeMillis() + intervalMs)
    }
    TriggerResult.CONTINUE
  }

  override def onProcessingTime(time: Long, window: TimeWindow, ctx: TriggerContext): TriggerResult = {
    if (time == window.getEnd) {
      TriggerResult.PURGE
    }
    else {
      ctx.registerProcessingTimeTimer(time + intervalMs)
      TriggerResult.FIRE
    }
  }

  override def onEventTime(time: Long, window: TimeWindow, ctx: TriggerContext): TriggerResult = {
    TriggerResult.CONTINUE
  }
}