PlayFramework - Actor 对接

playframework 本身就是akka、那如何对接其它的akka系统呢、也就是发消息给其它系统、或者其它系统连接playframework的akka。由于网上的demo几乎是没有的、在没有API的条件下研究了一周终于搞定了。

使用指南

使用的版本是Play 2.7.x

libraryDependencies ++= Seq(
  guice,
  ws,
  filters,
  "org.scalatestplus.play" %% "scalatestplus-play" % "4.0.1" % Test,
  "com.typesafe.akka" %% "akka-actor" % "2.5.21",
  "com.typesafe.akka" %% "akka-slf4j" % "2.5.21",
  "com.typesafe.akka" %% "akka-remote" % "2.5.21",
  "com.typesafe.akka" %% "akka-stream" % "2.5.21",
  "com.typesafe.akka" %% "akka-protobuf" % "2.5.21"
)

添加下面配置到application.conf、很重要

akka {
  actor {
    warn-about-java-serializer-usage = false
    provider = "akka.remote.RemoteActorRefProvider"
  }

  remote {
    enabled-transports = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = "localhost"
      port = 2553
    }
  }
}

play.akka.actor-system = "demo"

actor.debug.receive = on
actor.default-dispatcher.fork-join-executor.pool-size-max = 64

Controller 即可以作为服务端也可以作为客户端

@Singleton
class ActorController @Inject()(cc: ControllerComponents, actorSystem: ActorSystem)(implicit ec: ExecutionContext)
  extends AbstractController(cc) {

  val testActor: ActorRef = actorSystem.actorOf(Props[HelloActor], "hello")
  implicit val timeout: Timeout = 3.seconds

  def PING(): Action[AnyContent] = Action.async {
    (testActor ? Message.PING).mapTo[String].map(message => Ok(message))
  }

  def send(): Action[AnyContent] = Action {
    actorSystem.actorOf(Props(new HiActor()))
    Ok("success")
  }


  def hi: Action[AnyContent] = Action {
    Ok("hi")
  }

}

作为服务端的Actor

class ServerActor extends Actor with ActorLogging {
  override def receive: Receive = {
    case Message.PING(msg: String) =>
      println(s"收到客户端发来的消息:$msg")
      sender() ! Message.PONG("我是服务端")
  }
}

作为客户端的Actor

class ClientActor extends Actor with ActorLogging {

  override def preStart(): Unit = {
    val actor = context.actorSelection(s"akka.tcp://ServerSystem@localhost:2000/user/ServerActor")
    actor ! Message.PING("我是客户端")
  }

  override def receive: Receive = {
    case Message.PONG(msg: String) =>
      println(s"收到服务端发来的消息:$msg")
  }
}

注意

PS:测试之前一定要访问 http://localhost:9000/init
因为在开发环境下、是需要访问之后才会进行编译的、不然如下警告

[INFO] [05/31/2019 12:47:49.069] [main] [akka.remote.Remoting] Starting remoting
[INFO] [05/31/2019 12:47:49.238] [main] [akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://LocalSystem@localhost:57285]
[INFO] [05/31/2019 12:47:49.240] [main] [akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://LocalSystem@localhost:57285]
[WARN] [05/31/2019 12:47:49.434] [LocalSystem-akka.remote.default-remote-dispatcher-5] [akka.tcp://LocalSystem@localhost:57285/system/endpointManager/reliableEndpointWriter-akka.tcp%3A%2F%2Fdemo%40localhost%3A2553-0] Association with remote system [akka.tcp://demo@localhost:2553] has failed, address is now gated for [5000] ms. Reason: [Association failed with [akka.tcp://demo@localhost:2553]] Caused by: [java.net.ConnectException: Connection refused: localhost/127.0.0.1:2553]
[WARN] [05/31/2019 12:47:49.434] [New I/O boss #3] [NettyTransport(akka://LocalSystem)] Remote connection to [null] failed with java.net.ConnectException: Connection refused: localhost/127.0.0.1:2553
[INFO] [05/31/2019 12:47:49.437] [LocalSystem-akka.actor.default-dispatcher-3] [akka://LocalSystem/deadLetters] Message [controllers.Message$PING] from Actor[akka://LocalSystem/user/LocalActor#1787938353] to Actor[akka://LocalSystem/deadLetters] was not delivered. [1] dead letters encountered. If this is not an expected behavior, then [Actor[akka://LocalSystem/deadLetters]] may have terminated unexpectedly, This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.

测试

客户端连接服务端、发送消息给playframework


object ActorNodeTest {

  def main(args: Array[String]): Unit = {

    val str: String =
      """
        |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
        |akka.actor.warn-about-java-serializer-usage = off
        |akka.remote.netty.tcp.hostname = localhost
        |akka.remote.netty.tcp.port = 0
      """.stripMargin
    val conf = ConfigFactory.parseString(str)
    val actorSystem = ActorSystem("LocalSystem", conf)
    actorSystem.actorOf(Props(new Actor() {

      override def preStart(): Unit = {
        val a = context.actorSelection("akka.tcp://demo@localhost:2553/user/hello")
        a ! Message.PING("我是客户端")
      }

      override def receive: Receive = {
        case Message.PONG(msg:String) => println(s"收到服务器发来的消息:$msg")
      }
    }), "LocalActor")
  }

}

作为服务端启动让playframework发来消息

object ActorServerTest {

  def main(args: Array[String]): Unit = {

    val str: String =
      """
        |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
        |akka.actor.warn-about-java-serializer-usage = off
        |akka.remote.netty.tcp.hostname = localhost
        |akka.remote.netty.tcp.port = 2000
      """.stripMargin
    val conf = ConfigFactory.parseString(str)
    val actorSystem = ActorSystem("ServerSystem", conf)
    actorSystem.actorOf(Props(new Actor() {
      override def receive: Receive = {
        case Message.PING(msg: String) =>
          println(s"收到客户端发来的消息:$msg")
          sender() ! Message.PONG("我收到了")
      }
    }), "ServerActor")
  }

}

成功结果如下

client -> server

[INFO] [05/31/2019 12:49:43.192] [main] [akka.remote.Remoting] Starting remoting
[INFO] [05/31/2019 12:49:43.366] [main] [akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://LocalSystem@localhost:57770]
[INFO] [05/31/2019 12:49:43.367] [main] [akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://LocalSystem@localhost:57770]
收到服务器发来的消息:我是服务端

[info] play.api.Play - Application started (Dev) (no global state)
收到客户端发来的消息:我是客户端

server <- client

[INFO] [05/31/2019 12:50:39.695] [main] [akka.remote.Remoting] Starting remoting
[INFO] [05/31/2019 12:50:39.868] [main] [akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://ServerSystem@localhost:2000]
[INFO] [05/31/2019 12:50:39.869] [main] [akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://ServerSystem@localhost:2000]
收到客户端发来的消息:我是客户端

[info] play.api.Play - Application started (Dev) (no global state)
收到服务端发来的消息:我收到了

项目源码 https://github.com/dounine/play-actor

你可能感兴趣的:(playframework)