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