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.
Related
I'm attempting to shoot fire off a POST request like the one in the Akka docs showed here. http://doc.akka.io/docs/akka-http/current/scala/http/client-side/request-level.html#request-level-api
However, I'm trying to do it as inside of another defined class. If I try to add in anything that requires the Actor context like val http = HTTP(context.system) I get an error. How can I pass in the context into the class I'm trying to make the POST request from?
trait CustomerProfilesComponent {
def customerProfileManager: CustomerService.Async
}
object DefaultCustomerProfiles {
private case class ProfileUpdateData(path: Seq[String], data: JsObject, metadata: JsObject)
}
trait DefaultCustomerProfiles extends CustomerProfilesComponent {
self: DatabaseComponent with SourceVersionComponent with ExecutionContextComponent =>
import DefaultCustomerProfiles._
lazy val customerProfileManager = new CustomerService.Async {
import db.api._
import AvroConverter._
override def getVersion : Future[AvroVersion] = {
Future.successful(toAvro(sourceVersion))
}
}
What you need is actually an actor system. Posting from the akka-http docs:
The request-level API is implemented on top of a connection pool that is shared inside the ActorSystem.
There are two usage scenarios for the request API:
use it within an actor - when you can get the actor system via the actor context (like you have tried, but since you are not inside an actor, you don't have an actor context available)
use it outside an actor (your case) - when you can get the actor system by providing it as an dependency (either via class/method params or implicit params)
I hope this is helpful.
I am just starting with Akka Http (and Scala) and was wondering if there are any well-defined patterns for structuring Akka code. In particular, I am looking for structural patterns for composing/aggregating routes dynamically. In particular, I'm looking for a solution similar to the below pseudocode:
trait MyActor extends Actor {
val myRouter: ActorRef = context.actorOf(FromConfig.props(Props[MyWorker]), "Worker")
val myRoute = .... //route definition for this trait
}
trait MySecondActor extends Actor {
val mySecondRouter: ActorRef = context.actorOf(FromConfig.props(Props[MySecondWorker]), "SecondWorker")
val myRoute = .... //route definition for this trait
}
Then, in my main server just mix in the traits to get both actors and routes automagically:
class HttpServer extends SomeTrait with MyActor with MySecondActor {
.....
.....
}
There are some obvious problems with the above pattern including:
All traits inherit from Actor and then mix into the same class - not sure what the final behaviour will be.
The HttpServer class itself becomes an actor and cannot be instantiated by new HttpServer()
The routes are not concatinated.
What I'm looking for is a pattern that:
Allows me to separate routing logic in various packages and preferable bundle it with corresponding actors.
Make routes dynamic and discoverable instead of specifying them at boot time.
I came across the following two on StackOverflow but was wondering if there's a better approach and a well-defined pattern:
akka-http with multiple route configurations (This isn't really dynamic)
How to aggregate akka-http routes using a trait? (Old question using reflection)
Thanks!
When using akka-http you usually don't need actors to implement the routing. Actors are usually only used to access the business logic asynchronously. Let me give you an example on how to structure your code with predefined routes. This is the pattern I learned from Heiko Seeberger.
First create an object creating your routes:
object Api {
def route: Route = {
import import akka.http.scaladsl.server.Directives._
pathSingleSlash {
get {
complete(StatusCodes.OK)
}
}
}
}
If you need an actor to access your business logic you can pass it as parameter to the route method. Remember to use the ask pattern, when interacting with actor during request handling.
Than you create a Root actor which creates your Api:
final class Root extends Actor with ActorLogging {
Http(context.system)
.bindAndHandle(Api.route, "0.0.0.0", 8000)
.pipeTo(self)
override def receive: Receive = {
case s: ServerBinding =>
log.info(s"Listening to ${s.localAddress}")
context.become(Actor.emptyBehavior)
case Status.Failure(t) =>
log.error(t, "Error binding to network interface")
context.stop(self)
}
}
Finally you need some main method to create the actor system and the Root actor:
object Main {
def main(args: Array[String]): Unit = {
val system = ActorSystem("user-api")
system.actorOf(Props(new Root))
Await.ready(system.whenTerminated, Duration.Inf)
}
}
So this would be my take on best practices for defining akka-http routes. What is not covered in my answer is how to dynamically discover routes, but to be honest, I fail to see the use case here. Usually your system should have some well defined end points. How would users know which endpoints they can talk to if even the system does not know which endpoints it will serve at startup time?
At the receival of a TCP specific message, i need to query my DB. For that, i created an Actor that is called DBActor and it's loaded in Application.scala file
class Application #Inject() (system: ActorSystem) extends Controller {
val tcpServer = system.actorOf(Props[TCPServer], "tcpserver")
val dbActor = system.actorOf(Props[DBActor], "dbActor")
}
Tcp server is the actor that received the message and need to push it on DB Actor with that
val handler = context.actorSelection("/dbActor")
DB actor is so initialized in this way, according to Play Framework specifications
object DBActor {
def props() =
Props(classOf[DBActor])
}
class DBActor #Inject() (protected val dbConfigProvider:
DatabaseConfigProvider) extends Actor
with HasDatabaseConfigProvider[JdbcProfile]
with ActorLogging
with TableComponent {
import akka.io.Tcp._
import driver.api._
val table = TableQuery[Table]
def receive: Receive = {
case GetConfig(id) => {
sender ! Await.result(db.run(table.filter(_.id=== id).result.headOption),
Duration.Inf)
.map { x => x.config }
}
}
}
At the moment, actor is not constructed due to the error
Error injecting constructor, java.lang.IllegalArgumentException:
no matching constructor found on class tcp.DBActor for arguments []
at controllers.Application.<init>(Application.scala:17)
at controllers.Application.class(Application.scala:17)
So i need a way to inject the db configuration in the DBactor for querying the database or an alternative. I evaluated before that to inject a DAO or transforming the DAO I needed into an actor, both failed.
Question at this point is, does it make any sense giving an actor the access to the db or, at least, a controller? If can't be done, what are the alternatives?
What you need is an injected actor. The full description can be found here in the play documentation (https://www.playframework.com/documentation/2.5.x/ScalaAkka#Dependency-injecting-actors), but here's the gist of it:
You define the actor binding like so:
bindActor[DBActor]("db-actor")
And inject the actor in the controller like so:
class Application #Inject() (#Named("db-actor") dbActor: ActorRef) extends Controller {
On a different note, you should avoid Await.result whenever possible. In your scenario, this could probably easily be replaced with:
val senderRef = sender()
db.run(table.filter(_.id=== id).result.headOption)
.map(res => senderRef ! res.config)
Note that the sender ref is stored before, because it is no longer valid inside the map (see scaladoc of the sender() method).
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"
}
}
There is parent actor which in preStart creates child actors, one for every "active" user. Now I'd like to write a test asserting that it really happens, I mean child actors are really created for preconfigured users.
class IntegrationActor(settingsDao: UserSettingsDAO, childMaker: ActorRefFactory => UserId => ActorRef) extends Actor with StrictLogging {
val createUserActor = childMaker(context)
override def preStart(): Unit = {
val e = settingsDao.findAllEnabled()
e.map(_.userId).foreach(createUserActor)
}
// ...
}
I have test like this, but it doesn't work as all is happening off-main-test-thread
// mockMaker is already defined/mocked
it should "create child actors for users with enabled integration when started" in {
// given
val us = UserSettings(...)
Mockito.when(settingsDao.findAllEnabled()).thenReturn(Set(us))
// when
system.actorOf(Props(new IntegrationActor(settingsDao, mockMaker)))
// then
verifyMockMakerCalledFor(us)
}
I was trying to assign CallingThreadDispatcher to parent actor, but with no luck.
Theoretically I could just new this class and test it as usual, but Actor's can't be new-ed by hand.
Is there any other way to do this other than extracting stuff to trait with self type being Actor? Not sure it's the right way.