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, затем выполняйте сетевые записи вне общей блокировки, с отдельной сериализацией записей в каждый сокет.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее