Web socket push notifications in Akka, Netty and Socko

I was looking for a solution for web socket push in my Akka system. Ultimately, I wanted to be able to simply say here’s some payload instance, push it out to all clients you are aware of. In other words, to have some clever WebSocketPushActor and to have it wired up to Netty.

More detailed discussion, including wiring up of regular HTTP APIs, is in the follow-up post.

case class Push[A](payload: A)

case class MyMessage()

class MyActor extends Actor {
  protected def receive = {
    case MyMessage() =>
      // do some business logic
      val payload = Notification("This is the text to show!")

      // spam every socket
      context.actorFor("/user/webSocketPush") ! Push(payload) 
  }
}

Let’s take a look at how we implemented the WebSocketPushActor; the handler for the Push message is clear; the interesting portion of the code is that this actor keeps track of all open web sockets:

case class WebSocketRegistered(channel: Channel)

class WebSocketPushActor extends Actor {
  private val socketConnections = new DefaultChannelGroup()

  protected def receive = {
    case Push(payload) =>
      socketConnections.write(new TextWebSocketFrame(payload.toString()))
    case WebSocketRegistered(channel) =>
      socketConnections.add(channel)
  }
}

The main application simply boots the ActorSystem and starts the two actors:

object Main extends App {
  val actorSystem = ActorSystem("example")

  val myActor = actorSystem.actorOf(Props[MyActor])
  val webSocket = actorSystem.actorOf(Props[WebSocketPushActor], 
                                      "webSocketPush")
}

What is missing, of course is a web server. We shall be lazy and use something that already exists: Netty and a nice Scala wrapper for it called Socko. Modifying the Main singleton gives:

object Main extends App {
  val actorSystem = ActorSystem("example")

  val myActor = actorSystem.actorOf(Props[MyActor])
  val webSocket = actorSystem.actorOf(Props[WebSocketPushActor], 
                                      "webSocketPush")

  val webServer = new WebServer(WebServerConfig(), Routes {
    case WebSocketHandshake(wsHandshake) => wsHandshake match {
      case PathSegments("websocket" :: Nil) => {
        wsHandshake.isAllowed = true
        actorSystem.actorFor("/user/webSocketPush") ! 
          WebSocketRegistered(wsHandshake.channel)
      }
    }
    case WebSocketFrame(frame) =>
      throw new UnsupportedOperationException(
        "Eh? We're meant to be pushing only!")
  })

  webServer.start()
}

So, this completes the web socket server-side code. I can now write some fancy JavaScript, which creates the web socket connection and simply shows the received message in a box (I did say fancy JavaScript, right?)

<html>
<head>
    <title>WebSockets & Akka</title>
    <script type="text/javascript" src="jquery-1.7.2.js"></script>
</head>
<body>
<div id="responseText"></div>

</body>
<script type="text/javascript">
  var socket;

  if (!window.WebSocket) {
    window.WebSocket = window.MozWebSocket;
  }

  if (window.WebSocket) {
    socket = new WebSocket("ws://localhost:8888/websocket");
    socket.onmessage = function(event) { 
      $("#responseText").html("Received " + event.data); 
    };
    socket.onopen = function(event) { 
      $("#responseText").html("Opened"); 
    };
    socket.onclose = function(event) { 
      $("#responseText").html("Closed"); 
    };
  } else {
    alert("Your browser does not support Web Socket.");
  }
</script>
</html>

And, this is it! You can now run your Main application, which will start the actors MyActor and WebSocketPushActor; it will also start Netty and react on web socket connection by sending a “register” message to the WebSocketPushActor. That actor will, when it receives the Push message, send it to all the sockets that it knows about.

Yes, the application is trivial and it ignores lots and lots of useful code: for example, how to add Spray routes to the fray, how to deal with marshalling of the payload. For the answers to that, you’ll have to wait a few days for another post. In the meantime, head over to https://github.com/mashupbots/socko and try the code I’ve shown you here.

And finally, in production, you will want to separate out the responsibilities for keeping track of the open sockets, sending and possibly the marshalling amongst separate actors!

This entry was posted in Jan's Blog and tagged , , . Bookmark the permalink.

14 Responses to Web socket push notifications in Akka, Netty and Socko

  1. ? says:

    Very cool!

    How about simply having the websocketpusher subscribing to the system’s eventStream for Push-messages and have the producers simply publish to the eventStream?

    Cheers,
    ?

  2. Jan Machacek says:

    Thanks for the tip, Viktor; that’ll make it even easier.

    Jan

    P.S. Sorry I missed you last week at Skills Matter–before I sorted out all the delegates, you were gone.

  3. Pingback: This week in #Scala (11/05/2012) | Cake Solutions Team Blog

  4. Pingback: APIs for Akka applications | Cake Solutions Team Blog

  5. Pingback: » Chat?r’s YooH – Nettie adamwest

  6. Vibul says:

    Hope you don’t mind … I’ve put a link to this article on the Socko web site: http://sockoweb.org/docs.html.

    Thanks

  7. Jan Machacek says:

    Sure–and if I can help out wick Socko, let me know! You have done awesome job with the web socket / Netty interface.

  8. Vibul says:

    Thanks Jan.

    The Socko feature set is currently driven by our use cases. If you have any specific use case or features that you think Socko should support – please email me. Perhaps we can work on them together.

    I really appreciate you taking the time to write this post. If you have any more, please let me know and I’ll put them up on the site as well.

  9. Jan Machacek says:

    Got you–and I might just be sitting on a feature request or two… I’ll definitely keep in touch.

    –J

  10. Pingback: Hongtium » 2012-05?Scala???

  11. Jason says:

    Thanks for your sharing, this is a very useful sample!! help me a lot!!

    but I still have some question, I wonder know if I can pull the html out of the code? ( I run the socko-sample on Github, and in that sample, the html is embedded in “WebSocketHandler” Actor?

  12. Vibul says:

    Hi Jason,

    Yes, you can just read from a file and write it back to the client.

  13. Jason says:

    Can you provide your whole complete example code ?

    I’m newbie and really need that..

    thanks!

  14. Jan Machacek says:

    I will add it to the akka-patterns project (https://github.com/janm399/akka-patterns) in the near future. If you want me to focus on web sockets first, open a request at https://github.com/janm399/akka-patterns/issues.

    –J

Leave a Reply