I'm using scala-io in my akka actors, in my case I need to send request and wait for response, in official docs (http://doc.akka.io/docs/akka/snapshot/scala/io-tcp.html) I can see the answer is asynchronous.
How can I wait for response, can I use somehow ? (ask) pattern
class SocketClient(remoteAddress: InetSocketAddress, listener: ActorRef) extends Actor {
import Tcp._
import context.system
IO(Tcp) ! Connect(remoteAddress)
def receive = {
case CommandFailed(_: Connect) =>
listener ! ConnectFailure
context stop self
case Connected(remote, local) =>
listener ! ConnectSuccess
val connection = sender
connection ! Register(self)
context become {
case data: ByteString =>
connection ! Write(data)
case CommandFailed(w: Write) =>
Logger.error(s"Error during writing")
case Received(data) =>
listener ! data
case Disconnect =>
connection ! Close
case _: ConnectionClosed =>
Logger.error(s"Connection has been closed ${remoteAddress.getAddress}")
context stop self
}
}
}
Can I use something like:
connection ? Write(data)
Yes, but you should take in account the fact that ask-pattern allows you to receive only first reply from actor.
In your case it's connection, which may reply some additional or even unrelated objects (it depends on back-pressure/acknowledgement mode you choose. For example, if you use Write - you may receive the written (to the socket) object's acknowledge instead of response.
You can avoid it by:
using NoAck as an AckEvent (see http://doc.akka.io/docs/akka/snapshot/scala/io-tcp.html, Throttling Reads and Writes section).
use atomic requests/replies (no multi-part)
use one actor per protocol (per each "ping-pong" sequence)
In other words, ask pattern just creates own internal actor per message and make it a sender, so all replies (for this particular message) are going to this micro-actor. When it receives first reply - future (returned by ?) becomes completed - and internal actor destroyed (so other replies will be ignored).
Also, connection automatically replies to registered (by Register message) listener instead of sender - so you should create mediate actor:
class Asker(connection: ActorRef) extends Actor {
import Tcp._
connection ! Register(self);
def receive = {
case x =>
val parent = sender()
connection ! x
context become {case x => parent ! x; context.unbecome()}
}
}
trait TcpAskSupport {
self: Actor =>
def asker(connection: ActorRef) =
context.child(connection.path.name + "Asker")
.getOrElse(system.actorOf(Props(classOf[Asker], connection),
connection.path.name + "Asker"))
}
Usage example:
class Client extends Actor with TcpAskSupport {
import Tcp._
import context.system
IO(Tcp) ! Connect(new InetSocketAddress("61.91.16.168", 80))
implicit val timeout = Timeout(new FiniteDuration(5, SECONDS))
def receive = {
case CommandFailed(_: Connect) =>
println("connect failed")
context stop self
case c # Connected(remote, local) =>
println("Connected" + c)
val connection = sender()
asker(connection) ? Write(ByteString("GET /\n", "UTF-8"), NoAck) onComplete {
case x => println("Response" + x)
}
case x => println("[ERROR] Received" + x)
}
}
Related
I'm currently working on an application with a signup process. This signup process will, at some point, communicate with external systems in an asynchronous manner. To keep this question concise, I'm showing you two important actors I've written:
SignupActor.scala
class SignupActor extends PersistentFSM[SignupActor.State, Data, DomainEvt] {
private val apiActor = context.actorOf(ExternalAPIActor.props(new HttpClient))
// At a certain point, a CreateUser(data) message is sent to the apiActor
}
ExternalAPIActor.scala
class ExternalAPIActor(apiClient: HttpClient) extends Actor {
override def preRestart(reason: Throwable, message: Option[Any]) = {
message.foreach(context.system.scheduler.scheduleOnce(3 seconds, self, _))
super.preRestart(reason, message)
}
def receive: Receive = {
case CreateUser(data) =>
Await.result(
apiClient.post(data)
.map(_ => UserCreatedInAPI())
.pipeTo(context.parent),
Timeout(5 seconds).duration
)
}
}
This setup seems to work as expected. When there is an issue with the external API (such as a timeout or network problems), the Future returned by HttpClient::post fails and will result in an exception thanks to Await.result. This, in turn thanks to the SupervisorStrategy of the SignupActor parent actor, will restart the ExternalAPIActor where we can re-send the last message to itself with a small delay to avoid deadlock.
I see a couple of issues with this setup:
Within the receive method of ExternalAPIActor, blocking occurs. As far as I understand, blocking within Actors is considered an anti-pattern.
The delay used to re-send the message is static. If the API is unavailable for longer periods of time, we will keep on sending HTTP requests every 3 seconds. I'd like some kind of exponential backoff mechanism here instead.
To continue on with the latter, I've tried the following in the SignupActor:
SignupActor.scala
val supervisor = BackoffSupervisor.props(
Backoff.onFailure(
ExternalAPIActor.props(new HttpClient),
childName = "external-api",
minBackoff = 3 seconds,
maxBackoff = 30 seconds,
randomFactor = 0.2
)
)
private val apiActor = context.actorOf(supervisor)
Unfortunately, this doesn't seem to do anything at all -- the preRestart method of ExternalAPIActor isn't called at all. When replacing Backoff.onFailure with Backoff.onStop, the preRestart method is called, but without any kind of exponential backoff at all.
Given the above, my questions are as follows:
Is using Await.result the recommended (the only?) way to make sure exceptions thrown in a Future returned from services called within actors are caught and handled accordingly? An especially important part of my particular use case is the fact that messages shouldn't be dropped but retried when something went wrong. Or is there some other (idiomatic) way that exceptions thrown in asynchronous contexts should be handled within Actors?
How would one use the BackoffSupervisor as intended in this case? Again: it is very important that the message responsible for the exception is not dropped, but retried until a N-number of times (to be determined by the maxRetries argument of SupervisorStrategy.
Is using Await.result the recommended (the only?) way to make sure
exceptions thrown in a Future returned from services called within
actors are caught and handled accordingly?
No. Generally that's not how you want to handle failures in Akka. A better alternative is to pipe the failure to your own actor, avoiding the need to use Await.result at all:
def receive: Receive = {
case CreateUser(data) =>
apiClient.post(data)
.map(_ => UserCreatedInAPI())
.pipeTo(self)
case Success(res) => context.parent ! res
case Failure(e) => // Invoke retry here
}
This would mean no restart is required to handle failure, they are all part of the normal flow of your actor.
An additional way to handle this can be to create a "supervised future". Taken from this blog post:
object SupervisedPipe {
case class SupervisedFailure(ex: Throwable)
class SupervisedPipeableFuture[T](future: Future[T])(implicit executionContext: ExecutionContext) {
// implicit failure recipient goes to self when used inside an actor
def supervisedPipeTo(successRecipient: ActorRef)(implicit failureRecipient: ActorRef): Unit =
future.andThen {
case Success(result) => successRecipient ! result
case Failure(ex) => failureRecipient ! SupervisedFailure(ex)
}
}
implicit def supervisedPipeTo[T](future: Future[T])(implicit executionContext: ExecutionContext): SupervisedPipeableFuture[T] =
new SupervisedPipeableFuture[T](future)
/* `orElse` with the actor receive logic */
val handleSupervisedFailure: Receive = {
// just throw the exception and make the actor logic handle it
case SupervisedFailure(ex) => throw ex
}
def supervised(receive: Receive): Receive =
handleSupervisedFailure orElse receive
}
This way, you only pipe to self once you get a Failure, and otherwise send it to the actor the message was meant to be sent to, avoiding the need for the case Success I added to the receive method. All you need to do is replace supervisedPipeTo with the original framework provided pipeTo.
Alright, I've done some more thinking and tinkering and I've come up with the following.
ExternalAPIActor.scala
class ExternalAPIActor(apiClient: HttpClient) extends Actor with Stash {
import ExternalAPIActor._
def receive: Receive = {
case msg # CreateUser(data) =>
context.become(waitingForExternalServiceReceive(msg))
apiClient.post(data)
.map(_ => UserCreatedInAPI())
.pipeTo(self)
}
def waitingForExternalServiceReceive(event: InputEvent): Receive = LoggingReceive {
case Failure(_) =>
unstashAll()
context.unbecome()
context.system.scheduler.scheduleOnce(3 seconds, self, event)
case msg:OutputEvent =>
unstashAll()
context.unbecome()
context.parent ! msg
case _ => stash()
}
}
object ExternalAPIActor {
sealed trait InputEvent
sealed trait OutputEvent
final case class CreateUser(data: Map[String,Any]) extends InputEvent
final case class UserCreatedInAPI() extends OutputEvent
}
I've used this technique to prevent the original message from being lost in case there is something wrong with the external service we're calling. During the process of a request to an external service, I switch context, waiting for either a response of a failure and switch back afterwards. Thanks to the Stash trait, I can make sure other requests to external services aren't lost as well.
Since I have multiple actors in my application calling external services, I abstracted the waitingForExternalServiceReceive to its own trait:
WaitingForExternalService.scala
trait WaitingForExternalServiceReceive[-tInput, +tOutput] extends Stash {
def waitingForExternalServiceReceive(event: tInput)(implicit ec: ExecutionContext): Receive = LoggingReceive {
case akka.actor.Status.Failure(_) =>
unstashAll()
context.unbecome()
context.system.scheduler.scheduleOnce(3 seconds, self, event)
case msg:tOutput =>
unstashAll()
context.unbecome()
context.parent ! msg
case _ => stash()
}
}
Now, the ExternalAPIActor can extend this trait:
ExternalAPIActor.scala
class ExternalAPIActor(apiClient: HttpClient) extends Actor with WaitingForExternalServiceReceive[InputEvent,OutputEvent] {
import ExternalAPIActor._
def receive: Receive = {
case msg # CreateUser(data) =>
context.become(waitingForExternalServiceReceive(msg))
apiClient.post(data)
.map(_ => UserCreatedInAPI())
.pipeTo(self)
}
}
object ExternalAPIActor {
sealed trait InputEvent
sealed trait OutputEvent
final case class CreateUser(data: Map[String,Any]) extends InputEvent
final case class UserCreatedInAPI() extends OutputEvent
}
Now, the actor won't get restarted in case of failures/errors and the message isn't lost. What's more, the entire flow of the actor now is non-blocking.
This setup is (most probably) far from perfect, but it seems to work exactly as I need it to.
In my Spray app, I delegate requests to actors. I want to be able to kill a actor that takes too long. I'm not sure whether I should be using Spray timeouts, Akka ask pattern or something else.
I have implemented:
def processRouteRequest(system: ActorSystem) = {
respondWithMediaType(`text/json`) {
params { p => ctx =>
val builder = newBuilderActor
builder ! Request(p) // the builder calls `ctx.complete`
builder ! PoisonPill
system.scheduler.scheduleOnce(routeRequestMaxLife, builder, Kill)
}
}
}
The idea being that the actor lives only for the duration of a single request and if it doesn't complete within routeRequestMaxLife it gets forcibly killed. This approach seems over-the-top (and spews a lot of info about undelivered messages). I'm not even certain it works correctly.
It seems like what I'm trying to achieve should be a common use-case. How should I approach it?
I would tend to using the ask pattern and handling the requests as follows:
class RequestHandler extends Actor {
def receive = {
case "quick" =>
sender() ! "Quick Reply"
self ! PoisonPill
case "slow" =>
val replyTo = sender()
context.system.scheduler.scheduleOnce(5 seconds, self, replyTo)
case a:ActorRef =>
a ! "Slow Reply"
self ! PoisonPill
}
}
class ExampleService extends HttpService with Actor {
implicit def actorRefFactory = context
import context.dispatcher
def handleRequest(mode: String):Future[String] = {
implicit val timeout = Timeout(1 second)
val requestHandler = context.actorOf(Props[RequestHandler])
(requestHandler ? mode).mapTo[String]
}
val route: Route =
path("endpoint" / Segment) { str =>
get {
onComplete(handleRequest(str)) {
case Success(str) => complete(str)
case Failure(ex) => complete(ex)
}
}
}
def receive = runRoute(route)
}
This way the actor takes care of stopping itself, and the semantics of Ask give you the information about whether or not the request timed out.
I'm writing an old school text based telnet server that right now is a glorified chat room in Scala with Akka actor based IO. What is happening is that client the client will start to type something and then an event will happen and when it gets written, it wipes out anything that has already been typed. In the following example, Tom has started to type "say How are you?" but Fred arrives after he has only typed "say How ar" and this input is wiped out:
Tom > say How ar
Fred has arrived.
Tom >
Is there any way to get telnet to redisplay it's output buffer it hasn't flushed yet?
Here is the server:
class TcpServer(port: Int) extends Actor {
import TcpServer._
import context.system
val log = Logging(system, this)
var connectionNum: Int = 1
log.info(STARTING_SERVER)
IO(Tcp) ! Bind(self, new InetSocketAddress("0.0.0.0", port))
def receive = {
case b # Bound(localAddress) =>
log.info(PORT_BOUND, localAddress)
case c # Connected(remote, local) =>
log.info(CONNECTION_ACCEPTED)
context.actorOf(ConnectionHandler.connectionProps(sender()), s"conn$connectionNum")
connectionNum += 1
case CommandFailed(_: Bind) =>
log.error(BINDING_FAILED)
context stop self
}
}
Here is the ConnectionHandler, it's companion object, and the message case classes it uses:
class ConnectionHandler(connection: ActorRef) extends Actor {
import context._
val log = Logging(context.system, this)
connection ! Register(self)
var delegate = actorOf(Props[LoginHandler], "login")
watch(delegate)
def receive = {
case Received(data) =>
val dataString = data.utf8String.trim
log.info("Received data from connection: {}", dataString)
delegate ! Input(dataString)
case Output(content) =>
connection ! Write(ByteString(content))
case LoggedIn(user) =>
unwatch(delegate)
delegate ! PoisonPill
delegate = actorOf(UserHandler.connectionProps(user), user.name.toLowerCase)
watch(delegate)
case Terminated =>
log.warning("User delegate died unexpectedly.")
connection ! ConfirmedClose
case CloseConnection(message) =>
connection ! Write(ByteString(message + "\n"))
connection ! ConfirmedClose
log.info("User quit.")
case ConfirmedClosed =>
log.info("Connection closed.")
stop(self)
case PeerClosed =>
log.info("Connection closed by client.")
stop(self)
}
}
object ConnectionHandler {
def connectionProps(connection: ActorRef): Props = Props(new ConnectionHandler(connection))
}
case class Input(input: String)
case class Output(output: String)
case class LoggedIn(user: User)
case class CloseConnection(message: String)
Okay, after finally phrasing my google queries correctly, I found what I needed here:
Force telnet client into character mode
The basic solution is that I forced the client into character at a time mode and echo'd back the characters I care about. The bonus to this is that now I can do tab completion, command history, and make the passwords not show up.
Here is the relevant code snippet:
val controlString = ByteString('\u00ff','\u00fb','\u0001','\u00ff','\u00fb','\u0003','\u00ff','\u00fc','\u0022')
connection ! Write(controlString)
Stuff I need help with is in bold.
I have an actor that is flying multiple spray HttpRequests, the requests are paginated and the actor makes sure it writes the results in sequence into a database (sequence is important to resume crawlers). I explain this because I don't want to explore other patterns of concurrency at the moment. The actor needs to recover from timeouts without restarting.
in my actor I have the following :
case f : Failure => {
system.log.error("faiure")
system.log.error(s"$f")
system.shutdown()
}
case f : AskTimeoutException => {
system.log.error("faiure")
system.log.error(s"$f")
system.shutdown()
}
case msg # _ => {
system.log.error("Unexpected message in harvest")
system.log.error(s"${msg}")
system.shutdown()
}
but I can't match correctly :
[ERROR] [11/23/2013 14:58:10.694] [Crawler-akka.actor.default-dispatcher-3] [ActorSystem(Crawler)] Unexpected message in harvest
[ERROR] [11/23/2013 14:58:10.694] [Crawler-akka.actor.default-dispatcher-3] [ActorSystem(Crawler)] Failure(akka.pattern.AskTimeoutException: Timed out)
My dispatches look as follows :
abstract class CrawlerActor extends Actor {
private implicit val timeout: Timeout = 20.seconds
import context._
def dispatchRequest(node: CNode) {
val reqFut = (System.requester ? CrawlerRequest(node,Get(node.url))).map(r=> CrawlerResponse(node,r.asInstanceOf[HttpResponse]))
reqFut pipeTo self
}
class CrawlerRequester extends Actor {
import context._
val throttler = context.actorOf(Props(classOf[TimerBasedThrottler],System.Config.request_rate),"throttler")
throttler ! SetTarget(Some(IO(Http).actorRef))
def receive : Receive = {
case CrawlerRequest(type_,request) => {
throttler forward request
}
}
}
Once I find the correct way of matching, is there anyway I can get my hands on the CrawlerRequest that the timeout occurred with ? it contains some state I need to figure out how to recover.
This situation occurs if you use pipeTo to respond to message that sent by tell.
For example:
in actorA: actorB ! message
in actorB: message => doStuff pipeTo sender
in actorA: receives not 'scala.util.Failure', but 'akka.actor.Status.Failure'
The additional logic in pipeTo is to transform Try's Failure into akka's actor Failure (akka.actor.Status.Failure). This works fine when you use ask pattern, because temporary ask actor handle akka.actor.Status.Failure for you, but does not work well with tell.
Hope this short answer helps :)
Good luck!
Need to type out the full path of the Failure case class, (or import it I guess).
case f: akka.actor.Status.Failure => {
system.log.error("faiure")
system.log.error(s"${f.cause}")
system.shutdown()
}
That just leaves getting to the request associated with the timeout. Seems a map and pipe with a custom failure handler is needed at point request dispatch. Looking into it now.
The following trampolines the timeout into the actor.
case class CrawlerRequestTimeout(request: CrawlerRequest)
abstract class CrawlerActor extends Actor {
private implicit val timeout: Timeout = 20.seconds
import context._
def dispatchRequest(node: CNode) {
val req = CrawlerRequest(node,Get(node.url))
val reqFut = (System.requester ? req).map(r=> CrawlerResponse(node,r.asInstanceOf[HttpResponse]))
reqFut onFailure {
case te: akka.pattern.AskTimeoutException => self ! CrawlerRequestTimeout(req)
}
reqFut pipeTo self
}
}
with a match of :
case timeout : CrawlerRequestTimeout => {
println("boom")
system.shutdown()
}
Need to find a way of suppressing the exception though, it's still firing. Perhaps suppression isn't really a concern, verifying.
No, suppression is a concern, or the exception trickles down to the msg # _, need to put in a case class to absorb the redundant failure message.
ok, so getting rid of the pipeto gets rid of the exception entering the client actor. It's also a lot easier to read :D
abstract class CrawlerActor extends Actor {
private implicit val timeout: Timeout = 20.seconds
import context._
def dispatchRequest(node: CNode) {
val req = CrawlerRequest(node,Get(node.url))
val reqFut = (System.requester ? req)
reqFut onFailure {
case te: akka.pattern.AskTimeoutException => self ! CrawlerRequestTimeout(req)
}
reqFut onSuccess {
case r: HttpResponse => self ! CrawlerResponse(node,r)
}
}
}
If I understand correctly, you currently don't succeed in matching the AskTimeoutException.
If so, you should match case Failure(AskTimeoutException) => ... instead of case f : AskTimeoutException => ....
Is it possible to make an Actor wait for X amount of seconds to receive any message, and if a message is received, process it as usual, otherwise send a message to some other Actor (pre-determined in the constructor)?
It's possible, have a look at Akka Actor "ask" and "Await" with TimeoutException. But keep in mind that blocking inside an actor is a very bad idea since during that time actor can't handle any other messages. Moreover it blocks one Akka processing thread.
A better approach is to send a message (fire and forget) and schedule some timeout event using Akka scheduler. When the response arrives, cancel that event or set some flag so that it won't trigger if the reply actually came on time.
Yes, if you want to wait for any message, you simply set a receiveTimeout: http://doc.akka.io/docs/akka/current/scala/actors.html#receive-timeout
(The docs is slightly misleading here, you can set the receiveTimeout after every message also)
Might be an overkill, but you might check out the Finite State Machine (FSM) trait.
import akka._
import actor._
import util._
import duration._
import Impatient._
object Impatient {
sealed trait State
case object WaitingForMessage extends State
case object MessageReceived extends State
case object TimeoutExpired extends State
sealed trait Data
case object Unitialized extends Data
// In
case object Message
}
class Impatient(receiver: ActorRef) extends Actor with FSM[State, Data] {
startWith(WaitingForMessage, Unitialized)
when(WaitingForMessage, stateTimeout = 3 seconds) {
case Event(StateTimeout, data) => goto(TimeoutExpired) using data // data is usually modified here
case Event(Message, data) => goto(MessageReceived) using data // data is usually modified here
}
onTransition {
case WaitingForMessage -> MessageReceived => stateData match {
case data => log.info("Received message: " + data)
}
case WaitingForMessage -> TimeoutExpired => receiver ! TimeoutExpired
}
when(MessageReceived) {
case _ => stay
}
when(TimeoutExpired) {
case _ => stay
}
initialize
}
Here it is in action:
object Main extends App {
import akka._
import actor._
import Impatient._
val system = ActorSystem("System")
val receiver = system.actorOf(Props(new Actor with ActorLogging {
def receive = {
case TimeoutExpired => log.warning("Timeout expired")
}
}))
val impatient = system.actorOf(Props(new Impatient(receiver)), name = "Impatient")
impatient ! Message
val impatient2 = system.actorOf(Props(new Impatient(receiver)), name = "Impatient2")
Thread.sleep(4000)
impatient2 ! Message
system.shutdown()
}