Akka-http approach to handle websocket commands - scala

Let's say I have controller that handles commands that I receive from websocket.
class WebSocketController(implicit M: Materializer)
extends Controller
with CirceSupport {
override def route: Route = path("ws") {
handleWebSocketMessages(wsFlow)
}
def wsFlow: Flow[Message, Message, Any] =
Flow[Message].mapConcat {
case tm: TextMessage =>
decode[Command](tm.getStrictText) match {
// todo pass this command to some actor or service
// and get response and reply back.
case Right(AuthorizeUser(login, password)) =>
TextMessage(s"Authorized: $login, $password") :: Nil
case _ =>
Nil
}
case bm: BinaryMessage =>
bm.dataStream.runWith(Sink.ignore)
Nil
}
}
So, I get a command, deserialize it and next step I want to do is to pass it to some service or actor that will return me Future[SomeReply].
The question is:
What is the basic approach of handling such flow with akka streams?

When handling Futures inside a Flow, mapAsync is usually what you're looking for. To add to your specific example:
def asyncOp(msg: TextMessage): Future[SomeReply] = ???
def tailorResponse(r: SomeReply): TextMessage = ???
def wsFlow: Flow[Message, Message, Any] =
Flow[Message]
.mapConcat {...}
.mapAsync(parallelism = 4)(asyncOp)
.via(tailorResponse)
mapAsyncUnordered can also be used, in case the order of the Futures result is not relevant.
Parallelism indicates how many Futures can be run at the same time, before the stage backpressures.
See also
stage docs
how to use in conjunction with ask - here

Related

Scala Akka Actors: How to send the result of an Http response back to the sender?

I'm trying to execute the following Scala code inside an Akka Actor.
class FilteringService(implicit timeout: Timeout) extends Actor {
def receive: PartialFunction[Any, Unit] = {
case GetProfiles ⇒
val requester = sender
def getProfiles = {
var result = new Array[Profile](0)
println("[GET-PROFILES] Entered, making request")
val req = Get("http://localhost:9090/profiles")
implicit val profileFormat = jsonFormat16(Profile)
val responseFuture: Future[HttpResponse] = Http().singleRequest(req)
println("[GET-PROFILES] Entered, request sent")
responseFuture.onComplete {
case Success(response) =>
println("[RES - SUCCESS] Request returned with " + response.status)
val responseAsProfiles = Unmarshal(response.entity).to[Array[Profile]]
responseAsProfiles.onComplete {
println("[UNMARSH - SUCCESS] Unmarshaling Done!")
_.get match {
case profiles: Array[Profile] =>
println("[UNMARSH - SUCCESS] Sending Profiles message to " + sender())
requester ! profiles
println("[UNMARSH - SUCCESS] Message sent to " + sender())
case _ => println("error")
}
}
case Failure(_) =>
sys.error("something wrong")
//return Future[Array[Profile]]
}
}
println("[RECEIVE] Message GetProfiles received from " + sender().toString())
getProfiles
println("[RECEIVE] Message GetProfiles invoked")
}
When the Actor receives the message "GetProfiles":
1- it sends a request to a remote server, so the result of operation is a Future[HttpResponse]
2- in case of success it retrieves the response (a JSON array) and asks for unmarshalling the object to Array[Profile]. (It's not important the Profile model). The result of Unmarshall method is a Future[Array[Profile]]
3- In case of success, I want to send the result back to the original sender!
I managed to do this, but it's a trick because I'm saving the sender in a variable, that is visible in the scope (requester).
I know that there is the pipe pattern, so I could send the responseAsProfiles object back to the sender in theory, but the object is created inside the onComplete method of the responseFuture object (we have to wait it, of course!)
So that's all!
How could I send the result back to the sender using the pipe pattern in this case?
Thanks in advance!!!
General idea is that you compose futures using map and flatMap and try to avoid using onComplete as much as possible.
See if you can convert your code to following smaller pieces and then compose:
def getRawProfileData(): Future[HttpResponse] = {
// ... here you make http request
}
def unmarshalProfiles(response: HttpResponse): Future[List[Profile]] = {
// ... unmarshalling logic
}
def getProfiles(): Future[List[Profile]] = getRawProfileData().flatMape(unmarshalProfiles)
// now from receive block
case GetProfiles ⇒ getProfiles().pipeTo(sender())

Get a message out of an Akka Actor

I've built an Akka actor that queries an API at regular intervals, like this:
val cancellable =
system.scheduler.schedule(0 milliseconds,
5 seconds,
actor,
QueryController(1))
The Actor, in essence is:
object UpdateStatistics {
/**
* Query the controller for the given switch Id
*
* #param dpId Switch's Id
*/
case class QueryController(dpId: Int)
case object Stop
def props: Props = Props[UpdateStatistics]
}
class UpdateStatistics extends Actor with akka.actor.ActorLogging {
import UpdateStatistics._
def receive = {
case QueryController(id) =>
import context.dispatcher
log.info(s"Receiving request to query controller")
Future { FlowCollector.getSwitchFlows(1) } onComplete {
f => self ! f.get
}
case Stop =>
log.info(s"Shuting down")
context stop self
case json: JValue =>
log.info("Getting json response, computing features...")
val features = FeatureExtractor.getFeatures(json)
log.debug(s"Features: $features")
sender ! features
case x =>
log.warning("Received unknown message: {}", x)
}
}
What I am trying to do is get the json:Jvalue message out of UpdateStatistics actor. Reading the Akka docs I thought this may be useful:
implicit val i = inbox()
i.select() {
case x => println(s"Valor Devuelto $x")
}
println(i receive(2.second))
But I do not know how to modify UpdateStatistics actor in order to send the result to the inbox above.
Another option I've read in the docs are event streams.
But I do not think this is the correct way.
Is there a way of achieving what I want to do? Or do I need to use a second Actor to which I send the JSON response?
You probably are looking for the ask pattern in AKKA. This will allow you to return a value to the sender.
import akka.pattern.ask
import akka.util.duration._
implicit val timeout = Timeout(5 seconds)
val future = actor ? QueryController(1)
val result = Await.result(future, timeout.duration).asInstanceOf[JValue]
println(result)
To make this work, you need to send the response to the original sender, rather than self. Also, you should beware the dangers of closing over sender in a future when handling messages.

Akka round-robin: Sending response from remote routees to sender

I am using Akka Cluster (version 2.4.10) with few nodes designated for "front-end" role and few others as "workers". The workers are on remote machines. The incoming work is distributed by the front-end actor to workers by round-robin routing. The issue is sending back the response from the "workers" back to the front-end actor. I can see that the work is getting completed by the workers. But the message sent by the workers to front-end does not reach and ends up as dead-letters. I see the below error in the log.
[Cluster-akka.actor.default-dispatcher-21] [akka://Cluster/deadLetters] Message [scala.collection.immutable.$colon$colon] from Actor[akka://Cluster/user] to Actor[akka://Cluster/deadLetters] was not delivered. [6] dead letters encountered.
I have seen this and I am following the same in my code. I have also seen this, but the solution suggested does not apply in this case, because I do not know the routees up-front. It comes through the configuration and it can change. The round-robin router configuration is as below.
akka.actor.deployment {
/frontEnd/hm = {
router = round-robin-group
nr-of-instances = 5
routees.paths = ["/user/hmWorker"]
cluster {
enabled = on
use-role = backend
allow-local-routees = on
}
}
}
The router is instantiated in front-end actor like below.
val router = context.actorOf(FromConfig.props(), name = "hm")
val controller = context.actorOf(Props(classOf[Controller], router))
The controller and the worker codes are below.
// Node 1 : Controller routes requests using round-robin
class Controller(router: ActorRef) extends Actor {
val list = List("a", "b") // Assume this is a big list
val groups = list.grouped(500)
override def receive: Actor.Receive = {
val futures = groups.map(grp => (router ? Message(grp)).mapTo[List[String]]))
val future = Future.sequence(futures).map(_.flatten)
val result = Await.result(future, 50 seconds)
println(s"Result is $result")
}
}
// Node 2
class Worker extends Actor {
override def receive: Actor.Receive = {
case Message(lst) =>
val future: Future[List[String]] = // Do Something asynchronous
future onComplete {
case Success(r) => sender.!(r)(context.parent) // This message is not delivered to Controller actor.
case Failure(th) => // Error handling
}
}
}
Please let me know what I am doing wrong here. Appreciate your help.
You shouldn't use sender() in the callback on a Future. By the time the callback is processed, the sender() is likely referring to something different than it was when you received the message.
Consider either saving the reference outside of the callback first like:
override def receive: Actor.Receive = {
case Message(lst) =>
val future: Future[List[String]] = // Do Something asynchronous
val replyTo: ActorRef = sender()
future onComplete {
case Success(r) => replyTo.!(r)(context.parent) // This message is not delivered to Controller actor.
case Failure(th) => // Error handling
}
}
Or even better, use the pipe pattern:
import akka.pattern.pipe
override def receive: Actor.Receive = {
case Message(lst) =>
val future: Future[List[String]] = // Do Something asynchronous
future.pipeTo(sender())
}

Akka Supervisor Strategy - Correct Use Case

I have been using Akka Supervisor Strategy to handle business logic exceptions.
Reading one of the most famous Scala blog series Neophyte, I found him giving a different purpose for what I have always been doing.
Example:
Let's say I have an HttpActor that should contact an external resource and in case it's down, I will throw an Exception, for now a ResourceUnavailableException.
In case my Supervisor catches that, I will call a Restart on my HttpActor, and in my HttpActor preRestart method, I will call do a schedulerOnce to retry that.
The actor:
class HttpActor extends Actor with ActorLogging {
implicit val system = context.system
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
log.info(s"Restarting Actor due: ${reason.getCause}")
message foreach { msg =>
context.system.scheduler.scheduleOnce(10.seconds, self, msg)
}
}
def receive = LoggingReceive {
case g: GetRequest =>
doRequest(http.doGet(g), g.httpManager.url, sender())
}
A Supervisor:
class HttpSupervisor extends Actor with ActorLogging with RouterHelper {
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 5) {
case _: ResourceUnavailableException => Restart
case _: Exception => Escalate
}
var router = makeRouter[HttpActor](5)
def receive = LoggingReceive {
case g: GetRequest =>
router.route(g, sender())
case Terminated(a) =>
router = router.removeRoutee(a)
val r = context.actorOf(Props[HttpActor])
context watch r
router = router.addRoutee(r)
}
}
What's the point here?
In case my doRequest method throws the ResourceUnavailableException, the supervisor will get that and restart the actor, forcing it to resend the message after some time, according to the scheduler. The advantages I see is the fact I get for free the number of retries and a nice way to handle the exception itself.
Now looking at the blog, he shows a different approach in case you need a retry stuff, just sending messages like this:
def receive = {
case EspressoRequest =>
val receipt = register ? Transaction(Espresso)
receipt.map((EspressoCup(Filled), _)).recover {
case _: AskTimeoutException => ComebackLater
} pipeTo(sender)
case ClosingTime => context.system.shutdown()
}
Here in case of AskTimeoutException of the Future, he pipes the result as a ComebackLater object, which he will handle doing this:
case ComebackLater =>
log.info("grumble, grumble")
context.system.scheduler.scheduleOnce(300.millis) {
coffeeSource ! EspressoRequest
}
For me this is pretty much what you can do with the strategy supervisor, but in a manually way, with no built in number of retries logic.
So what is the best approach here and why? Is my concept of using akka supervisor strategy completely wrong?
You can use BackoffSupervisor:
Provided as a built-in pattern the akka.pattern.BackoffSupervisor implements the so-called exponential backoff supervision strategy, starting a child actor again when it fails, each time with a growing time delay between restarts.
val supervisor = BackoffSupervisor.props(
Backoff.onFailure(
childProps,
childName = "myEcho",
minBackoff = 3.seconds,
maxBackoff = 30.seconds,
randomFactor = 0.2 // adds 20% "noise" to vary the intervals slightly
).withAutoReset(10.seconds) // the child must send BackoffSupervisor.Reset to its parent
.withSupervisorStrategy(
OneForOneStrategy() {
case _: MyException => SupervisorStrategy.Restart
case _ => SupervisorStrategy.Escalate
}))

Resolving Akka futures from ask in the event of a failure

I am calling an Actor using the ask pattern within a Spray application, and returning the result as the HTTP response. I map failures from the actor to a custom error code.
val authActor = context.actorOf(Props[AuthenticationActor])
callService((authActor ? TokenAuthenticationRequest(token)).mapTo[LoggedInUser]) { user =>
complete(StatusCodes.OK, user)
}
def callService[T](f: => Future[T])(cb: T => RequestContext => Unit) = {
onComplete(f) {
case Success(value: T) => cb(value)
case Failure(ex: ServiceException) => complete(ex.statusCode, ex.errorMessage)
case e => complete(StatusCodes.InternalServerError, "Unable to complete the request. Please try again later.")
//In reality this returns a custom error object.
}
}
This works correctly when the authActor sends a failure, but if the authActor throws an exception, nothing happens until the ask timeout completes. For example:
override def receive: Receive = {
case _ => throw new ServiceException(ErrorCodes.AuthenticationFailed, "No valid session was found for that token")
}
I know that the Akka docs say that
To complete the future with an exception you need send a Failure message to the sender. This is not done automatically when an actor throws an exception while processing a message.
But given that I use asks for a lot of the interface between the Spray routing actors and the service actors, I would rather not wrap the receive part of every child actor with a try/catch. Is there a better way to achieve automatic handling of exceptions in child actors, and immediately resolve the future in the event of an exception?
Edit: this is my current solution. However, it's quite messy to do this for every child actor.
override def receive: Receive = {
case default =>
try {
default match {
case _ => throw new ServiceException("")//Actual code would go here
}
}
catch {
case se: ServiceException =>
logger.error("Service error raised:", se)
sender ! Failure(se)
case ex: Exception =>
sender ! Failure(ex)
throw ex
}
}
That way if it's an expected error (i.e. ServiceException), it's handled by creating a failure. If it's unexpected, it returns a failure immediately so the future is resolved, but then throws the exception so it can still be handled by the SupervisorStrategy.
If you want a way to provide automatic sending of a response back to the sender in case of an unexpected exception, then something like this could work for you:
trait FailurePropatingActor extends Actor{
override def preRestart(reason:Throwable, message:Option[Any]){
super.preRestart(reason, message)
sender() ! Status.Failure(reason)
}
}
We override preRestart and propagate the failure back to the sender as a Status.Failure which will cause an upstream Future to be failed. Also, it's important to call super.preRestart here as that's where child stopping happens. Using this in an actor looks something like this:
case class GetElement(list:List[Int], index:Int)
class MySimpleActor extends FailurePropatingActor {
def receive = {
case GetElement(list, i) =>
val result = list(i)
sender() ! result
}
}
If I was to call an instance of this actor like so:
import akka.pattern.ask
import concurrent.duration._
val system = ActorSystem("test")
import system.dispatcher
implicit val timeout = Timeout(2 seconds)
val ref = system.actorOf(Props[MySimpleActor])
val fut = ref ? GetElement(List(1,2,3), 6)
fut onComplete{
case util.Success(result) =>
println(s"success: $result")
case util.Failure(ex) =>
println(s"FAIL: ${ex.getMessage}")
ex.printStackTrace()
}
Then it would properly hit my Failure block. Now, the code in that base trait works well when Futures are not involved in the actor that is extending that trait, like the simple actor here. But if you use Futures then you need to be careful as exceptions that happen in the Future don't cause restarts in the actor and also, in preRestart, the call to sender() will not return the correct ref because the actor has already moved into the next message. An actor like this shows that issue:
class MyBadFutureUsingActor extends FailurePropatingActor{
import context.dispatcher
def receive = {
case GetElement(list, i) =>
val orig = sender()
val fut = Future{
val result = list(i)
orig ! result
}
}
}
If we were to use this actor in the previous test code, we would always get a timeout in the failure situation. To mitigate that, you need to pipe the results of futures back to the sender like so:
class MyGoodFutureUsingActor extends FailurePropatingActor{
import context.dispatcher
import akka.pattern.pipe
def receive = {
case GetElement(list, i) =>
val fut = Future{
list(i)
}
fut pipeTo sender()
}
}
In this particular case, the actor itself is not restarted because it did not encounter an uncaught exception. Now, if your actor needed to do some additional processing after the future, you can pipe back to self and explicitly fail when you get a Status.Failure:
class MyGoodFutureUsingActor extends FailurePropatingActor{
import context.dispatcher
import akka.pattern.pipe
def receive = {
case GetElement(list, i) =>
val fut = Future{
list(i)
}
fut.to(self, sender())
case d:Double =>
sender() ! d * 2
case Status.Failure(ex) =>
throw ex
}
}
If that behavior becomes common, you can make it available to whatever actors need it like so:
trait StatusFailureHandling{ me:Actor =>
def failureHandling:Receive = {
case Status.Failure(ex) =>
throw ex
}
}
class MyGoodFutureUsingActor extends FailurePropatingActor with StatusFailureHandling{
import context.dispatcher
import akka.pattern.pipe
def receive = myReceive orElse failureHandling
def myReceive:Receive = {
case GetElement(list, i) =>
val fut = Future{
list(i)
}
fut.to(self, sender())
case d:Double =>
sender() ! d * 2
}
}