Socket Libraries for Your Pub/Sub Architecture

Manan Sharma
JavaScript in Plain English
6 min readJun 2, 2021

--

The biggest socket libraries on npm, compared surgically

So you are looking to implement a pub/sub architecture and clearly, Redis and Kafka blew right across your use-case, because if they didn’t, why are you even here?

So you are now looking to implement this architecture natively for your project using sockets and you’re confused about which library to choose. Well, you are at the right place! I just had to make the same choice for a project and ended up studying the most popular socket libraries in the Node.js ecosystem, namely socket.io, ws, sockJS and primus and I hope I can help you out!

But before we dive in, let’s first make sure we are on the same page regarding what we mean by the pub/sub architecture.

The Pub/Sub Architecture 🔃

In the most basic of terms, every such architecture consists of three major participants, the publishers, the subscribers, and the broker.

The publishers are the ones which publish messages regarding certain “topics”. The subscribers can then “subscribe” to any of these topics and receive the messages published by the publishers for that topic. The broker is the central authority responsible for maintaining and managing the message flows, topics, and data abstraction.

We also have to ensure that the flow of data is instantaneous i.e. the time gap between the publishing of a message and the receiving should be as short as possible. Thereby, we are talking about a communication protocol where we need two-way communication that also near instantly, and that’s why sockets are the way to go!

The Pub/Sub architecture: https://aws.amazon.com/pub-sub-messaging/

Sockets in Pub/Sub Architecture ✨

So if we are going to use sockets for our pub/sub architecture, what should be our expectations from them? How should we implement them to work for our use-case?

The most important task should be to implement an isolated and clean architecture i.e. no publisher/subscriber should be aware of any other topics and messages than the ones it has subscribed to.

Now what should be our expectations from the socket libraries we might want to use:

  1. They should be lightweight and easy to implement, with good documentation support.
  2. They should be testable i.e. unit testing should be possible using some of the common unit testing frameworks like Jest, Mocha, etc.
  3. They should have good community support and it would be great if it is being used by many users before us.

On the basis of all these things, we now compare the libraries.

Let the battle begin! 💥

We will compare the libraries based on all the factors mentioned above. The first thing we’d look at is the libraries themselves, rather than their implementations.

Package size

According to bundlephobia, the minified sizes of these libraries are:

  • ws@7.4.6: 216B + no dependencies
  • sockjs@0.3.21: 23.5kB + multiple dependencies
  • socket.io@4.1.2: 46.1kB + multiple dependencies
  • primus@8.0.3: 98kB + multiple dependencies

Testing Support

All the libraries except primus have specific client-side libraries which can allow you to test your code and connections. As primus, is an overlay built on such socket libraries, it can also be tested in a similar fashion.

Please remember, while using primus, if you change the parent socket library on the broker/server, you would also have to make the same changes on your clients.

Socket server and client libraries should be kept the same. Most libraries transmit some library-specific data while handshaking with the server and establishing the connection. Socket.io servers can only connect with socket.io clients and a standard browser WebSocket instance will not be able to persist such a connection.

Community Support

Weighing the community support has been a very tough job. I have tried to compare on the basis of stars and forks on their GitHub repositories, weekly downloads on npm, stack overflow answers, and many other factors beyond the scope of this blog. But TLDR; socket.io comes out with the best community support both officially and unofficially, while ws is our leader when it comes to being community tested i.e. it has undergone very heavy load testing right out of the box and has the least number of open issues on its repository.

Implementing The Architecture 🧮

Pub/Sub architecture is based on two important properties:

  1. Ability to listen and emit custom events.
  2. No concern with data that is not related to a particular socket

Comparing the libraries based on the above two facts, we find that ws and sockJS have no support for custom events. Though they can be worked on and added on the developer side, but that would lead to one more portion of your code to be thoroughly tested and implemented, which is not a part of your business logic.

Regarding the second point, we look at the concept of rooms, which means that every socket upon connection is thrown in a specific room and can only emit/listen to events which are for that room. This is the architecture which gives us data isolation and abstraction and we need it for our architecture.

Unfortunately ws and sockJS lack inbuilt support for rooms, they can be added but it leads to the same problem as above.

The Winner 🎇

So, I think you know what is coming here, socket.io should be the go-to socket library for implementing your pub/sub architecture. Primus comes at a close second, because it has a higher size and dependency structure along with the fact that it is still an overlay library. You would have to install another package primus-rooms, to implement this architecture using primus.

But if you want to take even more control of your architecture and are not afraid of getting your hands dirty, you should look towards ws. The most battle-ready and heavily tested library. It scales gracefully and works perfectly even during very high loads. You’d love working with it!

All That Glitter Is Not Gold 🛑

So you thought socket.io is your perfect choice? Well maybe it is, but there are some things you have to keep in mind.

Race Conditions

Socket.io uses the callback syntax, which means when an event is emitted which a client is listening to, the reaction of the client is done in a callback function.

If the code in your callback happens to be asynchronous, NodeJS will let that happen and switch back to the primary thread and wait for a new emit. At this time, if another similar event is emitted and the previous asynchronous task is not completed, we have a race condition.

But if you only use synchronous code in your callbacks. You would be good.

The only way to fix the above is to implement a signaling mechanism to acknowledge the completion of a request by the callback and emit it to the server, while it maintains the back pressure until then.

Acknowledgments

A possible way to handle the above issue was through acknowledgments i.e. a client can send an acknowledgment to the emitter that the request has been accepted and executed.

But on high load systems, the order of acknowledgments is not guaranteed, and this can lead to complications in the data flow.

Conclusion 🚀

So all in all, socket.io is a very good resource for your pub/sub architecture, as it offers built in architectural paradigms regarding custom events and the ability to join rooms.

But if you’d be working on heavy load systems with a lot of asynchronous code requiring atomicity, maybe it is not the best choice for you and you should look into implementing a native architecture using ws.

More content at plainenglish.io

--

--

A Computer Sc. undergrad at Delhi Technological University. Trying to make an impact in this world, one keystroke at a time.