Handle Concurrency in Gorilla Web Sockets

André Marques
The Startup
Published in
3 min readNov 3, 2020

--

Most of the times, if you want to get started quickly with web sockets you will go to socket.io. But are there any other solutions? In this post, I’ll tell you all about an other framework, Gorilla web sockets, that I came across during a personal project.

Golang + Gorilla

At some point, I was building a web application and felt the need to have web sockets in my infrastructure. Because this was a personal project and I had some time, I thought that I could learn something new and use another framework besides socket.io. I was learning Go at the time and it was the perfect moment to try to use web sockets with it. I found that the Gorilla framework could do the trick, handling connections, and broadcasting messages.

Simple web sockets interaction

First of all, if you are using an HTTP framework in your Go project, you have to listen to the WS endpoint and create a handler that will connect all of your socket clients (in my case, I was using GIN framework).

router.GET("/ws", func(c *gin.Context) {
wshandler(c.Writer, c.Request)
})

The wshandler here will have to register connections and save them into a structure so that you can broadcast messages to your clients. These connections will have to be mapped with some kind of ID (normally a user ID). For the structure, normally a map is used to do the trick.

var sockets = make(map[string]*Connection)

Here, we’ll create a map binding a key to our connection, and this will be used for all kinds of web sockets messages. For instance:

var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true
},
}
func wshandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
// error handling here
defer conn.Close()

err = conn.ReadJSON(&message)
// error handling here

if sockets[message.SOME_ID] == nil {
connection := new(Connection)
connection.Socket = conn
sockets[message.SOME_ID] = connection
}
}

But what is this Connection struct exactly? This struct will hold the socket connection and a mutex so that you can lock the connection and wait for the unlock so that you can send the message.

// Connection - Websocket connection interfacetype Connection struct {
Socket *websocket.Conn
mu sync.Mutex
}
// Concurrency handling - sending messages
func (c *Connection) Send(message Message) error {
c.mu.Lock()
defer c.mu.Unlock()
return c.Socket.WriteJSON(message)
}

Now, instead of using WriteJSON directly in your wshandler, you will have to use this new Send method that will handle your desired concurrency.

for key, connection := range sockets {
err := connection.Send(message)
if err != nil {
delete(sockets, connection)
}
}

And there you have it! You successfully handled concurrency in your Gorilla project.

--

--