Scaling webscocket connections using golang

2.5k views Asked by At
   func handleConn(w http.ResponseWriter, r *http.Request) {
        ws, err := upgrader.Upgrade(w, r, nil)
        if err != nil {
            if _, ok := err.(websocket.HandshakeError); !ok {
                log.Println(err)
            }
            return
       }

        go writer(ws)
        reader(r, ws)
    }

    func main() {
       http.HandleFunc("/", handleConn)
    }

I am trying to build a high scaling websocket server using golang. Since there is lack of support in native go websocket package, i am using https://github.com/gorilla/websocket.

For every connection, a goroutine will be spawned to take care of writes to it. For scaling huge number of connections. Let's say if I have 1M concurrent connections then there should be 1M goroutines will be running on the server.

Hardware Specification:

16 GB Ram

4 Core CPU

2.5 GHz Intel Core i5

will it work for large number of connections without affecting the performance?

1

There are 1 answers

2
Prakhar Agnihotri On

Lets do some mathematics:

Theoretically: Each go-routine consumes 8KB of memory. So, for a million sockets, 8GB of ram is required.

Now, let's shift the paradigm towards practicality. Since, you are using web socket, you are looking for two way channel communication. So, you would have to reserve a buffer for reading data from each socket. So, another 8 GB of RAM gone just for listening the data from socket. These figures just denotes the resource, required to spin and spawn a million connections, and doesn't include other important factors (resource reserved for other tasks, kernel reserved space, OS reserved space, etc.)

Conclusion: This approach is simple, but not scalable beyond a certain limit.

You would definitely need much more RAM, to scale at the proposed figure.

You can minimise the memory consumption by implementing something like kQueue/ePoll. Via this approach, instead of constantly waiting for data to be consumed, and wasting the buffer memory, you would implement a callback, which gets informed if the socket is ready to read/write data, and then you can create a buffer and do read/write operations over the file descriptor. This would help you reuse the buffer, and thereby reduce resource consumption.

Hope that helps!