SocketCluster / socketcluster-client

JavaScript client for SocketCluster
MIT License
291 stars 92 forks source link

Using MessagePack (binary frames) with SocketCluster #64

Closed mirague closed 6 years ago

mirague commented 7 years ago

Currently the SocketCluster client uses a format of:

{"event":"someType","data":{"foo":"bar"},"cid":3}

The format has considerable overhead on the messaging structure and I was wondering how we could go about implementing a buffered-approach such as MessagePack. This could save us big on bandwidth as we're developing an application with a high rate of message throughput.

Right now I'm trying something like:

/**
 * Send a Message to Remote over WebSocket.
 * Note: This method is part of an abstracted Transport layer used by both Client and Server
 */
send(messageType, message, callback = null) {
  // this.socket = SocketCluster socket

  if (this.socket.getState() !== 'open') {
    return;
  }

  const encoded = msgpack.encode({event: messageType, data: message}); // returns Buffer

  // Get the SocketCluster Transport and send Binary frame
  if (this.socket.transport) { // SCClient Socket
    this.socket.transport.socket.send(encoded);
  } else { // SCServer Socket
    this.socket.socket.send(encoded);
  }

  // The original SocketCluster way which reproduces a "heavy" json string-payload
  this.socket.emit(messageType, message);
}

What do you think of supporting raw binary frames directly? And if there's no interest in official support, could you share your thoughts on this? Thanks!

jondubois commented 7 years ago

@mirague I think the current format is nice because it's simple and extensible... But I agree with your point.

For compression, there is support for perMessageDeflate (good for large messages). This has been in SC for a long time but I just added documentation today: Search for perMessageDeflate here: http://socketcluster.io/#!/docs/api-socketcluster

perMessageDeflate is great for large JSON messages but not very useful for very short messages sent with high throughput. (which is your use case)

I believe the WebSockets standard might support other custom compression algorithms in addition to per-message deflate but I'm not sure if/how that is implemented in browsers. If compression can be done at the WebSocket level then there wouldn't be much point changing our current event format. I will need to research this a bit more.

Either way, yes I think it would be nice to allow the user to inject their own compression/decompression functions on the client and server.

We could also consider changing the current format to something more lightweight but this would introduce new problems:

I'm kind of leaning on allowing the user to inject their own custom compression/decompression functions. What do you think?

jondubois commented 7 years ago

We could introduce a new concept: custom Codecs - So users could specify a custom JavaScript module for coding and decoding packets - Right now, only https://github.com/SocketCluster/sc-formatter is supported but we could allow the user to swap it out with anything they like.

For games, the user could use their own Codec optimized for their specific game - It could format messages as raw binary.

If a codec is used on the client, the same codec module will need to be provided on the server-side. It would just have two methods: encode and decode.

The community could then share codecs - Each one being optimized for specific purposes.

jondubois commented 7 years ago

@mirague I added a new custom codec feature. See https://github.com/SocketCluster/socketcluster#custom-codecs

mirague commented 7 years ago

@jondubois Excellent work! I will have a look at the changes coming week, thank you!

ivopc commented 7 years ago

If there's a lot of req by sec, consider using webRTC + socketCluster (if it is p2p task, of course).

jondubois commented 7 years ago

@mirague In case you're interested, I've done some work on a minimal binary codec which you can easily plug into SC: https://github.com/SocketCluster/sc-codec-min-bin

Right now it just uses MessagePack to encode/decode messages between JSON <=> ArrayBuffer/Buffer (Binary) but in the future, it will also do some SC-specific compression to reduce the protocol overhead to almost nothing - Suggestions and PRs are welcome.

You may want to upgrade socketcluster to v5.1.0 and socketcluster-client to v5.1.0 for best effect. Earlier versions of SC and the client did not encode the ping and pong messages (this is a minor aesthetic issue though).

Also, we started adding some features to speed up pub/sub in SC so that should help with high-throughput workloads as well.