> Какой механизм используется для отправки событий? (Go)
Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы
Компании: InDrive
Стек: Go
> Пример ответа
Короткий ответ
В Go для отправки событий чаще всего используется механизм каналов (channels) в сочетании с горутинами. Каналы обеспечивают синхронизацию и передачу данных между конкурентными компонентами. Для более сложных сценариев применяются паттерны на основе каналов: fan-out, pub/sub, event bus. Альтернатива - использование стандартной библиотеки context для отмены и таймаутов, а также сторонних библиотек вроде watermill или go-eventbus, если требуется гарантированная доставка или маршрутизация.
Подробное объяснение
Каналы в Go - это типизированный механизм передачи данных между горутинами. Они бывают буферизированными и небуферизированными. Небуферизированный канал блокирует отправителя до получения данных получателем, что обеспечивает синхронизацию. Буферизированный позволяет отправителю продолжать работу, пока буфер не заполнен.
Для отправки события используется оператор ch <- event. Получение - event := <-ch. Каналы можно закрывать через close(ch), что сигнализирует получателям о завершении потока.
Для pub/sub паттерна создают структуру с map подписчиков и мьютексом для защиты. Каждый подписчик - отдельный канал. При отправке события оно копируется во все каналы подписчиков. Важно обрабатывать медленных подписчиков - либо использовать буферизированные каналы, либо горутины-посредники.
Также используется паттерн "worker pool": несколько горутин читают из одного канала и обрабатывают события конкурентно. Это даёт масштабирование и балансировку нагрузки.
На практике
При проектировании event-системы на Go важно решить: нужна ли гарантия доставки, порядок событий, обработка ошибок. Для простых случаев достаточно канала с буфером. Для распределённых систем - Kafka или NATS, но это уже не механизм языка, а внешняя инфраструктура.
Типичный подход - создать интерфейс EventBus с методами Publish(topic string, event interface{}) и Subscribe(topic string) <-chan interface{}. Внутри - map[string][]chan interface{} и sync.RWMutex. При публикации событие отправляется во все каналы подписчиков топика.
Важно помнить: отправка в закрытый канал вызывает panic. Поэтому нужна защита через мьютекс или проверку состояния. Также стоит избегать блокировки отправителя - использовать select с default или буферизированные каналы.
Пример кода
GOtype EventBus struct {mu sync.RWMutexsubs map[string][]chan interface{}}func NewEventBus() *EventBus {return &EventBus{subs: make(map[string][]chan interface{})}}func (b *EventBus) Subscribe(topic string) <-chan interface{} {b.mu.Lock()defer b.mu.Unlock()ch := make(chan interface{}, 10)b.subs[topic] = append(b.subs[topic], ch)return ch}func (b *EventBus) Publish(topic string, event interface{}) {b.mu.RLock()defer b.mu.RUnlock()for _, ch := range b.subs[topic] {select {case ch <- event:default:// подписчик медленный - пропускаем или логируем}}}// использованиеbus := NewEventBus()ch := bus.Subscribe("user.created")go func() {for ev := range ch {fmt.Println(ev)}}()bus.Publish("user.created", User{ID: 1})
Как отвечать на собеседовании
Начни с каналов как базового механизма. Затем покажи, как строится паттерн pub/sub. Упомяни trade-off: буферизация vs блокировка, гарантии доставки. Если спросят про распределённые системы - скажи, что для этого используют Kafka/NATS, а каналы - только в рамках одного процесса.
Приведи пример кода, но не слишком длинный. Покажи, что понимаешь проблемы: закрытие каналов, медленные подписчики, конкурентный доступ к map. Упомяни альтернативы: sync.Cond, atomic для простых сигналов, но каналы - идиоматичный способ.
Что проверяет интервьюер
Интервьюер оценивает знание конкурентных примитивов Go, понимание блокировок и синхронизации. Важно показать, что ты знаешь не только синтаксис, но и паттерны: fan-out, worker pool, pub/sub. Также проверяется умение проектировать API для событийной системы и обрабатывать edge cases.
Типичные ошибки
- Отправка в закрытый канал - panic. Нужна защита.
- Гонки при доступе к map подписчиков без мьютекса.
- Бесконечная блокировка отправителя при медленном получателе - нужен select с default или буфер.
- Неправильное использование буферизации: слишком большой буфер маскирует проблемы, слишком маленький - блокирует.
- Забывают закрывать каналы подписчиков при отписке - утечка горутин.
- Путают каналы с очередями: каналы не хранят события, если нет получателя - событие теряется.
> Похожие задачи по Go
Почему вы сменили PHP на Go, что было мотиватором
Какие гарантии доставки сообщений предоставляет Google Pub/Sub?
Как отправляются события об изменении статусов?
Как хранится история переходов статусов платежей?
> Похожие задачи по backend
Работаете ли вы с Python и Go
Какие гарантии доставки сообщений предоставляет Google Pub/Sub?
Как отправляются события об изменении статусов?
Как хранится история переходов статусов платежей?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью