简体   繁体   English

Play + Akka-加入集群并在另一个ActorSystem上询问actor

[英]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. 我能够使Play应用加入现有的Akka集群,然后对在另一个ActorSystem上运行的actor进行询问,并返回结果。 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? 我怀疑Play正在启动自己的Akka集群吗? I am really not sure what it means. 我真的不确定这意味着什么。

    Could not register Cluster JMX MBean with name=akka:type=Cluster as it is already registered. 无法注册name=akka:type=Cluster集群JMX MBean,因为它已被注册。 If you are running multiple clust ers in the same JVM, set 'akka.cluster.jmx.multi-mbeans-in-same-jvm = on' in config` 如果您在同一个JVM中运行多个集群,请在配置中设置“ akka.cluster.jmx.multi-mbeans-in-same-jvm = on”

  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. 现在,每次我收到不正确的方法请求到Controller时,我都会重新初始化actor系统。 I am new to Scala, Akka, Play thing and having difficulty figuring out how to make it Singleton service and inject into my controller. 我是Scala,Akka和Play的新手,很难弄清楚如何使其成为Singleton服务并注入我的控制器。

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}
}

Answers 答案

  1. Refer to this URL. 引用此URL。 https://www.playframework.com/documentation/2.6.x/ScalaAkka#Built-in-actor-system-name has details about configuring the actor system name. https://www.playframework.com/documentation/2.6.x/ScalaAkka#Built-in-actor-system-name包含有关配置actor系统名称的详细信息。

  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. 您不应在每个请求上初始化actor系统,而应在Application类中使用Play注入的actor系统,如果要自定义Actor系统,则应通过修改AKKA配置来实现。 For that, you should create your own ApplicationLoader extending GuiceApplicationLoader and override the builder method to have your own AKKA configuration. 为此,您应该创建自己的ApplicationLoader扩展GuiceApplicationLoader并重写builder方法以具有自己的AKKA配置。 Rest of the things taken care by Play like injecting this actor system in Application for you. Play照顾的其他事情,例如在您的Application中注入这个actor系统。

Refer to below URL 请参阅下面的URL

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM