indusBull
indusBull

Reputation: 1854

Play + Akka - Join the cluster and ask actor on another ActorSystem

I am able to make Play app join the existing Akka cluster and then make ask call to actor running on another ActorSystem and get results back. But I am having trouble with couple of things -

  1. I see below in logs when play tries to join the cluster. I suspect that Play is starting its own akka cluster? I am really not sure what it means.

    Could not register Cluster JMX MBean with name=akka:type=Cluster as it is already registered. If you are running multiple clust ers in the same JVM, set 'akka.cluster.jmx.multi-mbeans-in-same-jvm = on' in config`

  2. Right now I m re-initializing the actorsystem every time when the request comes to Controller which I know is not right way do it. I am new to Scala, Akka, Play thing and having difficulty figuring out how to make it Singleton service and inject into my controller.

So far I have got this -

class DataRouter @Inject()(controller: DataController) extends SimpleRouter {
  val prefix = "/v1/data"

  override def routes: Routes = {
    case GET(p"/ip/$datatype") =>
      controller.get(datatype)

    case POST(p"/ip/$datatype") =>
      controller.process

  }

} 

case class RangeInput(start: String, end: String)
object RangeInput {

  implicit val implicitWrites = new Writes[RangeInput] {
    def writes(range: RangeInput): JsValue = {
      Json.obj(
        "start" -> range.start,
        "end" -> range.end

      )
    }
  }
}

@Singleton
class DataController @Inject()(cc: ControllerComponents)(implicit exec: ExecutionContext) extends AbstractController(cc) {

  private val logger = Logger("play")
  implicit val timeout: Timeout = 115.seconds
  private val form: Form[RangeInput] = {
    import play.api.data.Forms._

    Form(
      mapping(
        "start" -> nonEmptyText,
        "end" -> text
      )(RangeInput.apply)(RangeInput.unapply)
    )
  }


  def get(datatype: String): Action[AnyContent] = Action.async { implicit request =>
    logger.info(s"show: datatype = $datatype")
    logger.trace(s"show: datatype = $datatype")
    //val r: Future[Result] = Future.successful(Ok("hello " + datatype ))
    val config = ConfigFactory.parseString("akka.cluster.roles = [gateway]").
      withFallback(ConfigFactory.load())
    implicit val system: ActorSystem = ActorSystem(SharedConstants.Actor_System_Name, config)
    implicit val materializer: ActorMaterializer = ActorMaterializer()
    implicit val executionContext = system.dispatcher

    val ipData = system.actorOf(
      ClusterRouterGroup(RandomGroup(Nil), ClusterRouterGroupSettings(
        totalInstances = 100, routeesPaths = List("/user/getipdata"),
        allowLocalRoutees = false, useRoles = Set("static"))).props())

    val res: Future[String] = (ipData ? datatype).mapTo[String]
    //val res: Future[List[Map[String, String]]] = (ipData ? datatype).mapTo[List[Map[String,String]]]
    val futureResult: Future[Result] = res.map { list =>
      Ok(Json.toJson(list))
    }
    futureResult
  }

  def process: Action[AnyContent] = Action.async { implicit request =>
    logger.trace("process: ")
    processJsonPost()
  }

  private def processJsonPost[A]()(implicit request: Request[A]): Future[Result] = {
    logger.debug(request.toString())
    def failure(badForm: Form[RangeInput]) = {
      Future.successful(BadRequest("Test"))
    }

    def success(input: RangeInput) = {
      val r: Future[Result] = Future.successful(Ok("hello " + Json.toJson(input)))
      r
    }

    form.bindFromRequest().fold(failure, success)
  }

}

akka {
  log-dead-letters = off
  log-dead-letters-during-shutdown = off
  actor {
    provider = "akka.cluster.ClusterActorRefProvider"
  }
  remote {
    log-remote-lifecycle-events = off
    enabled-transports = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = ${myhost}
      port = 0
    }
  }
  cluster {
    seed-nodes = [
      "akka.tcp://MyCluster@localhost:2541"
    ]

} seed-nodes = ${?SEEDNODE}
}

Upvotes: 1

Views: 538

Answers (1)

Gajendra Naidu
Gajendra Naidu

Reputation: 405

Answers

  1. Refer to this URL. https://www.playframework.com/documentation/2.6.x/ScalaAkka#Built-in-actor-system-name has details about configuring the actor system name.

  2. You should not initialize actor system on every request, use Play injected actor system in the Application class, if you wish to customize the Actor system, you should do it through modifying the AKKA configuration. For that, you should create your own ApplicationLoader extending GuiceApplicationLoader and override the builder method to have your own AKKA configuration. Rest of the things taken care by Play like injecting this actor system in Application for you.

Refer to below URL

https://www.playframework.com/documentation/2.6.x/ScalaDependencyInjection#Advanced:-Extending-the-GuiceApplicationLoader

Upvotes: 0

Related Questions