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?
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'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 trying to refactor an Akka application from a single tier to multi-tier structure with akka clustering (http://doc.akka.io/docs/akka/2.3.9/scala/cluster-usage.html). Within the application are some actors that have a parameterized default constructor, e.g.
class MyActor(someParam: boolean = true) extends Actor {
def receive = { /* message handling here... */ }
}
I am now trying to create a generic router actor that will route to a node with a specific node. The cluster configuration is working based of the example on the akka site, and I can create a router in 1 role that routes to the actor in another role:
class MyActorProxy extends Actor {
val workerRouter = context.actorOf(FromConfig.props(Props(classOf[MyActor]), name = "workerRouter")
def receive = {
case msg: Any =>
val currentSender = sender()
workerRouter.tell(msg, currentSender)
}
}
This method works as needed. However, I need to go and create a handful of proxy classes with the same functionality, and it's painful. I'm trying to make it work better, and limited scala experience is making it difficult. Here's what I am trying to do:
abstract class RouterProxy[T <: Actor](routerPath: String) extends Actor {
val router = context.actorOf(FromConfig.props(Props(classOf[T])),name=routerPath)
def receive = {
case msg: Any => {
val currentSender = sender()
router.tell(msg,currentSender)
}
}
}
class MyActorProxy extends RouterProxy[MyActor](routerPath = "myActorRouter")
Have come up with this by reading through some scala docs and through stackoverflow, but can't seem to get it right - this approach doesn't let me create the props for the actor. I've seen How to instantiate an instance of type represented by type parameter in Scala and I think that the type information is being lost (type erasure).
How would I go about passing an actor class to the router proxy to allow for router initialization?
This is the error I get during compilation, while initializing the router:
class type required but T found
edit
Background for using a generic proxy router
The reason for this approach is to refactor the application for both single tier and multi-tier architecture. When running multi-tier, I want to skip initializing any "heavy" actors. Given I have the following config on a frontend node:
akka.actor.deployment {
/workerRouter {
routee.paths = ["/user/myActor"]
cluster.use-role = backend
}
}
akka.cluster.roles = [frontend]
and the backend node has roles of [backend], then when I start up frontend nodes, MyActor will not be initialized. MyActor is an example of 1 of many actors that are supervisors / managers. Each 1 of them might initialize a few other actors in turn (sometimes with routers). What I'm trying to do is refactor the application so I can run lightweight frontend nodes in a different tier to resource heavy backend nodes if I need to, but also still have the ability to run all on a single node. By this approach, I can add in initialization for heavy managers into my application bootstrap and add a role and it becomes a multifunctional app without needing recoding.
edit 2
I am able to get the application to work as expected if the class constructor doesn't take any arguments.
MyActor extends Actor { /* ... */ }
And then in my abstract class:
abstract class RouterProxy[T <: Actor](routerPath: String) extends Actor {
val router = context.actorOf(FromConfig.props(Props[T]),name = routerPath)
}
Props[MyActor] works fine for a parameterless actor, but Props(classOf[T]) looses the type.
You don't actually have to define another class for the router, you can do it all through your configuration file:
val myActorRouter = system.actorOf(Props[MyActor]
.withRouter(FromConfig())
,"workerRouter")
Where "workerRouter" is defined in your application.conf file like this:
akka.actor.deployment {
/workerRouter {
router = round-robin
nr-of-instances = 5
}
}
You can also configure it all through code if you have a pretty simple configuration:
val myActorRouter = system.actorOf(Props[MyActor].withRouter(
RoundRobinRouter(nrOfInstances = 5)))
Either way you end up with myActorRouter as an actor ref you can send messages to. In the background the Akka system has created a pool of MyActor actors and forwards the messages to them. The router takes itself out of the response so you don't need to spoof the sender like you've done: when a routee sends a message to sender it goes directly back to the actor that sent the message to the router.
is it possible in Akka (scala) to get a reference to an existing ActorSystem?
I am working on a Spray application with another Actor for DB. I am also extending Directives to have an object per path. the directives are not actors by themselves, but they need to pass messages to the DBActor. here:
class HttpActor extends Actor with HttpService {
val actorRefFactory = context
def receive = runRoute(
IndexService.route ~
HostsService.route
)
}
object HostsService extends Directives{
def route(implicit dm: DetachMagnet2) = {
path("hosts") {
get {
detach() {
**dbActor ! CreateHost**
complete("get me hosts!")
}
} ~
post {
detach() {
entity(as[String]) { payload =>
complete(s"post hosts $payload")
}
}
}
}
}
}
is there a way for HostsService to discover the ActorSystem itself so he can find the DBActor, or must HttpActor pass it in? the latter seems less elegant, as it HostsService will need to become a class (not an object), so no longer a singleton.
From here:
there was a ticket for creating such a registry, but we were not
satisfied with what we got when trying to specify the semantics in
detail. One part is that we removed all global state so that different
parts of an application can use Akka without having to worry about
each other and a global feature would break this. Another is that it
would encourage get-or-create usage—my pet peeve—which would make the
semantics unclear: you give a name and a config, but if the name
already exists you potentially get back a differently configured
system (which is usually quite fatal).
There is nothing stopping you from putting a hashmap in some central
place of your application, (pre-)populate that with the actor systems
you need and be done, that's basically a one-liner (which is another
reason for not including it in Akka, because instead of a simple
solution to a very narrow problem we'd have to think of a solution to
a much more generic problem)
In your case, it's better to pass your system implicitly to the route function:
class HttpActor extends Actor with HttpService {
implicit val actorRefFactory = context
def receive = runRoute(
IndexService.route ~
HostsService.route
)
}
object HostsService extends Directives {
def route(implicit dm: DetachMagnet2, as: ActorContext) = {...}
}
I'm currently exploring using Scaldi for Dependency Injection in a Play2.2 application.
I have read the documentation on Scaldi's website, but what is unclear to me is how to use it with Akka.
What I have so far in my project:
Models/ (Daos and case classes)
User.scala
Services/ (Akka Actors)
UserService.scala
ProfileService.scala
Managers/ (Regular Manager Classes)
UserManager.scala (The Trait Interface)
UserManagerImpl.scala (An actual implementation)
UserManagerMock.scala (Mocked version)
etc..
In UserService.scala I would use an instance of the UserManager to do the work:
class UserService extends ServiceActor with Injection
{
val userManager = inject[UserManager]
def receive = {
case Register(email: String, password: String)
}
}
object UserService extends Service
{
case class Register(email: String, password: String)
override protected val actorRef = Akka.system.actorOf(Props[UserService].withRouter(SmallestMailboxRouter(resizer = Some(resizer))))
}
Then depending on the injected manager, the actor can be sort of mocked if it delegate all the work to the manager?
However, what if the managers needs to call other Services, which are just companion objects? Or Services calling other services that are also referenced via companion objects?
Does anyone have some pointers on how to integrate Akka with Scaldi?
You mentioned, that you are using companion objects as a services. I also noticed, that you are
creating actors inside of the objects. In general I will discourage you from doing this. Scala (companion) objects
are just singletons. While they can be useful and appropriate in some circumstances, in general they are considered to be
an anti-pattern rather than a pattern, especially if you want to do dependency injection or inversion of control in
your application. There are a lot of reasons for this, but the most important ones in this case are: it's hard to mock them,
it's hard to control their instantiation, and in general they represent an opposite of inversion of control.
Another problem, is that you are creating actors inside of these singleton objects. Very important aspect of actor model is
supervision hierarchy. By creating this actor
(UserService in your case) in isolation, you most probably let guardian actor to be it's supervisor, which in most case
is not what you want. So I would recommend to create most of the actors within another actors, except few,
that need to be top-level actors. This will make sure that they have proper supervision hierarchy.
These ideas also remain the same if you are using Scaldi. scaldi-akka provides
convenient way to inject an ActorRef or Props for some particular actor. Here is a small example of how you can
inject normal bindings and ActorRefs:
class ProfileManager (implicit inj: Injector) extends Injectable
trait UserManager {
def register(email: String, password: String): User
}
class UserManagerImpl(implicit inj: Injector) extends UserManager with Injectable {
val profileManager = inject [ProfileManager]
def register(email: String, password: String) = ???
}
class UserService(implicit inj: Injector) extends Actor with AkkaInjectable {
val userManager = inject [UserManager]
import UserService._
def receive = {
case Register(email, password) =>
userManager
}
}
object UserService {
case class Register(email: String, password: String)
}
class ReceptionistService(implicit inj: Injector) extends Actor with AkkaInjectable {
val userManager = injectActorRef [UserService]
def receive = ???
}
Please note, that injectActorRef creates and actor within the context of current actor. So the equivalent would
be:
val userManager = context.actorOf(injectActorProps[UserService])
Now you need to create binding for the ActorSystem (it's optional, and if you are using Play, you probably
need to get ActorSystem from the play application, which already has one), services (which are actors in your case)
and managers:
implicit val module = new Module {
bind [ActorSystem] to ActorSystem("MySystem")
binding toProvider new UserService
binding toProvider new ReceptionistService
bind [UserManager] to new UserManagerImpl
binding to new ProfileManager
}
It is important to bind Actors with toProvider. This will make sure, that each time Akka asks Scaldi for some
particular Actor, it will always get the new instance of it.
Now, if you want ReceptionistService to be your top-level actor, you can use it like this:
implicit val system = inject [ActorSystem]
val receptionist = injectActorRef [ReceptionistService]
receptionist ! DoStuff
In this case, systems guardian actor would be it's supervisor.