Счётчик и реестр соединений нужно менять под одной блокировкой, а чтение счётчика тоже синхронизировать. Реестр защищает от повторного подключения и двойного отключения одной записи.
WebSocket Connection Management
Счётчик и реестр соединений нужно менять под одной блокировкой, а чтение счётчика тоже синхронизировать. Реестр защищает от повторного подключения и двойного отключения одной записи.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Задача требует правильной реализации методов handleConnect и handleDisconnect. Необходимо учитывать, что несколько клиентов могут подключаться и отключаться одновременно, и обеспечить безопасное обновление счетчика активных соединений в условиях конкуренции.
type Message struct {
UserID int
Payload string
}
type Connection struct {
UserID int
DeviceID string
}
func (c *Connection) Write(p []byte) (n int, err error) {
// Предположим, что реализовано
return 0, nil
}
type WSServer struct {
connectedClientsCount uint64
}
func (w *WSServer) handleConnect(c Connection) {
// TODO
}
func (w *WSServer) handleDisconnect(c Connection) {
// TODO
}
func (w *WSServer) totalConnectedClients() uint64 {
return w.connectedClientsCount
}
func (w *WSServer) handleQueueMessages(messages []Message) (int, error) {
for i, m := range messages {
err := w.sendToConnectedDevices(m)
if err != nil {
return i, err
}
}
return len(messages), nil
}
func (w *WSServer) sendToConnectedDevices(m Message) error {
// TODO
return nil
}
Ответ
Счётчик и реестр соединений нужно менять под одной блокировкой, а чтение счётчика тоже синхронизировать. Реестр защищает от повторного подключения и двойного отключения одной записи.
Вариант с импортом sync и допущением, что пара UserID–DeviceID имеет не более одного активного соединения:
type WSServer struct {
mu sync.Mutex
connectedClientsCount uint64
active map[Connection]struct{}
}
func (w *WSServer) handleConnect(c Connection) {
w.mu.Lock()
defer w.mu.Unlock()
if w.active == nil { w.active = make(map[Connection]struct{}) }
if _, exists := w.active[c]; exists { return }
w.active[c] = struct{}{}
w.connectedClientsCount++
}
func (w *WSServer) handleDisconnect(c Connection) {
w.mu.Lock()
defer w.mu.Unlock()
if _, exists := w.active[c]; !exists { return }
delete(w.active, c)
w.connectedClientsCount--
}
func (w *WSServer) totalConnectedClients() uint64 {
w.mu.Lock()
defer w.mu.Unlock()
return w.connectedClientsCount
}
Для реальных переподключений добавьте уникальный connection ID: позднее отключение старого сокета не должно удалить новый того же устройства. Один atomic-счётчик решает гонку, но не такую ошибку идентичности. При рассылке копируйте список адресатов под lock, затем выполняйте сетевые записи вне общей блокировки, с отдельной сериализацией записей в каждый сокет.