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.
...
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.
....
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
...
//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
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
and it started transmitting the messages.
I’m only going to comment on the code you have shown here and not the overall design.
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: