Http vs WebSocket Protocols With Nodejs Clients and Python Servers

Francisco Ramos
The Startup
Published in
9 min readOct 1, 2020

Before we begin, you might be wondering: what does Eve have to do with Web protocols, Nodejs and Python?… well, it’s a long story, but basically this is the beginning of a project I have in mind where I combine these technologies to train AI agents. Yes, that’s my goal, and this is the first step towards it.

For the moment there is no need to mention what the next steps are to achieve this goal of mine—articles to come — , but I’ll say that I want to build something using Javascript — or TypeScript, depending on my mood—and I need communication with Python world. This communication must be the fastest possible since I’m gonna be making millions of requests and I can’t afford much lag.

How do we communicate Javascript, running on Nodejs platform, and Python?, well, there are some ways to do so. Recently I learnt about Pyodide project. This could actually be the fastest way of communication, bringing Python world right into Javascript world, through WebAssembly, compiling Python packages into a format, wasm, that Javascript can load and use. Unfortunately I discovered this project quite late after I started investigating other ways, so it’s gone to my TODO list of researches… and maybe future articles 😉.

Http

Another way to exchange messages between these two platforms would be using HTTP protocol, by simply building a REST API server, and a client that consumes it. Not being the purpose of this articles, we could still brush up on Http and Rest API.

Image from NTU Singapore

HTTP stands for HyperText Transfer Protocol, and it was designed mainly for communication between a web browser, this would be the client, and a web server. But the client is not limited to a web browser, any software that’s able to use this protocol can become a client. The way it works is fairly simple, without going into technical details such as additional metadata that’s sent along with the actual information, or even deeper, the underlaying TCP layer, and how data packets are transmitted: there is this request-response client-server model whereby a client sends a request to the server, and the server gets back with a response. That’s it. I mean, that’s the whole idea. But the important thing to know is that the client always initiates the communication, and the server is always listening. One-way communication. If the client needs to wait for something from the server, it needs to poll the server:

Client: hey, server! is there something for me? 😊
Server: Nope.

Client: hey!, it’s me again. Is there something for me? 🤗
Server: NOO! 😤

Client: Hello?, how about now? is there something for me? 😅
Server: actually, there is 😊. Here you go…
Client: why didn’t you tell me before? 🤔
Server: Because this is HTTP 😏
Client: 😥

Rest API

REST is acronym for REpresentational State Transfer, and it’s just an architectural style of communication using a request-response model. It has nothing to do with HTTP, but the reason there is this strong association is because a Rest API normally uses it as its transport protocol. The way for a client to access this API is through Uniform Resource Identifier or URI, which is nothing more than unique identifiers used to address different resources.

WebSocket

So using Rest API and Http protocol a Nodejs client could talk to a Python server… question is, could this provide me with a fast enough communication? can we do better? well, there is this other protocol that would enable us to use a bidirectional communication. Not only that, but it’s fast… way faster… and we’ll see why. I’m talking about WebSockets. Here is a interesting visualization of how this works.

Image from PubNub

There is this first, and standard, http request made by the client, same as before, but there is a small difference now: the client is actually asking to open a WebSocket connection by upgrading the mechanism of communication. This is called the “handshake”. If the server agrees/is able to—successful handshake — then it keeps open the same TCP connection used on this initial request — HTTP is built on top of TCP — so both, client and server, can talk to each other in a two-ways communication. The way I like to see it is kind of like a pipe where each party is at the end of it. Whatever one of them pushes inside, comes out on the other side… which reminds me of…

WebSockets is an event-based solution, which means, both client and server will be notified whenever something comes out from their side of this opened pipe. There is no longer need for polling.

Now, you might be wondering: “ok, we now have a bidirectional communication, and the client and server can talk to each other independently at the same time. This is cool, but if we don’t need such exchange, and it’s only the client who needs stuff that the server can always provide with… why not to stick to HTTP?”. Well, the answer is that, this is still faster than HTTP. Why? simple: each http request opens and closes a connection. Client needs something, it opens a connection. The server returns something, the connection closes. This happens every time there is a new request. This non-stop opening and closing creates an overhead that I cannot need for my use case. The experiments I’m about to show you makes this more obvious.

Experiment Setup

Alright, so let’s make some performance tests. We’re gonna build a small client in Javascript, and a small server in Python. The client will introduce itself to the server with some random name, and this last one will say hello to the client with some random greeting. Now, in order to get some real numbers I’ll be sending a couple of thousand requests, fixing this number to 10k. I think that would give me a good idea about the performance without having to wait too long to run all the tests. On the way, and out of curiosity, I’ll be using different libraries.

After doing some quick search in Google, and also filtering out millions of potential candidates, I got the following libraries:

Http Clients:

  1. Axios: “Promise based HTTP client for the browser and node.js”.
  2. Node-fetch: “A light-weight module that brings Fetch API to Node.js”. I use this one a lot because I’m used to the browser API.
  3. Superagent: “light-weight progressive ajax API crafted for flexibility, readability, and a low learning curve…”
  4. Plain Http: built-in Nodejs package to handle HTTP communication.

Http Servers:

  1. FastAPI: “a high performance framework, easy to learn, fast to code, ready for production…”
  2. Flask: it doesn’t need introduction, does it? This web framework is widely used to build Python backend.
  3. Pyramid: “small, fast, down-to-earth Python web framework…”
  4. Tornado: “Python web framework and asynchronous networking library…”

WebSocket Clients:

  1. Websocket-Node: “pure JavaScript implementation of the WebSocket protocol versions 8 and 13 for Node”. I’m using here the client version.
  2. WS: “simple to use, blazing fast, and thoroughly tested WebSocket client and server implementation.”

And that’s it for WebSocket clients. There are some other options, but these two seem to be the most used ones… and yes I know about SockJS-client, but this client seems to need its counterpart, SockJS-node server, to work along with.

UPDATE: I recently learnt about a Python server implementation of SockJS that uses Asyncio. Unfortunately it didn’t make it to my tests 🤷‍♂️

WebSocket Servers:

  1. Eventlet: “concurrent networking library for Python that allows you to change how you run your code, not how you write it…”
  2. FastAPI WebSockets: Http server we saw before, but it also allows you to use WebSockets.
  3. Tornado WebSockets: Http server Tornado providing with WebSocket protocol.
  4. WebSockets python package: “a library for building WebSocket servers and clients in Python with a focus on correctness and simplicity.”

Again, there are tons of other options, but this ones came up on top of my Google search.

I also tested, and once again, out of curiosity, a couple of Socket.IO libraries. The reason is because I used it in the past and I wanted to see how it would compare with Http and WebSocket, although it seems to use WebSockets under the hood if available, otherwise Long Polling. For those who don’t know Socket.IO, it’s a transport protocol developed around 2010 — or that’s what Github says — to use open connections to facilitate realtime communication. It also allows bi-directional communication between client and server. But this communications are enabled only when a client uses Socket.IO and a server has also integrated the Socket.IO package. Since there are a couple of implementations for Python servers, and this was initially intended for Javascript, I thought I could also give it a try.

Socket.IO Client:

  1. Socket.IO-client: official implementation and probably the only client.

Socket.IO Servers:

  1. Flask-Socket.IO: “gives Flask applications access to low latency bi-directional communications between the clients and the server…”
  2. Python-Socket.IO: “enables real-time bidirectional event-based communication between clients (typically, though not always, web browsers) and a server.”

So, the idea is to send some random information to the server as a JSON. I thought it would be fun to use this weird npm package I found by chance, which generates random names, Unique Names Generator, so the client can introduce itself to the server with a funny name like:

{ name: “Productive Copper Beaver” }

Yeah, this library can generate names with 1400 adjectives, 50 colors an 350 animals. I’d say that’s random enough 😜.

The server will received that and return a random greeting as a JSON response:

{ greeting: “How’s life, Productive Copper Beaver!” }

In order to measure the performance I created a wrapper around Nodejs Performance measurement APIs. This is a built-in package that provides an implementation of a subset of the W3C Web Performance APIs as well as additional APIs for Node.js-specific performance measurements. This will make it easy for me to see how long it took for the whole communication between client and server.

Running the Experiment

Ok, so we’re all setup here. Let’s actually run the tests and see the results. Let’s begin with Http Client-Server:

Axios client was always on top. Pyramid and Flask servers were, most of the time, giving me better results.

Next, let’s have a look at Socket.IO:

A completely disappointment. I was pretty sure they would try to establish WebSocket connection, but after looking at these results I had my doubts. I went back to the documentation of both client and server to see if I was missing something, but looks like everything is properly setup to use WebSockets in both sides. Maybe there is something else going on in this communication that takes even longer than simple http requests. Perhaps someone could shed some light on this?

And now it’s time for WebSocket communication:

Look at that!!…

Incredible performance compared to http requests. Eventlet server was always the winner in combination with WebSocket-Node client, running 10k iterations consistently below 2 seconds.

Also a big improvement against HTTP, but this client was constantly giving me worse results than the previous one.

Conclusion

So, after running these tests many times, I started getting more consistent numbers, and these numbers were telling me that these two guys, WebSocket-Node client and Eventlet server, were the best team. So these are my winners and I’ll be using them for the next step of this project.

You can find all the clients and servers implementation in my Github repo:

Thanks for reading… and please do let me know for any question, suggestion, improvement, correction, mistake I might have made or inconsistency. It’s the only way to learn 😊.

--

--

Francisco Ramos
The Startup

Machine and Deep Learning obsessive compulsive. Functional Programming passionate. Frontend for a living