Improve the Throttle for Akka(1)Local Cache
1 Basic Idea of Akka
Start the Akka System, send messages.
package com.sillycat.throttle.demo
import akka.actor.{Actor, ActorSystem, Props}
class HelloActor extends Actor{
def receive = {
case "hello" => {
println("hello!")
}
case _ => {
println("huh?")
}
}
}
object HelloApp extends App {
val system = ActorSystem("HelloSystem")
system.actorOf(Props[HelloActor], name="test1")
system.actorOf(Props[HelloActor], name="test2")
system.actorOf(Props[HelloActor], name="test3")
// implicit val resolveTimeout = Timeout(5 seconds)
// system.actorSelection("/user/test*").resolveOne().map { helloActor=>
// println(helloActor)
// helloActor ! "hello"
// helloActor ! "bye"
// }
val helloActor = system.actorSelection("/user/test1")
helloActor ! "hello"
helloActor ! "bye"
system.awaitTermination()
}
Simple Example with Router
package com.sillycat.throttle.demo
import akka.actor._
import akka.routing.FromConfig
import com.typesafe.config.ConfigFactory
object RoutingApp extends App{
// A simple actor that prints whatever it receives
class Printer extends Actor {
def receive = {
case x => println(self.path + " saying " + x)
}
}
class Shooter extends Actor {
def receive = {
case x => println(self.path + " shouting " + x)
}
}
val system = ActorSystem("RoutingSystem", ConfigFactory.load())
val router1: ActorRef = system.actorOf(Props[Printer].withRouter(FromConfig()), name = "Router1")
// These three messages will be sent to the printer immediately
router1 ! "11"
router1 ! "12"
router1 ! "13"
// These two will wait at least until 1 second has passed
router1 ! "14"
router1 ! "15"
println(" Router 1 " + router1.path)
val router2: ActorRef = system.actorOf(Props[Shooter].withRouter(FromConfig()), name = "Router2")
router2 ! "21"
router2 ! "22"
val router3: ActorSelection = system.actorSelection("/user/Router2")
router3 ! "23"
println(" Router 2 " + router2.path)
system.shutdown()
}
2 Throttler Based on Local Cache
Try to implement this solution.
//FUNCTION LIMIT_API_CALL(ip)
// ts = CURRENT_UNIX_TIME()
// keyname = ip+":"+ts
// current = GET(keyname)
// IF current != NULL AND current > 10 THEN
// ERROR "too many requests per second"
// ELSE
// MULTI
// INCR(keyname,1)
// EXPIRE(keyname,10)
// EXEC
// PERFORM_API_CALL()
//END
I first build one implementation on top of guava local cache.
package actors.throttle
import akka.actor.{Actor, ActorRef}
import com.sillycat.util.IncludeLogger
import services.LocalCache
import utils.IncludeDateTimeUtil
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
class LocalCacheBasedThrottler(var rate: Rate,var target: ActorRef) extends Actor with IncludeLogger with IncludeDateTimeUtil{
val throttleKey = "THROTTLE_"
def receive = {
case msg: MessageTick => {
val msgKey = msg.key
val realMsg = msg.msg
val counter = msg.count
val limitCalls = rate.numberOfCalls
val timeWindows = rate.duration
val timeKey = convertCurrentTime2Key(timeWindows)
val key = throttleKey + timeKey + msgKey
LocalCache.throttleBucket.getIfPresent(key) match {
case count:java.lang.Integer if count >= limitCalls => {
//delay random and tick self
LocalCache.throttleBucket.put(key, count + counter)
val delay = calculateDelay(count + counter, limitCalls, timeWindows)
//tick to self within the delay
context.system.scheduler.scheduleOnce(delay second, self, msg)
}
case count:java.lang.Integer => {
//count + 1
LocalCache.throttleBucket.put(key, count + counter)
//pass the ticket
target ! realMsg
}
case _ => {
//init the count
LocalCache.throttleBucket.put(key, new Integer(counter))
//pass the ticket
target ! realMsg
}
}
}
case _ => {
logger.error("Received a message I don't understand.")
}
}
}
case class Rate(val numberOfCalls: Int, val duration: Int)
case class MessageTick( key:String, msg:Any, count: Int = 1)
The Local Cache Classes
package services
import java.util.concurrent.TimeUnit
import com.google.common.cache.CacheBuilder
import models.CountStep
object LocalCache {
val builderThrottle = CacheBuilder.newBuilder().expireAfterWrite(60, TimeUnit.SECONDS)
val throttleBucket = builderThrottle.build[java.lang.String, java.lang.Integer]()
val builderMsg = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MINUTES)
val msgBucket = builderMsg.build[java.lang.String, CountStep]()
}
Here is how to use it.
Akka.system.actorOf(Props(classOf[LocalCacheBasedThrottler],
Rate(4, 15),
contextIOActor),
name = "context-io-throttler")
References:
http://sillycat.iteye.com/blog/2258226
http://letitcrash.com/post/28901663062/throttling-messages-in-akka-2
https://github.com/hbf/akka-throttler
http://sillycat.iteye.com/blog/1553508
http://doc.akka.io/api/akka/2.3.4/index.html#akka.contrib.throttle.TimerBasedThrottler
http://redis.io/commands/incr
http://doc.akka.io/docs/akka/snapshot/scala/routing.html
Improve the Throttle for Akka(1)Local Cache
猜你喜欢
转载自sillycat.iteye.com/blog/2276564
今日推荐
周排行