How to send web socket message from server to client at particular time intervals? - scala

I have implemented a web socket server using Play Framework. The server can take connections and respond to clients.
If the connections are idle for some time then the server automatically closes the connection .
I am not sure if there is any configuration to make the connections always alive.
So in order to monitor the connection status (connection is alive or not), the server needs to send PING message to the client at a particular time
intervals and it should receive PONG from the client.
Below is my server implementation
#Singleton
class RequestController #Inject()(cc: ControllerComponents)(implicit system: ActorSystem, mat: Materializer) extends AbstractController(cc) {
def ws = WebSocket.accept[String, String] {req =>
ActorFlow.actorRef { out =>
ParentActor.props(out)
}
}
}
object ParentActor {
def props(out: ActorRef) = Props(new ParentActor(out))
}
class ParentActor(out : ActorRef) extends Actor {
override def receive: Receive = {
case msg: String => out ! s"Echo from server $msg"
}
}
So how to send web socket ping message from server to client at particular time intervals?

You can use a scheduler in order to send message to client in certain interval. Following is one of the example that can be use to implement your scenario:
class ParentActor(out : ActorRef) extends Actor {
var isPongReceived = false
override def receive: Receive = {
case "START" =>
// Client started the connection, i.e. so, initially let mark client pong receive status as true.
isPongReceived = true
out ! s"Echo from server - Connection Started"
// This part schedules a scheduler that check in every 10 seconds if client is active/connected or not.
// Sends PING response if connected to client, otherwise terminate the actor.
context.system.scheduler.schedule(new DurationInt(10).seconds,
new DurationInt(10).seconds) {
if(isPongReceived) {
// Below ensures that server is waiting for client's PONG message in order to keep connection.
// That means, next time when this scheduler run after 10 seconds, if PONG is not received from client, the
// connection will be terminated.
isPongReceived = false
out ! "PING_RESPONSE"
}
else {
// Pong is not received from client / client is idle, terminate the connection.
out ! PoisonPill // Or directly terminate - context.stop(out)
}
}
case "PONG" => isPongReceived = true // Client sends PONG request, isPongReceived status is marked as true.
}
}

Related

Grpc parallel Stream communication leads to error:AkkaNettyGrpcClientGraphStage

I have two services: one that sends stream data and the second one receives it using akka-grpc for communication. When source data is provided Service one is called to process and send it to service two via grpc client. It's possible that multiple instances of server one runs at the same time when multiple source data are provided at the same time.In long running test of my application. I see below error in service one:
ERROR i.a.g.application.actors.DbActor - GraphStage [akka.grpc.internal.AkkaNettyGrpcClientGraphStage$$anon$1#59d40805] terminated abruptly, caused by for example materializer or act
akka.stream.AbruptStageTerminationException: GraphStage [akka.grpc.internal.AkkaNettyGrpcClientGraphStage$$anon$1#59d40805] terminated abruptly, caused by for example materializer or actor system termination.
I have never shutdown actor systems but only kill actors after doing their job. Also I used proto3 and http2 for request binding. Here is a piece of my code in service one:
////////////////////server http binding /////////
val service: HttpRequest => Future[HttpResponse] =
ServiceOneServiceHandler(new ServiceOneServiceImpl(system))
val bound = Http().bindAndHandleAsync(
service,
interface = config.getString("akka.grpc.server.interface"),
port = config.getString("akka.grpc.server.default-http-port").toInt,
connectionContext = HttpConnectionContext(http2 = Always))
bound.foreach { binding =>
logger.info(s"gRPC server bound to: ${binding.localAddress}")
}
////////////////////client /////////
def send2Server[A](data: ListBuffer[A]): Future[ResponseDTO] = {
val reply = {
val thisClient = interface.initialize()
interface.call(client = thisClient, req = data.asInstanceOf[ListBuffer[StoreRequest]].toList)
}
reply
}
///////////////// grpc communication //////////
def send2GrpcServer[A](data: ListBuffer[A]): Unit = {
val reply = send2Server(data)
Await.ready(reply, Duration.Inf) onComplete {
case util.Success(response: ResponseDTO) =>
logger.info(s"got reply message: ${res.description}")
//////check response content and stop application if desired result not found in response
}
case util.Failure(exp) =>
//////stop application
throw exp.getCause
}
}
Error occurred exactly after waiting for service 2 response :
Await.ready(reply, Duration.Inf)
I can't catch the cause of error.
UPDATE
I found that some stream is missed such that service one sends an stream an indefinitely wait for the response and service two does not receive any thing to reply to service one but still don't know why stream is missed
I also updated akka grpc plugin but has no sense:
addSbtPlugin("com.lightbend.akka.grpc" % "sbt-akka-grpc" % "0.6.1")
addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.4")

Javascript WebSocket not connecting to Scala server

I am trying to implement a real-time game server in Scala, but I have a problem connecting to it with javascript's WebSocket. I tryed connecting with websocket.org/echo.html using either ws://localhost:8888, ws://127.0.0.1:8888 or ws://[my ip]:8888 .
Here's my server code, whch is pretty much the example from Akka's doc:
class MyServer(port: Int) extends Actor with ActorLogging{
var address: InetSocketAddress = new InetSocketAddress("", port)
var socketServer: ServerHandle = IOManager(context.system).listen(address)
override def receive = {
//Fired when the server is listening
case Listening(server, add) =>
log.info(s"The server is now listening on $add.")
//When a new client connect to the server...
case NewClient(server) =>
log.info("A new client just connected !")
server.accept()
//When a message is received by the server
case Read(socket, bytes) =>
log.info("The server just received some data.")
log.info(bytes.decodeString("US-ASCII"))
//When a connection with a client is closed
case Closed(socket, cause) =>
log.info(s"A client just disconnected : $cause")
}
}
object GemsApplication extends App{
println("Starting Gems application !")
val port = Option(System.getenv("PORT")).map(_.toInt).getOrElse(8888)
println(s"Selected port is $port.")
ActorSystem().actorOf(Props( new MyServer(port) ))
}
The odd thing is that I can connect to my server using Python sockets. Since they are may more low-level I'm guessing that I'll have to implement the handshake response to make it work with WebSocket, but shouldn't I see the message from the web socket asking for upgrade ? or even just connecting ?
Thanks for any insight you might have
Robin

How can we send data to a websocket client side function?

In play-scala websocket using Iteratee how can we send data to a websocket client side function? How can we specify the function in scala before doing the channel push?
My current websocket server code is as follows :
lazy val (out, channel) = Concurrent.broadcast[String] def connect = WebSocket.using[String] { request => val in = Iteratee.foreach[String] {
msg => println(msg)
channel push("I received your message: " + msg)
}
(in,out)
}
My current client code is as follow:
var ws = new WebSocket("ws://localhost:9000/connect.ws");
ws.onopen = function(){
console.log("Socket has been opened! ");
};
ws.onmessage = function(message) {
console.log(JSON.parse(message.data))
};
Every time I send a message using “channel push”, “ws.onmessage” is getting triggered. How can I emit/trigger the custom event in client side from server side?
Thank you very much in advance.

Child actor Termination validation

I have following actor hierarchy parent -> child -> worker
where child's life scope is tight to request - when request is completed child actor should be terminated. That I wanted to verify as a part of the test. I created StepParent as a for testing purposes as I want to validate the response for the given request which forwards to msg to testprobe.
class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
context.watch(context.actorOf(props = child, name = name))
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
}
My test looks as follows:
class ImagesControllerActorTest extends TestKit(ActorSystem("testsystem"))
with WordSpecLike with MustMatchers with StopSystemAfterAll {
val id = "456"
"ControllerActor" must {
"distribute a work to dedicated dedicated workers and combine back results and then terminate" in {
val p = TestProbe()
val ica = system.actorOf(Props(classOf[StepParent], createActorWithMockedWorkers(id, p.ref), "ControllerActor", p.ref), "parent")
p.send(ica, PersistImages(Set(new URL("http://success"), new URL("http://fail"))))
p.expectMsgPF(2 seconds)(validMsgPersistImageActor)
p.expectMsgPF(2 seconds)(validMsgPersistImageActor)
p.expectMsg(2 seconds, ImagesProcessed(id, Set(new URI("file:/"))))
p.expectMsg(4 seconds, Terminated)
}
}
My test is failing because of the last check for expected message:
assertion failed: timeout (4 seconds) during expectMsg while waiting for Terminated
java.lang.AssertionError: assertion failed: timeout (4 seconds) during expectMsg while waiting for Terminated
at scala.Predef$.assert(Predef.scala:179)
at akka.testkit.TestKitBase$class.expectMsg_internal(TestKit.scala:338)
...
According to the detailed log Terminated msg is forwarded as well(as per last line)
2015-01-11 17:41:10,386 [WARN ] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem#127.0.0.1:2555/user/parent/ControllerActor - id: 456 image url: http://fail FAILED
2015-01-11 17:41:10,386 [INFO ] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem#127.0.0.1:2555/user/parent/ControllerActor - id: 456 Processing completed with 1 downloded and 1 failed
2015-01-11 17:41:10,387 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka.tcp://testsystem#127.0.0.1:2555/user/parent - Msg forwarded to probe ImagesProcessed(456,Set(file:/))
2015-01-11 17:41:10,392 [DEBUG] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor/$b - stopped
2015-01-11 17:41:10,394 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent/ControllerActor - stopping
2015-01-11 17:41:10,396 [INFO ] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor/$b - Message [akka.dispatch.sysmsg.Terminate] from Actor[akka://testsystem/user/parent/ImagesControllerActor/$b#-426862126] to Actor[akka://testsystem/user/parent/ControllerActor/$b#-426862126] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
2015-01-11 17:41:10,396 [INFO ] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor/$a - Message [akka.dispatch.sysmsg.Terminate] from Actor[akka://testsystem/user/parent/ControllerActor/$a#1067345522] to Actor[akka://testsystem/user/parent/ControllerActor/$a#1067345522] was not delivered. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
2015-01-11 17:41:10,398 [DEBUG] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor - stopped
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/user/parent/ControllerActor/$a - stopped
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232]),Actor[akka://testsystem/user/parent/ControllerActor#-770422232])
**2015-01-11 17:41:10,400 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem#127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232])**
...
I do not really understand why last expectedMsg check doesn't work here since the message is forwarded as usual. Is there any special handling for Auto recieved messages?
Can someone pleas bring some shed into this?
Thx
UPDATE:
Tried to solve that as suggested - remove wrapper Envelope as follows:
class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
context.watch(context.actorOf(props = child, name = name))
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case mssg: Envelope =>
log.debug(s"Envelope msg forwarded to probe $mssg")
probe.tell(mssg.message, sender)
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
}
2015-01-11 23:52:16,352 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor - stopping
2015-01-11 23:52:16,354 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor - stopped
2015-01-11 23:52:16,358 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem#127.0.0.1:2555/user/parent - Msg forwarded to probe ImagesProcessed(456,Set(file:/))
2015-01-11 23:52:16,358 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139]),Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
2015-01-11 23:52:16,360 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem#127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
2015-01-11 23:52:16,365 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/system/testActor2 - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139]),Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
It is still failing and seems that something suspicious going on here:
2015-01-11 23:52:16,360 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem#127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ImagesControllerActor#-1965336139])
That is not correct message from StepParent but it gets somehow extracted.
StepParent actually receives an Envelope of the Terminated message.
You can see it from the log:
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232]),Actor[akka://testsystem/user/parent/ControllerActor#-770422232])
Yet, the Envelope message is then printed in your logs as if it was a Terminated while it is not.
The message is an Envelope in order to contain the sender metadata from the automatically sent message even though in this case, a Terminated message already contains the sender information.
So for your test to pass, you can do something like:
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case msg: Envelope =>
log.debug(s"Envelope msg forwarded to probe $msg")
probe.tell(msg.message, sender)
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
Your assertion for termination is not correct. As it currently is coded:
p.expectMsg(4 seconds, Terminated)
you are basically saying that you are expecting a message that is the Terminated type itself and not an instance of the Terminated case class. You should change the assertion to:
p.expectMsg(4 seconds, Terminated(refToBeTerminated))
or better yet:
p.expectTerminated(refToBeTerminated, 4 seconds)
where refToBeTerminated is the ActorRef that you are expecting to be terminated. I'm not sure if this is your only issue, but it is an issue for sure.
EDIT
Of course, if all you care about is that you got any kind of Terminate then you have multiple options to test that. You could try:
p.expectMsgType[Terminated](4 seconds)
or:
p.expectMsgClass(4 seconds, classOf[Terminated])
or even:
p.expectMsgPF(4 seconds){case t:Terminated => }
Unfortunately non of the proposed solution worked even though I was also convinced they could work and from auto receive logging the Terminated message were passed the only way I got this thing working is a kind of "ugly" solution by translated Terminated message to something else:
class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
context.watch(context.actorOf(props = child, name = name))
override def receive: Actor.Receive = {
case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
case msg: Terminated =>
log.debug("Parent: Terminated recieved")
probe.tell("controller terminated",sender)
case msg =>
log.debug(s"Msg forwarded to probe $msg")
probe.tell(msg, sender)
}
}
And the assertion logic works fine as:
p.expectMsg(4 seconds,"controller terminated")
Not sure what was going on as it seems that Terminated msg cannot be simply forwarded even though it was obviously received.

Scala with Play Framework 2.3.6: sending a message to all socket clients

I'm new to Scala and the Play Framework, so I'm experimenting a bit.
I've successfully created websockets, but I'd like to be able to send a message to multiple socket clients from a simple POST request.
For instance, I have 10 different random browsers connected to my socket (ws:// ... /websocket), and I can myself send a "POST: HELLO" to /newMessage. How do I make so this "HELLO" gets sent to each of the 10 clients ?
Here's the controller receiving the HELLO. Works fine and prints "Got: AnyContentAsText(HELLO)" :
def newMessage = Action { implicit request =>
println("Got: " + request.body)
/* add something here to send "request.body" to every socket client */
Ok("Got: " + request.body)
}
And here's my simple "Socket" controller that sends "Welcome" to connected clients :
object Socket extends Controller {
def txSocket = WebSocket.using[String] { request =>
// Log events to the console
val in = Iteratee.foreach[String](println).map { _ =>
println("Disconnected")
}
// Send a single 'Welcome!' message
val out = Enumerator("Welcome!")
(in, out)
}
}
How can I, from my "Message" controller, send request.body to the websocket ?
Thank you for your time !
Each websocket connection makes a new actor. You need to select the actors and send the message to them.
Like so.
object Application extends Controller {
def connect = WebSocket.acceptWithActor[JsValue, JsValue] { request => out =>
ClientActor.props(out)
}
def broadcast = Action { _ =>
system.actorSelection("akka://application/system/websockets/*/handler") ! "msg"
Ok
}
def system = play.api.libs.concurrent.Akka.system(play.api.Play.current)
}