Из-за особенностей работы сборщика мусора в Go у вас скорее всего возникнут
проблемы, если ключи в map'е содержат указатели (включая структуры их содержащие).
Вот накидал способ хранения сокетов немного в другом виде без указателей в ключах:
const n = 4 // длина идентификатора комнаты в байтах
type (
noop struct{}
WebSocket struct{} // сокет
Sockets []WebSocket
RoomKey [n]byte
Index uint32
Room map[Index] struct{}
Rooms map[RoomKey] Room
)
var sockets = make(Sockets, 0, 2000000)
var rooms = make(Rooms, 1000)
var lastSocketRoomKey RoomKey
func insert(ws WebSocket, rk RoomKey){
room, _ := rooms[rk]
// тут создание комнаты, если ее нет
// ...
sockets = append(sockets, ws)
last := len(sockets) - 1
room[Index(last)] = noop{}
// сохраним ключ комнаты последнего сокета
// чтобы можно было исправить индекс при удалении
copy(lastSocketRoomKey[:], rk[:])
}
func remove(i Index, rk RoomKey){
// удаляем сокет,
// переместив последний в массиве на место удаляемого
last := len(sockets) - 1
sockets[i] = sockets[last]
sockets = sockets[:last]
// удаляем сокет из комнаты
room, _ := rooms[rk]
delete(room, Index(i))
// чиним индекс перемещенного сокета
roomOfLast := rooms[lastSocketRoomKey]
delete(roomOfLast, Index(last))
roomOfLast[Index(i)] = noop{}
}
Когда вам нужно разослать сообщения, в моем примере доступ к сокету будет происходить примерно так:
for _, index := range rooms[roomkey]{
// sockets[index] -- сокет в комнате с roomkey
// т.к. из-за особенности структуры искать сокет в комнате получается невыгодно долго,
// придется удалять закрытые сокеты "ленивым" способом во время рассылки,
// либо заполнять массив индексов и после уже удалять эти сокеты из общего хранилища
// функцией remove(index, roomkey)
}
Очевидно, что в данном примере доступ к sockets и rooms не должен происходить из разных горутин.