skip to Main Content

I want to create a realtime api. Github Link of current code

There will be two types of users A and B.
Users A can connect to the service using a websocket to see realtime Updates.
Users B can only make http request to push data to mongodb. I have been following this tutorial. In this tutorial sqlitedb and redis pub/sub is used but i don’t want to use that.

slotServer.go

...

func (server *WsServer) Run() {
    gb := *models.GetInstanceGlobal()
    for {
        select {

        case client := <-server.register:
            server.registerClient(client)

        case client := <-server.unregister:
            server.unregisterClient(client)
        case message := <-server.broadcast:
            server.broadcastToClient(message)
        case message := <-gb.Channel:           
            server.createRoomAndBroadCast(message)
        }

    }
}
...

func (server *WsServer) createRoom(name string) *Room {
    room := NewRoom(name)
    go room.RunRoom()
    server.rooms[room] = true
    return room
}

func (server *WsServer) createRoomAndBroadCast(name string) {
    room := server.createRoom(name)
    var roomList []string
    for r := range server.rooms {
        roomList = append(roomList, r.GetName())
    }

    addRoomReply := models.AddRoomReply{
        RoomList:  roomList,
        AddedRoom: room.GetName(),
    }

    addReply := MessageAddRoom{
        Data:   addRoomReply,
        Action: "room-add-success",
    }

    server.broadcast <- addReply.encode()
}

I am trying to a listen on channel global. If a string is pushed to it, a function createRoomAndBroadCast will be called.

models.go

....
type GlobalChannel struct {
    Channel chan string
}

func GetInstanceGlobal() *GlobalChannel {
    return &GlobalChannel{
        Channel: make(chan string),
    }
}

I am writing to this channel in POST message handler

room.go

...
    //add room to mongo
    if err := db.CreateRoom(&room); err != nil {    
        ctx.JSON(http.StatusBadGateway, gin.H{
        "data": err,
    })
    return
    }
    //write it to channel
    gb := models.GetInstanceGlobal()
    gb.Channel <- *room.Name
    // send reponse to user
    ctx.JSON(http.StatusCreated, gin.H{
        "data": "Room Created Successfully",
    })
...

But my post request gets stuck at line gb := models.GetInstanceGlobal()
In the logs I see the following message

redirecting request 307: /api/v1/room/ --> /api/v1/room/

I don’t understand whether I am doing something wrong or my logic is completely wrong.

2

Answers


  1. Chosen as BEST ANSWER

    I started reading about how channels work in golang and thanks to this post found that mentioning buffer limit in a channel is very important.

    If ch is unbuffered, then ch <- msg will block until the message is consumed by a receiver

    So I changed

    models.go

    //Channel: make(chan string),
    Channel: make(chan string,100),
    

    and it started transmitting the messages.


  2. I’m only going to comment on the code you have shown here and not the overall design.

    func GetInstanceGlobal() *GlobalChannel {
        return &GlobalChannel{
            Channel: make(chan string),
        }
    }
    

    This is likely a misunderstanding of channels. While you have named it GetInstanceGlobal it is actually returning a brand new channel each time you call it. So if one side is calling it and then trying to receive messages and the other is calling it and pushing messages, the two sides will be different channels and never communicate. This explains why your push is blocking forever. And it also explains why when you add 100 to make it a buffered channel that appears to unblock it. But really all you have done is let it build up to 100 queued messages on the push side and eventually block again. The receiver is still on its own channel.

    Likely what the code implies is that you wanted to create the global once, return it, and share it. I’m not going to get into the design issues with globals here. But it might look like this for your case:

    var globalChannel *GlobalChannel
    
    func GetInstanceGlobal() *GlobalChannel {
        if globalChannel == nil {
            globalChannel = &GlobalChannel{
                Channel: make(chan string),
            }
        } 
        return globalChannel
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search