Lagom。限制外部服务

时间:2017-11-14 18:37:12

标签: scala lagom akka-typed

我正在玩Lagom,它看起来很不错,但我完全迷失了一个问题。

让我说我依赖外部HTTP服务,它只允许10个请求/秒,在其他情况下甚至可以禁止:) 我用Google搜索,但没有找到任何有效的例子。我可以在无类型的actor中包装服务并为其添加速率限制器,但我不明白如何在Akka Typed或Lagom服务之上实现它。

也许有人已经解决了这个问题?谢谢!

1 个答案:

答案 0 :(得分:3)

你想要一个token bucket。 Akka-Streams内置了Flow.throttle,但听起来你正在使用原始akka,所以不能使用它。存在token bucket implementation in Akka,但不幸的是它没有给出任何使用指导,我自己也没有使用它。

对于我自己的用例(不是Akka但是使用Scala Futures)我编写了自己的令牌桶。它允许我根据指定的限制引用Future的触发。它是针对monix调度程序编码的,但为此目的与Akka计划非常相似:

import java.util.concurrent.ConcurrentLinkedQueue

import monix.execution.Scheduler.Implicits.global
import monix.execution.atomic.AtomicInt

import scala.concurrent.{Future, Promise}
import scala.concurrent.duration._

case class RateLimiter(duration: FiniteDuration, maxInvocations: Int) {

  @volatile var permits: Int = maxInvocations
  val queue = new ConcurrentLinkedQueue[() => Any]()

  global.scheduleAtFixedRate(duration, duration) {
    this synchronized {
      permits = maxInvocations

      while (!queue.isEmpty && permits > 0) {
        Option(queue.poll()).foreach { fun =>
          permits -= 1
          fun.apply()
        }
      }
    }
  }

  def apply[T](f: => Future[T]): Future[T] =
    this synchronized {
      if (permits > 0) {
        permits -= 1
        f
      } else {
        val res = Promise[T]()
        queue.add(() => { res.completeWith(f) })
        res.future
      }
    }
}

用法是

val limiter = RateLimiter(1.second, 10)

limiter {
  someWebService.asyncCall()
}