In the current version of the Play Framework, there is no way to have the WebSocket connection to be persistent.
https://www.playframework.com/documentation/2.8.x/ScalaWebSockets#Keeping-a-WebSocket-Alive
I have the following piece of code and the need for this WebSocket connection to be persistent.
class ProvisioningActor(sink: ActorRef) extends Actor {
private[this] val source = Observable.interval(appConfig.pingInterval).map(elem => elem.toInt)
private[this] val ping = Consumer.foreach[Int](x => self ! x)
private[this] val task = source.consumeWith(ping).runToFuture
override def receive: Receive = {
case jsValue: JsValue =>
logger.debug(s"Received OCPPCallRequest: \n ${Json.prettyPrint(jsValue)}")
jsValue.validate[OCPPCallRequest].asEither match {
case Right(ocppCall) => handleOCPPCallRequest(ocppCall).materialize.map {
case Failure(fail) => sink ! JsError(s"${fail.getMessage}")
case Success(succ) => sink ! Json.toJson(succ)
}
case Left(errors) =>
logger.error(s"Errors occurred when validating OCPPCallRequest: \n $errors")
sink ! Json.toJson(s"error -> ${errors.head._2}") // TODO: Work on this issue here on how we want to propagate errors
}
case x: Int =>
logger.debug(s"Elem: $x")
handleHeartBeatRequest(2, "HeartbeatRequest").materialize.map {
case Failure(fail) => sink ! JsError(s"${fail.getMessage}")
case Success(succ) => sink ! Json.toJson(succ)
}
case msg: Any =>
logger.warn(s"Received unknown message ${msg.getClass.getTypeName} that cannot be handled, " +
s"eagerly closing websocket connection")
task.cancel()
self ! PoisonPill
}
}
It kind of works be sending a heartbeat message back to the client. My question is:
Is this good enough for an implementation?
By default all WebSocket connections will be persistent and this may not be desired. So this has to be on a per connection basis. Correct?
Is there any other way that is advisable?
We use PlayFramework websockets for long running sessions, a busy server supports more than 1000 concurrent Websocket connections and lack of ping-pong packets causes idle websocket connections to be terminated by intermediate firewalls, proxies etc and also the play framework idleTimeout itself - play.server.https.idleTimeout.
Form PlayFramework v2.1(now with v2.8) we have been using Sockjs protocol, Play Sockjs - https://github.com/fdimuccio/play2-sockjs which uses a application layer heartbeat
https://github.com/fdimuccio/play2-sockjs/wiki/API-reference-for-0.5.x#configuring-sockjs-handler
package controllers
import scala.concurrent.duration._
import play.api.mvc._
import play.sockjs.api._
// mixin SockJSRouter trait with your controller
class SockJSController extends Controller with SockJSRouter {
// override this method to specify custom SockJSSettings
override protected val settings = SockJSSettings(websocket = false, heartbeat = 55 seconds)
// here goes the request handler
def sockjs = SockJS.accept[String, String] { request =>
...
}
}
We use 20s heartbeat in production which has proven very safe, each connection has the same heartbeat setting, which works well for our usecase.
This topic may be helpful: Play2.5 Java WebSockets
Related
I have a backend app writed in Scala Play. Because I have a realtime implementation using Akka Actors with data stored in a Redis server, I want as my each backend instance (deployed on centos servers) to be a Publisher and in same time a Subscriber to Redis service. Why this? Because a 3rd party app will send requests to my backend to update the data from Redis, and I want that all actors from all instances to push data to clients (frontend) indifferent on which backend instance is redirected this request (a load balancer is used there).
So, when instance1 will publish on Redis, I want that all subscribers(instance2, instance3, even instance1 because I said each instance must be pub/sub) to push data to clients.
I created an object with a Publisher and a Subscriber client and I was expecting that these will have a singleton behavior. But, for an unknown reason, over the night I see that my instances are unsubscribed from the Redis server without a message. I think this, because in the next day, my Redis service have 0 subscribers. I don't know if I have a bad implementation there or just Redis kill the connections after some time.
RedisPubSubServer.scala (In facts, here are just 2 Akka Actors which take RedisClient as params)
class Subscriber(client: RedisClient) extends Actor {
var callback: PubSubMessage => Any = { m => }
implicit val timeout = Timeout(2 seconds)
override def receive: Receive = {
case Subscribe(channel) => client.subscribe(channel)(callback)
case Register(cb) => callback = cb; self ? true
case Unsubscribe(channel) => client.unsubscribe(channel); self ? true
}
}
class Publisher(client: RedisClient) extends Actor {
implicit val timeout = Timeout(2 seconds)
override def receive: Receive = {
case Publish(channel, msg) => client.publish(channel, msg); self ? true
}
}
RedisPubSubClient.scala (here I create the Publisher and Subscriber as singleton)
object Pub {
println("starting publishing service...")
val config = ConfigFactory.load.getObject("redis").toConfig
val client = new RedisClient(config.getString("master"), config.getInt("port"))
val system = ActorSystem("RedisPublisher")
val publisher = system.actorOf(Props(new Publisher(client)))
def publish(channel: String, message: String) =
publisher ! Publish(channel, message)
}
object Sub {
val client = new RedisClient(config.getString("master"), config.getInt("port"))
val system = ActorSystem("RedisSubscriber")
val subscriber = system.actorOf(Props(new Subscriber(client)))
println("SUB Registering...")
subscriber ! Register(callback)
def sub(channel: String) = subscriber ! Subscribe(channel)
def unsub(channel: String) = subscriber ! Unsubscribe(channel)
def callback(msg: PubSubMessage) = {
msg match {
case S(channel, no) => println(s"subscribed to $channel and count $no")
case U(channel, no) => println(s"unsubscribed from $channel and count $no")
case M(channel, msg) => msg match {
case "exit" => client.unsubscribe()
case jsonString => // do the job
}
case E(e) => println(s"ERR = ${e.getMessage}")
}
}
}
and the RedisService
object RedisService {
val system = ActorSystem("RedisServiceSubscriber")
val subscriber = system.actorOf(Props(new Subscriber(client)))
subscriber ! Register(callback)
subscriber ! Subscribe("channelName")
// So, here I'm expecting that subscriber to have a life-cycle as the backend instance
}
from an api endpoint, I push data calling Pub publish method as:
def reloadData(request: AnyType) {
Pub.publish("channelName", requestAsString)
}
Can be possible as Publisher/Subscriber Actors to be killed after a while and due of that to throw in some errors for redis clients Pub/Sub?
For Publisher, I must say that I'm thinking to create the client each time when the api call is made, but for the Subscriber, I can not use another way that a singleton object which will listen the Redis entire life of the backend.
thanks
edit: used library:
"net.debasishg" %% "redisclient" % "3.41"
After some researches, I found another scala redis lib which seems to do exactly what I need in an easier maner
"com.github.etaty" %% "rediscala" % "1.9.0"
Consider this
def handle = WebSocket.accept[Array[Byte], Array[Byte]]
{
request =>
log.info("Handling byte-message")
ActorFlow.actorRef
{
out => MyActor.props(out)
}
}
Whenever a byte message is sent to the websocket, it gets delegated to the actor and before I get a log entry.
Works fine.
Now the same logic, with a Flow instead
def handle = WebSocket.accept[Array[Byte], Array[Byte]]
{
request =>
{
log.info("Handling byte-message")
Flow.fromSinkAndSource(sink, source).log("flow")
}
}
I'll add the rest of the code:
val tickingSource: Source[Array[Byte], Cancellable] =
Source.tick(initialDelay = 1 second, interval = 10 seconds, tick = NotUsed)
.map(_ => Wrapper().withKeepAlive(KeepAlive()).toByteArray)
val myActor = system.actorOf(Props{new MyActor(null)}, "myActor")
val serverMessageSource = Source
.queue[Array[Byte]](10, OverflowStrategy.backpressure)
.mapMaterializedValue { queue => myActor ! InitTunnel(queue)}
val sink = Sink.actorRefWithAck(myActor, InternalMessages.Init(), InternalMessages.Acknowledged(), InternalMessages.Completed())
val source = tickingSource.merge(serverMessageSource)
It has a keepAlive source, and an actual source, if the server wants to push something, merged.
The sink is again the actor.
Now the problem is, in this scenario I get EXACTLY one message from the client TO the server, even if it sends more, they do not get passed to myActor
At first I thought this may be due to the null reference passed to myActor here, but then the first one could not be processed either. I am out of ideas, what is causing this. The flow itself works, I get the keepAlive messages just fine and if I refresh the client (Scala.js) again, first request gets sent just fine to the server and server responds and all is well
edit to clarify:
I am NOT talking about the log entry here - I am sorry, I had another log entry in myActor and got myself confused.
If the client sends more than one message the server does not handle it. It never reaches the actor, although the client definitely sends it :(
What I would expect:
1) At first message from client to server, the websocket gets created
2) The websocket is kept alive by the server, via the tickingSource (that actually works!)
3) If the client sends another request, it gets handled by myActor and that also responds to the client over the websocket
So, 3) does not work. In fact, the client sends a message, but that never reaches myActor after the initial one :(
edit:
This is my actor logic for initializing the websocket/stream in myActor:
var tunnel: Option[SourceQueueWithComplete[Array[Byte]]] = None
override def receive: Receive = {
case i: InternalMessages.InitTunnel =>
log.info("Initializing tunnel")
tunnel = Some(i.sourceQueue)
case _: InternalMessages.Init =>
sender() ! InternalMessages.Acknowledged()
log.info("websocket stream initialized")
case _: InternalMessages.Completed =>
log.info("websocket stream completed")
case q: Question => {
tunnel match {
case Some(t) => t offer Answer()...
case None => log.error("No tunnel available")
}
}
}
object InternalMessages {
case class Acknowledged()
case class Init()
case class Completed()
case class InitTunnel(sourceQueue: SourceQueueWithComplete[Array[Byte]])
}
I have the feeling that you don't send acks after receiving the Question message, but you should as the akka docs says (http://doc.akka.io/docs/akka/current/scala/stream/stream-integrations.html#sink-actorrefwithack): It also requires the given acknowledgement message after each stream element to make back-pressure work.
I had almost the same problem in Java. But messages were not sent to the "actorRefWithAck" at all(just onInitMessage was received). The actor was remote and was sending "Acknowledged" message that was not the same instance as in Sink.actorRefWithAck() method. Adding equals method to the message resolved the issue.
#Override
public boolean equals(Object obj) {
return obj.getClass().equals(getClass());
}
I'm trying to write an Actor which connects to an Amazon Kinesis stream and then relays any messages received via Comet to a Web UI. I'm using Source.actorPublisher for this and using the json method with Comet in Play described here. I got the events working just fine using Source.tick(), but when I tried using an ActorPublisher, the Actor never seems to be sent any Request messages as expected. How are requests for data usually sent down an Akka flow? I'm using v2.5 of the Play Framework.
My controller code:
def subDeviceSeen(id: Id): Action[AnyContent] = Action {
val deviceSeenSource: Source[DeviceSeenMessage, ActorRef] = Source.actorPublisher(DeviceSeenEventActor.props)
Ok.chunked(deviceSeenSource
.filter(m => m.id == id)
.map(Json.toJson(_))
via Comet.json("parent.deviceSeen")).as(ContentTypes.JSON)
}
Am I doing anything obviously wrong in the above? Here is my Actor code:
object DeviceSeenEventActor {
def props: Props = Props[DeviceSeenEventActor]
}
class DeviceSeenEventActor extends ActorPublisher[DeviceSeenMessage] {
implicit val mat = ActorMaterializer()(context)
val log = Logging(context.system, this)
def receive: Receive = {
case Request => log.debug("Received request message")
initKinesis()
context.become(run)
case Cancel => context.stop(self)
}
def run: Receive = {
case vsm:DeviceSeenMessage => onNext(vsm)
log.debug("Received request message")
onCompleteThenStop() //we are currently only interested in one message
case _:Any => log.warning("Unknown message received by event Actor")
}
private def initKinesis() = {
//init kinesis, a worker is created and given a reference to this Actor.
//The reference is used to send messages to the actor.
}
}
The 'Received request message' log line is never displayed. Am I missing some implicit? There are no warnings or anything else obvious displayed in the play console.
The issue was that I was pattern matching on case Request => ... instead of case Request() => .... Since I didn't have a default case in my receive() method, the message was simply dropped by the Actor.
In my Spray app, I delegate requests to actors. I want to be able to kill a actor that takes too long. I'm not sure whether I should be using Spray timeouts, Akka ask pattern or something else.
I have implemented:
def processRouteRequest(system: ActorSystem) = {
respondWithMediaType(`text/json`) {
params { p => ctx =>
val builder = newBuilderActor
builder ! Request(p) // the builder calls `ctx.complete`
builder ! PoisonPill
system.scheduler.scheduleOnce(routeRequestMaxLife, builder, Kill)
}
}
}
The idea being that the actor lives only for the duration of a single request and if it doesn't complete within routeRequestMaxLife it gets forcibly killed. This approach seems over-the-top (and spews a lot of info about undelivered messages). I'm not even certain it works correctly.
It seems like what I'm trying to achieve should be a common use-case. How should I approach it?
I would tend to using the ask pattern and handling the requests as follows:
class RequestHandler extends Actor {
def receive = {
case "quick" =>
sender() ! "Quick Reply"
self ! PoisonPill
case "slow" =>
val replyTo = sender()
context.system.scheduler.scheduleOnce(5 seconds, self, replyTo)
case a:ActorRef =>
a ! "Slow Reply"
self ! PoisonPill
}
}
class ExampleService extends HttpService with Actor {
implicit def actorRefFactory = context
import context.dispatcher
def handleRequest(mode: String):Future[String] = {
implicit val timeout = Timeout(1 second)
val requestHandler = context.actorOf(Props[RequestHandler])
(requestHandler ? mode).mapTo[String]
}
val route: Route =
path("endpoint" / Segment) { str =>
get {
onComplete(handleRequest(str)) {
case Success(str) => complete(str)
case Failure(ex) => complete(ex)
}
}
}
def receive = runRoute(route)
}
This way the actor takes care of stopping itself, and the semantics of Ask give you the information about whether or not the request timed out.
I'm using scala-io in my akka actors, in my case I need to send request and wait for response, in official docs (http://doc.akka.io/docs/akka/snapshot/scala/io-tcp.html) I can see the answer is asynchronous.
How can I wait for response, can I use somehow ? (ask) pattern
class SocketClient(remoteAddress: InetSocketAddress, listener: ActorRef) extends Actor {
import Tcp._
import context.system
IO(Tcp) ! Connect(remoteAddress)
def receive = {
case CommandFailed(_: Connect) =>
listener ! ConnectFailure
context stop self
case Connected(remote, local) =>
listener ! ConnectSuccess
val connection = sender
connection ! Register(self)
context become {
case data: ByteString =>
connection ! Write(data)
case CommandFailed(w: Write) =>
Logger.error(s"Error during writing")
case Received(data) =>
listener ! data
case Disconnect =>
connection ! Close
case _: ConnectionClosed =>
Logger.error(s"Connection has been closed ${remoteAddress.getAddress}")
context stop self
}
}
}
Can I use something like:
connection ? Write(data)
Yes, but you should take in account the fact that ask-pattern allows you to receive only first reply from actor.
In your case it's connection, which may reply some additional or even unrelated objects (it depends on back-pressure/acknowledgement mode you choose. For example, if you use Write - you may receive the written (to the socket) object's acknowledge instead of response.
You can avoid it by:
using NoAck as an AckEvent (see http://doc.akka.io/docs/akka/snapshot/scala/io-tcp.html, Throttling Reads and Writes section).
use atomic requests/replies (no multi-part)
use one actor per protocol (per each "ping-pong" sequence)
In other words, ask pattern just creates own internal actor per message and make it a sender, so all replies (for this particular message) are going to this micro-actor. When it receives first reply - future (returned by ?) becomes completed - and internal actor destroyed (so other replies will be ignored).
Also, connection automatically replies to registered (by Register message) listener instead of sender - so you should create mediate actor:
class Asker(connection: ActorRef) extends Actor {
import Tcp._
connection ! Register(self);
def receive = {
case x =>
val parent = sender()
connection ! x
context become {case x => parent ! x; context.unbecome()}
}
}
trait TcpAskSupport {
self: Actor =>
def asker(connection: ActorRef) =
context.child(connection.path.name + "Asker")
.getOrElse(system.actorOf(Props(classOf[Asker], connection),
connection.path.name + "Asker"))
}
Usage example:
class Client extends Actor with TcpAskSupport {
import Tcp._
import context.system
IO(Tcp) ! Connect(new InetSocketAddress("61.91.16.168", 80))
implicit val timeout = Timeout(new FiniteDuration(5, SECONDS))
def receive = {
case CommandFailed(_: Connect) =>
println("connect failed")
context stop self
case c # Connected(remote, local) =>
println("Connected" + c)
val connection = sender()
asker(connection) ? Write(ByteString("GET /\n", "UTF-8"), NoAck) onComplete {
case x => println("Response" + x)
}
case x => println("[ERROR] Received" + x)
}
}