I'm using Spray in my application and from the examples I've see on Github it looks like people handle HTTP requests in Akka by passing the HTTPContext object around to all the actors and calling onComplete { } on the Future in the last actor.
Is sending the context deep down in the application really a good idea ? This way every event object will have a context parameter.
How do we handle HTTP requests & response properly in Akka? I've read this article but I would like to know people's thoughts who run Akka in production on the right way of achieving this.
I prefer to use the ask pattern in the Spray service, and the onSuccess directive, e.g.:
trait MyService extends HttpService {
def worker: ActorRef
implicit def timeout:Timeout
implicit def ec:ExecutionContext
def askWorker: Future[String] = (worker ? "Hello").mapTo[String]
def myRoute = path("/") {
get {
onSuccess(askWorker){
case str => complete(str)
}
}
}
}
Then a concrete actor such as:
class ServiceActor extends MyService with Actor {
implicit val ec = context.system
implicit val timeout = Timeout(3 seconds)
val worker = context.actorOf(Props[WorkerActor])
override def actorRefFactory = context.system
def receive = runRoute(myRoute)
}
I like this pattern rather than passing the request context around since it means that the other actors don't have to have any concept of Http. The service could be completely replaced with a different protocol. In this example the worker actor is can be something like:
class WorkerActor extends Actor {
def receive = {
case "Hello" => sender() ! "Hello World"
}
}
Related
I have an old Scala/Akka Http project that I'm trying to simplify and refactor. I was wondering if there's a better way to organize routes and perhaps split them across actors. Here's what I have at the moment (far from ideal):
```
object MyAPI {
def props(): Props = Props(new MyAPI())
val routes = pathPrefix("api") {
pathPrefix("1") {
SomeActor.route //More routes can be appended here using ~
}
}
}
final class MyAPI extends Actor with ActorLogging {
implicit lazy val materializer = ActorMaterializer()
implicit lazy val executionContext = context.dispatcher
Http(context.system)
.bindAndHandleAsync(Route.asyncHandler(MyAPI.routes), MyHttpServer.httpServerHostName, MyHttpServer.httpServerPort)
.pipeTo(self)
override def receive: Receive = {
case serverBinding: ServerBinding =>
log.info(s"Server started on ${serverBinding.localAddress}")
context.become(Actor.emptyBehavior)
case Status.Failure(t) =>
log.error(t, "Error binding to network interface")
context.stop(self)
}
}
```
```
object SomeActor {
def props(): Props = Props[SomeActor]
val route = get {
pathPrefix("actor") {
pathEnd {
complete("Completed") //Is there a clean way 'ask' the actor below?
}
}
}
}
class SomeActor extends Actor with ActorLogging {
implicit lazy val executionContext = context.dispatcher;
override def receive: Receive = {
//receive and process messages here
}
```
So, my question is - is there a clean way to structure and refactor routes instead of lumping them together in one large route definition? I could perhaps create a hierarchy of actors (routers) and the main route definition just delegates it to the routers and we incrementally add more details as we go deeper in the actor hierarchy. But is there a generally accepted patter or two to organize routes?
I would like to suggest you on the basis of the functionality you can have as many actors you you want, but create one supervisor actors which will keep watch on each child actor. And all the supervision strategies should be written into the supervisor itself, and all the msg you gonna sent to the actors should be forwarded by the supervisor.
As soon as you get the data from the end point, may be get or post method take the data into someRequest case class. Then sent it to some handleReq() method. And then do your processing make traits on functionality basis.
You can structure project something like this.
src/
actor//all the actors will be in this package
model// all the case classes and constants
repo// all the db related function you can do here
service// all your routes and endPoint functions
Now you can have package util where all the utilities trait you can put which will be used by any of the actor, or service may be you have lots of validations you can have a package named validator.
The structure is depends on your business. I think it would help.
I want to test that an actor A send a message to an actor B after have received a message.
I'm using Play! 2.5 and I use the factories since I need to inject some of my classes and things like wSClient inside the actors.
The Actor A looks like:
object ActorA {
trait Factory {
def apply(ec: ExecutionContext, actorBRef: ActorRef): Actor
}
}
class ActorA #Inject()(implicit val ec: ExecutionContext,
#Named("actor-b") actorBRef: ActorRef)
extends Actor with ActorLogging with InjectedActorSupport {
override def receive: Receive = {
case i: Long =>
log info s"received $i"
actorBRef ! (i+1)
}
And the actor B is even more simple:
object ActorB {
trait Factory {
def apply(): Actor
}
}
class ActorB extends Actor with ActorLogging {
override def receive: Receive = {
case _ =>
log error "B received an unhandled message"
}
}
But my test doesn't pass, it is said that the expected message doesn't arrive, I get a Timeout in the test (but it is well logged by the actor B) so the problem comes from the test (and probably the Probe).
Here is the test:
val actorBProbe = TestProbe()
lazy val appBuilder = new GuiceApplicationBuilder().in(Mode.Test)
lazy val injector = appBuilder.injector()
lazy val factory = injector.instanceOf[ActorA.Factory]
lazy val ec = scala.concurrent.ExecutionContext.Implicits.global
lazy val factoryProps = Props(factory(ec, actorBProbe.ref))
val ActorARef = TestActorRef[ActorA](factoryProps)
"Actor B" must {
"received a message from actor A" in {
ActorARef ! 5L
actorBProbe.expectMsg(6L)
}
}
I also created a minimum Play! application with the code above available here.
In your test, actorBProbe is not the ActorB ref passed to ActorA constructor (of ref ActorARef). What really happens is that Guice creates a different ActorB (named actor-b), and passes its ref to ActorA (of ref ActorARef) constructor.
The test ends up with ActorB actor-b receiving 6L (as evident in log). While actorBProbe receives nothing.
The confusion really comes from mixing Guice lifecyle with Actors. In my experience, it creates more pains than I can bear.
To prove, simply print hash code of ActorRef's, you'll see they are different. Illustrated as followings:
val actorBProbe = TestProbe()
println("actorBProbe with ref hash: " + actorBProbe.ref.hashCode())
And,
class ActorA ... {
override def preStart =
log error "preStart actorBRef: " + actorBRef.hashCode()
// ...
}
In fact, even ec inside ActorA is not the same ec in the test code.
The following is a way to "force" the test to pass and at the same time prove that actorBProbe wasn't really being used by ActorB.
In stead of relying on Guice to "wire in" ActorB, we tell Guice to leave it alone by replacing #Named("actor-b") with #Assisted, like this,
import ...
import com.google.inject.assistedinject.Assisted
class ActorA #Inject()(...
/*#Named("actor-b")*/ #Assisted actorBRef: ActorRef)
...
Re-run the test, it'll pass. But this is probably not what you wanted to begin with.
Below is my controller:
package controllers
import java.util.TimeZone
import akka.actor.{ActorNotFound, ActorSystem}
import akka.util.Timeout
import com.google.inject.Inject
import com.typesafe.akka.extension.quartz.QuartzSchedulerExtension
import play.api.Logger
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.json.{JsValue, JsError, JsSuccess}
import play.api.mvc._
import scala.concurrent.Future
import scala.concurrent.duration._
class ScheduleController #Inject()(system: ActorSystem) extends Controller {
val scheduler = QuartzSchedulerExtension.get(system)
implicit val timeout = new Timeout(5.seconds)
def index = Action.async {
Future.successful(Ok("hi"))
}
def run = Action.async { request =>
// find actor and "start" it by sending message to it, return Ok() if found
// if actor is not found, return BadRequest()
// if error return InternalServerError()
}
}
I've been looking for tutorials but most are outdated since they deal with Play 2.3.
I solved a very similar problem with Play2 controllers and actors except in my case I'm not injecting an ActorSystem into my controller. Rather, I use Play's default actor system and create actors from my global settings object when my application starts. For a given actor I provide a selection method in its companion object. For example:
class MyActor extends Actor {
def receive = {
case Request => sender ! Response
}
}
object MyActor {
val path = // assuming fixed actor path
val sys = // reference to play actor system
def select: ActorSelection = sys.actorSelection(path)
// my actor messages
case object Request
case object Response
}
The way I have this actor setup I plan on asking it from inside my controller method using a Request and expect to process a Response object back from it. The controller is a non-actor sender in this scenario. By calling ask on the actor selection, I close over the future and map the result to a partial function that matches the expected response. Here's what my controller looks like:
class MyController extends Controller {
implicit val timeout = // set your timeout duration
def index = Action.async {
MyActor.select ? Request map {
case Response => Ok("success")
}
}
}
Testing this controller endpoint is VERY challenging because your actor might not be in a ready state to start handling messages. I spent about two days trying to figure out how to properly block on the my actor's lifecycle state using the actor selection I've exposed through its companion object. What I ended up doing inside my test class that worked for me was to create a method that takes in a function parameter and loops over my actor state until I can resolve its ActorRef. I'm a fan of ScalaTest so this is what my unit tests amounted to:
class MySpec extends FlatSpec with Matchers with BeforeAndAfterAll {
implicit val fakeApp: FakeApplication = new FakeApplication()
override def beforeAll() = Play.start(fakeApp)
override def afterAll() = Play.stop(fakeApp)
def awaitableActorTest(assertions: => Any): Unit = {
val timeout = 1.second
var isActorReady = false
while(!isActorReady) {
val futureRef = Await.ready(MyActor.select.resolveOne(timeout), timeout)
futureRef.value.get match {
case Success(_) =>
assertions
isActorReady = true
case Failure(_) =>
Thread.sleep(2000)
}
}
}
it should "process actor request and response via controller endpoint" in {
awaitableActorTest {
val result = route(routes.MyController.index).get
status(result) shouldBe OK
contentAsString(result) shouldBe "success"
}
}
}
What I get out of this awaitableActorTest pattern is a really clean way of reliably hitting my controller endpoint only when my actor is alive and available to process messages. If it's not, my ActorRef using its selection companion won't resolve in the future with success -- instead it completes with failure. When it fails I sleep a few seconds and repeat the loop. I break the loop when I its selection successfully resolved an ActorRef.
I'm unfamiliar with the QuartzSchedulerExtension to which you refer in your code snippet, but I hope my example is useful.
I'm playing with the akka-stream-and-http-experimental 1.0. So far, I've a user service that can accept and respond to HTTP requests. I'm also going to have an appointment service that can manage appointments. In order to make appointments, one must be an existing user. Appointment service will check with the user service if the user exists. Now this obviously can be done over HTTP but I'd rather have the appointment service send a message to the user service. Being new to this, I'm not clear how to use actors (as akka-http abstracts that) to send and receive messages. There's mention of ActorRef and ActorPublisher in the doc but no examples of the former and the later looks like an overkill for my need.
My code looks like the following and is on Github:
trait UserReadResource extends ActorPlumbing {
val userService: UserService
val readRoute = {
// route stuff
}
}
trait ActorPlumbing {
implicit val system: ActorSystem
implicit def executor: ExecutionContextExecutor
implicit val materializer: Materializer
def config: Config
val logger: LoggingAdapter
}
trait UserService { // Implemented by Slick and MongoDB in the backend
def findByFirstName(firstName: String): Future[immutable.Seq[User]]
}
object UserApp extends App with UserReadResource with UserWriteResource with ActorPlumbing {
override implicit val system = ActorSystem()
override implicit def executor = system.dispatcher
override implicit val materializer = ActorMaterializer()
override def config = ConfigFactory.load()
override val logger = Logging(system, getClass)
private val collection = newCollection("users")
val userRepository = new MongoDBUserRepository(collection)
val userService: UserService = new MongoDBUserRepositoryAdapter(userRepository) with UserBusinessDelegate {
// implicitly finds the executor in scope. Ain't that cute?
override implicit def executor = implicitly
}
Http().bindAndHandle(readRoute ~ writeRoute, config.getString("http.interface"), config.getInt("http.port"))
}
Edit:
I figured out how to send messages, which could be done using Source.actorRef. That only emits the messages into the stream. What I'd like to do is for the route handler class to receive the response. That way when I create the appointment service, it's actor can call the user service actor and receive the response in the same manner as the user route handler in my example does.
Pseudo code:
val src = Source.single(name) \\ How to send this to an actor and get the response
Edit 2:
Based on the #yardena answer, I came up with the following but the last line doesn't compile. My actor publisher returns a Future which I'm guessing will be wrapped in a Promise and then delivered as a Future to the route handler.
get {
parameters("firstName".?, "lastName".?).as(FindByNameRequest) { name =>
type FindResponse = Future[FindByNameResponse]
val src: Source[FindResponse, Unit] = Source.actorPublisher[FindResponse](businessDelegateProps).mapMaterializedValue {
_ ! name
}
val emptyResponse = Future.apply(FindByNameResponse(OK, Seq.empty))
val sink = Sink.fold(emptyResponse)((_, response: FindResponse) => response)
complete(src.runWith(sink)) // doesn't compile
}
}
I ended up with using Actor.ask. Simple.
This link may be helpful: http://zuchos.com/blog/2015/05/23/how-to-write-a-subscriber-for-akka-streams/ and this answer by #Noah Accessing the underlying ActorRef of an akka stream Source created by Source.actorRef
Basically you have 2 choices:
1) if you want a "simple" actor, which will forward into the stream all messages that it receives, you can use Source.actorRef. Then you can pipeline the messages into UserService by creating a processing stage using mapAsync.
2) Another option, in case you want the actor to have some custom behavior, is to write your own ActorPublisher.
HTH
In my Spray route, I delegate to an actor to process the request. The RequestContext is sent in the message to that actor.
path("mypath") {
parameters("thing".as[String]) { thing => ctx =>
myActor ! ProcessThingAndResondToContext(thing, ctx)
}
}
In my test, I substitute a TestProbe for the actor, because the actor's processing is expensive.
class MySpec extends Specification with Specs2RouteTest with ScalaCheck with MyService {
val testProbe = TestProbe()
override val myActor = testProbe.ref
def is = s2"""
it should $doTheRightThing
"""
def doTheRightThing = {
Get(s"/mypath?thing=fruit") ~> route ~> check {
testProbe.expectMsgClass(classOf[ProcessThingAndResondToContext])
status mustEqual StatusCodes.Success
}
}
This spec fails because there's no status. The TestProbe does nothing and so the ctx was never responded to.
Request was neither completed nor rejected within 1 second
The line status mustEqual StatusCodes.Success is not crucial to my test, but I can't remove it because then the spec doesn't compile - the method no longer typechecks as a MatchResult.
How can I test the route delegated to the actor?
I didn't solve the question, but resolved my problem by using a TestActorRef instead of a TestProbe. Then I could specify a simplified behaviour and still respond to ctx.