Войти Регистрация
Конкурентность

24. Онлайн-статусы в VK Мессенджере: хаб подписок

Сложная

Go · 61 строк

Один инстанс держит около 40 тысяч открытых вкладок. Консьюмер очереди вызывает OnPresence на каждое событие, в час пик это 6 тысяч событий в секунду. serve крутится в отдельной горутине на каждую вкладку и пишет статусы в веб-сокет.

Вкладку отключают два места: ошибка записи в сокет и таймер простоя в 10 минут. Отметьте строки с ошибками.

В работе: Сервис присутствия, который показывает зелёную точку у собеседника.

Как проверяем Нажмите на строку с ошибкой и коротко напишите, что не так. Баг засчитан, если отмечена хотя бы одна его строка. Зачёт — найдено не меньше 60 % багов с учётом серьёзности (критичный весит 3, обычный 2, мелкий 1), а лишних отметок не больше, чем багов в коде. Пустые строки, скобки и комментарии лишними не считаем. Плюс 2 вопроса про поведение программы — на зачёт не влияют. Ответ один на подход: после него показываем разбор.
каналызакрытие каналадедлокпорядок событийутечка горутин

Источник: наша задача на данных VK: статусы «в сети» в мессенджере. Условие пересказано своими словами.

  1. package presence
  2. import (
  3. "sync"
  4. "time"
  5. )
  6. type Status struct {
  7. UserID int64
  8. Online bool
  9. }
  10. type Conn interface{ WriteJSON(v any) error }
  11. type Hub struct {
  12. mu sync.Mutex
  13. subs map[int64]chan Status
  14. }
  15. func (h *Hub) Subscribe(connID int64) <-chan Status {
  16. ch := make(chan Status)
  17. h.mu.Lock()
  18. h.subs[connID] = ch
  19. h.mu.Unlock()
  20. return ch
  21. }
  22. // Unsubscribe зовут ошибка записи и таймер простоя.
  23. func (h *Hub) Unsubscribe(connID int64) {
  24. h.mu.Lock()
  25. ch := h.subs[connID]
  26. delete(h.subs, connID)
  27. h.mu.Unlock()
  28. close(ch)
  29. }
  30. func (h *Hub) Publish(s Status) {
  31. h.mu.Lock()
  32. defer h.mu.Unlock()
  33. for _, ch := range h.subs {
  34. ch <- s
  35. }
  36. }
  37. // OnPresence вызывает консьюмер очереди на каждое событие.
  38. func (h *Hub) OnPresence(s Status) {
  39. go h.Publish(s)
  40. }
  41. func (h *Hub) serve(connID int64, ws Conn) {
  42. updates := h.Subscribe(connID)
  43. idle := time.AfterFunc(10*time.Minute, func() { h.Unsubscribe(connID) })
  44. defer idle.Stop()
  45. for s := range updates {
  46. idle.Reset(10 * time.Minute)
  47. if err := ws.WriteJSON(s); err != nil {
  48. h.Unsubscribe(connID)
  49. return
  50. }
  51. }
  52. }