Socket IO and WebSockets with Gatling

Socket.IO is a WebSocket like API that lets developers create things like realtime web apps and games. You can learn more about the protocol here which was essential in understanding how we might simulate load for this on Flood IO.

Overview of the Socket.IO protocol

In short, a client will first determine what transport to use, common transports being WebSocket or XHR-polling amongst others. The client will then handshake with the server via a HTTP request at which point the server will respond with a session ID, heartbeat timeout and socket timeout. This session ID is then used in subsqeuent communication with the server via the chosen transport.

XHR-Polling in JMeter

It is possible to test this process via JMeter using XHR-polling as the transport. A test plan would look something like this:

The handshake would be a POST to a URL like this:

/socket.io/1?t=${__time(,)}

With a regular expression extractor for the session ID / token like this:

The subsequent connect request would be a GET to a URL like this:

/socket.io/1/xhr-polling/${token}?t=${__time(,)}

And depending on your use case, you could send events as a raw post body like this:

5:::{"name":"my other event","args":[{"my":"data"}]}

All good so far. However whilst we're exercising the functionality of Socket.IO (handshake, connect, event, disconnect) via XHR polling, we're still stuck on the hack version of push technology.

WebSockets affords us full duplex communication between the client and server, perfect for those real time applications in which the browser supports it.

WebSockets in Gatling

Gatling now supports websockets as an extension of the HTTP DSL. The test plan should look something like this:

import scala.concurrent.duration._

import io.gatling.core.Predef._  
import io.gatling.http.Predef._  
import io.gatling.jdbc.Predef._

// Mandatory, you must import Flood libraries
import flood._

class WebSockets extends Simulation {

  // Optional, Flood IO will pass in threads, rampup and duration properties from UI
  val threads   = Integer.getInteger("threads",  10)
  val rampup    = Integer.getInteger("rampup",   10).toLong
  val duration  = Integer.getInteger("duration", 120).toLong

  // Mandatory, you must use httpConfigFlood
  val httpProtocol = httpConfigFlood
    .userAgentHeader("gatling")
    .wsBaseURL("ws://echo.websocket.org")

  val scn = scenario("WebSocket")
    .exec(ws("Connect WS").open("/"))
    .exec(session => session.set("id", "Flood_" + session.userId))
    .pause(1)
    .repeat(2, "i") {
    exec(ws("Say Hello WS")
      .sendText("""{"text": "Hello, I'm ${id} and this is message ${i}!"}""")
      // .check(wsAwait.within(30).until(1).regex("Pong.*"))
    )
    .pause(1)
    }
    .exec(ws("Close WS").close)

  setUp(scn.inject(rampUsers(50) over (60 seconds))).protocols(httpProtocol)
}

We've got a mix of HTTP (for handshaking) and WebSocket (open, sendMessage, close) protocols in the script.

Now you can test some high concurrency WebSocket style communications from Flood IO.