Golang Gorilla Websocket fails when used concurrently

I have an app which uses the Gorilla Websockets at: “GitHub - gorilla/websocket: Package gorilla/websocket is a fast, well-tested and widely used WebSocket implementation for Go.
My app opens the connection, sends a list of stock symbols then, in a loop, reads the messages returned through that connection until it is cancelled or errors out.
Using Go routines, I want to monitor multiple lists of stocks concurrently. The error occurs when the 2nd connection is opened and the first connection is reading messages. In my code I identify the routines by iterationNumber and due to the way go routines work there is no guarantee which connection will be attempted first. I am getting an error when I run this, so I extracted the necessary code to exemplify the issue. I am pretty new to web sockets, so I’m probably just doing something incorrectly. I will send the example code and results:

package main

import (
	"fmt"
	"github.com/gorilla/websocket"
	"net/url"
	"sync"
)

type Stock struct {
	Symbol string `json:"symbol"`
	Type   string `json:"type"`
}

func main() {
	// URL and token for the WebSocket connection

	var wg *sync.WaitGroup = &sync.WaitGroup{}

	for i := 0; i < 2; i++ {
		wg.Add(1)
		go processStocks(i, wg)
	}

	wg.Wait()
}

func processStocks(interationNumber int, wg *sync.WaitGroup) {
	iterationStr := fmt.Sprintf("%d", interationNumber)

	u := url.URL{Scheme: "wss", Host: "ws.finnhub.io", RawQuery: "token=cbbb00iad3ibhoa1vbcg"}

	fmt.Printf("begin processStocks() for iteration: %s\n", iterationStr)

	// List of stock symbols
	stocks := []Stock{
		{"IBM", "subscribe"},
		// Add more stocks here
	}

	defer wg.Done()

	// Create a new WebSocket connection
	fmt.Printf("begin Dial() for iteration: %s\n", iterationStr)
	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		//cAddress := fmt.Sprintf("%p", c)
		fmt.Printf("failed dial error for iteration:%s address:%p: %v\n", iterationStr, c, err)
	} else {
		fmt.Printf("successful connection at address: %p for iteration: %s \n", c, iterationStr)
	}
	defer func(c *websocket.Conn) {
		err := c.Close()
		if err != nil {
			fmt.Printf("iteration %s Close error: %v\n", iterationStr, err)
		}
	}(c)

	// Send the list of stock symbols
	for _, stock := range stocks {
		fmt.Printf("begin WriteJSON() for iteration: %s address: %p\n", iterationStr, c)
		err := c.WriteJSON(stock)
		if err != nil {
			fmt.Printf("iteration %s write error: %v\n", iterationStr, err)
			return
		} else {
			fmt.Printf("successful write at address: %p for iteration: %s \n", c, iterationStr)
		}

	}

	// Endless loop to read the results
	for {
		fmt.Printf("begin ReadMessage() for iteration:%s address:%p\n", iterationStr, c)
		_, message, err := c.ReadMessage()
		if err != nil {
			fmt.Printf("iteration %s read error: %v address:%p: \n", iterationStr, err, c)
			return
		} else {
			fmt.Printf("iteration %s successful read: [%s] address:%p: \n", iterationStr, message, c)
		}
	}
}

the output:

begin processStocks() for iteration: 1
begin Dial() for iteration: 1
begin processStocks() for iteration: 0
begin Dial() for iteration: 0
successful connection at address: 0x14000012580 for iteration: 0 
begin WriteJSON() for iteration: 0 address: 0x14000012580
successful write at address: 0x14000012580 for iteration: 0 
begin ReadMessage() for iteration:0 address:0x14000012580

*iteration 0 read error: websocket: close 1006 (abnormal closure): unexpected EOF address:0x14000012580:* 

successful connection at address: 0x140000126e0 for iteration: 1 
begin WriteJSON() for iteration: 1 address: 0x140000126e0
successful write at address: 0x140000126e0 for iteration: 1 
begin ReadMessage() for iteration:1 address:0x140000126e0
iteration 1 successful read: [{"data":[{"c":["1","12"],"p":144.995,"s":"IBM","t":1691083530560,"v":1}],"type":"trade"}] address:0x140000126e0: 
begin ReadMessage() for iteration:1 address:0x140000126e0

BTW, this has my key in it so you can recreate the issue, but I will be changing it soon and it’s free anyway.

OK, figured it out. The api key is only good for one connection, so each user will need to get his own or I would have to work with the provider to see if this can be accomplished by getting a multiuser key or something. At least I can quit trying to fix the unfixable.

3 Likes

You can think of it differently, by sharing the WebSocket connection among multiple goroutine.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.