如何使用ttl管理akka流的状态

时间:2017-09-06 11:40:08

标签: akka akka-stream

我正在尝试使用akka流来收听sqs,我从q获取消息 使用此代码段:

implicit val system = ActorSystem()
implicit val mat = ActorMaterializer()
implicit val ec = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(ConfigUtils.dtsConfiguration.ioThreadPoolSize))
val awsSqsClient: AmazonSQSAsync = AmazonSQSAsyncClientBuilder
  .standard()
  .withCredentials(new ClasspathPropertiesFileCredentialsProvider())
  .withEndpointConfiguration(new EndpointConfiguration(sqsEndpoint, ConfigUtils.dtsConfiguration.regionName))
  .build()

val future = SqsSource(sqsEndpoint)(awsSqsClient)
  .takeWhile(_ => true)
  .mapAsync(parallelism = 2)(m => {
    val msgBody = SqsMessage.deserializeJson(m.getBody)
    msgBody match {
      case Right(body) =>  //for each stream add (body.ID,body.Record.FileContent) or concatenate the new fileContent
                           // with current map (of same id) 
                           // that for each key in the map - if the filecontent size > 100 kb remove the relevant tuple from 
                         the map and perform an operation on it 
    }
    Future(m, Ack())
  })
  .to(SqsAckSink(sqsEndpoint)(awsSqsClient))
  .run()

我已经评论了我需要操作流的代码中的特定点。

我需要的是从sqs获取的每个记录执行此操作:

我想将它的内容放入Int [Int,String],Int代表键,字符串是记录内容。 (对于其他键,我将连接它的内容,直到它的大小超过1kb)

(这就像保存每个键的状态一样)。

然后我想做以下事情:

我想为每个元组执行一个操作(不断地使用流) 当它的内容大小> 1kb,然后将其从地图中删除。

我还需要一个ttl,用于那些没有在地图中更新太空时间的记录,例如30秒。

是否可以使用akka流完成?

感谢。

1 个答案:

答案 0 :(得分:0)

Flow.statefulMapConcat是一种使用swizz-army-knife的东西,允许你保持状态和条件下游值。如果你很高兴只在元素到达时丢弃TTL,这也是可行的,但是通过实现自定义GraphStage,有一个嘀嗒触发驱逐有点棘手,可能会更容易。

这是一个简化的示例,它将累积每个键的值,直到达到限制然后向下游发射。

import scala.collection.immutable.Iterable
val theThing: Flow[(String, Int), (String, Int), NotUsed] =
  Flow[(String, Int)]
    .statefulMapConcat { () =>
      // state kept in factory function scope
      var state = Map[String, Int]()

      // for each incoming tuple
      {
        case (key, value) =>
          val newValueForKey = state.getOrElse(key, 0)

          // ... evicting old elements could go here ...

          if (newValueForKey > 10) {
            // max size, emit something downstream
            state = state - key
            Iterable(key -> newValueForKey)
          } else {
            // just update state, don't emit anything
            state = state + (key -> newValueForKey)
            Iterable.empty
          }
      }
    }

要做到这一点,你必须以某种方式引入一个tick元素,但这可能也会受到背压的影响,因此可以返回到自定义的GraphStage。 GraphStages有一个计时器API,允许他们将滴答作为不受背压影响的侧通道。您可以在Akka文档的这一部分中找到有关如何实现此类的详细信息:http://doc.akka.io/docs/akka/current/scala/stream/stream-customize.html#custom-linear-processing-stages-using-graphstage