Pull to refresh

Comments 8

А как такой вариант: код запускает горутину отправки в канал и блокируется уже горутина, тем самым мы не блокируем основной поток, из минусов можно наплодить слишком много горутин (но если предположить что это временный пик и очередь постоянно вычитывается, то вроде и нет в этом проблемы)

Горутина это вам не тред ОС. Если много тредов - это где-то 10000, то 1000000 горутин это ещё не много. Кроме того, можно же запилить token bucket и не запускать больше n. А вообще, конечно этот вариант гораздо адекватнее всего того, что в статье.

Тут возможны, как мне кажется, всего два варианта. Либо сообщения из канала вам важны и вы блокируетесь на записи, либо сообщения не важны и вы их выкидываете при заполненном буфере.

Варианты с временным "расширением" вместимости канала кажутся странными и как минимум это переусложнение на ровном месте, можно просто сделать канал больше, но это путь в никуда. Можно "выкидывать" данные в другой канал, заполнение которого будет явно свидетельствовать о проблеме и тут уже можно будет "бить в колокол".

Размер канала в 50 кажется странным решением. Зачем вам ещё одна очередь? Что станет с данными в канале, если сервис вдруг "упадёт"? Вероятно стоит поднять количество обработчиков, если очерёдность не важна, ну и канал с нулевым буфером тоже напрашивается сам собой. Ну и конечно же добавить метрику по тому, на какой промежуток времени блокируетесь при записи в канал, либо сколько выкидываете данных и после на данную метрику повесить алерты.

Похоже, вы используете каналы go не по назначению. Если вам нужна очередь, то лучше использовать нормальную очередь, например, RabbitMQ, у которого есть нормальная админка и он управляем и расширяем. А каналы в go - это замена межпотокового взаимодействия и это довольно низкоуровневый механизм. Поэтому (имхо) в каналах go можно только блокироваться.

Не стоит использовать каналы как очереди. Их конечно можно использовать так, но это плохая идея. Каналы - это в первую очередь способ синхронизации горутин с возможностью обмена сообщениями, и размер буфера канала должен определяться не логикой функции(нужно ли вам отправить и забыть или заблокироваться), а нагрузкой на этот канал(вы должны четко понимать, сколько у вас будет чтений из канала и сколько записей и исходя из этого подобрать оптимальный размер буфера). А вы при помощи канала пытаетесь решить задачу, которую решают очереди. В таком случае лучше обернуть слайс в структуру и защитить ее sync.Cond, таким образом получив подобие каналов, которые с одной стороны блокируют поток, если буфер пустой, расширяют буфер если его недостаточно и обеспечивают потокобезопасность. Или использовать службы очередей вроде Kafka или RabbitMQ

Так если через канал пересылаются сообщения о том, что что-то в базе изменилось, то закономерно предположить, что в заполненном канале такие сообщения уже есть. И консюмер всё равно обработает ситуацию "база обновилась" когда прочитает такое сообщение из канала. Значит можно спокойно дропать дублирующие сообщения.

Выше уже озвучили мнения, и я дополню.
Если рассматривать каналы как один из вариантов Inter process communication (IPC), станет более понятно, зачем каналы нужны.

В описании модели памяти Go:

Программы, которые изменяют данные, к которым одновременно обращаются несколько горутин, должны сериализовать такой доступ. Чтобы упорядочить доступ, защитите данные с помощью операций с каналами или других примитивов синхронизации, таких как в пакетах sync и sync/atomic.

P. S. Кажется, что использовать каналы в Go как очередь можно и используют, но надо понимать при этом про ограничения и риски. В вашем случае можно попробовать применить паттерн Fanout с автоскейлингом.

Вариантов вообще то один: допустима длина канала 0 или 1, все остальное всегда забъется с потерями. Канал будет эпизодически блокироваться, но можно увеличить количество горутин на вычитку. Остальное неприемлимо, поставьте это правило на ревью

Sign up to leave a comment.

Articles