Size: a a a

2020 April 23

ВС

Владимир Столяров... in Go-go!
не совсем, rate.Limit это частота, а вот burst - количество событий за период
то есть, при t=1с и burst=3 можно будет либо 3 раза за одну секунду вызвать Allow() либо 1 раз за секунду AllowN(3)
источник

VT

Vasiliy Toporov in Go-go!
Кто-нибудь может объяснить как это работает? У меня есть приложение, в котором есть несколько каналов, каждый из которых обрабатывается пулом горутин. В каждый из этих каналов попадает свой тип сообщений. Всё работало хорошо, но недавно я написал вот такой метод (вкратце, он достаёт пачку необработанных сообщений stalledEvents из базы при старте приложения и рассовывает их по каналам streamChannel, которые берутся из словаря по определённому ключу).

for _, e := range stalledEvents {
     
     streamName := e.GetStreamNameFromEventName()
     streamChannel, channelExists := c.eventsChannels[streamName]

     if channelExists {
       streamChannel <- &e

Каждый канал - это канал указателей на события (которые каналы и должны обрабатывать) - chan *Event. В итоге, у меня началось безобразие: в каналы стали попадать события разного типа, причём возникало такое ощущение, что весь пул горутин в каждом канале пытается обработать одно событие, да ещё и не того типа, всё это ломалось на глобальном локе в Редисе, и из всей пачки stalledEvents обрабатывалась только одна запись.

Долго пытался понять в чём причина этого бардака и наткнулся на такой пассаж https://golang.org/doc/effective_go.html#channels со слов "The bug is that in a Go for loop, the loop variable is reused for each iteration, so the req variable is shared across all goroutines." В моём случае всё пофиксилось после вот такой правки:

for _, e := range stalledEvents {
     e := e
     streamName := e.GetStreamNameFromEventName()

Но почему? У меня в этом методе нет анономных функций и горутин, в которых мог бы быть конфликт из-за областей видимости и перетирания переменной в цикле. Ест только складывание ссылки на структуру в цикле, но есть ещё ранее запущенные горутины, которые и читают разные типы streamChannel, причём там путаницы нет - я проверял сравнивая указатели на канал, который они слушают.
источник

ВС

Владимир Столяров... in Go-go!
а можно сигнатуру GetStreamNameFromEventName
источник

ВС

Владимир Столяров... in Go-go!
Просто если ресивер является указателем, то вы попали на грабли переиспользования памяти для переменных в range
Потому что на самом деле вызов метода идет так (*Event).GetStreamNameFromEventName(&e)
источник

AS

Andrei 🦉 Sergeev in Go-go!
Vasiliy Toporov
Кто-нибудь может объяснить как это работает? У меня есть приложение, в котором есть несколько каналов, каждый из которых обрабатывается пулом горутин. В каждый из этих каналов попадает свой тип сообщений. Всё работало хорошо, но недавно я написал вот такой метод (вкратце, он достаёт пачку необработанных сообщений stalledEvents из базы при старте приложения и рассовывает их по каналам streamChannel, которые берутся из словаря по определённому ключу).

for _, e := range stalledEvents {
     
     streamName := e.GetStreamNameFromEventName()
     streamChannel, channelExists := c.eventsChannels[streamName]

     if channelExists {
       streamChannel <- &e

Каждый канал - это канал указателей на события (которые каналы и должны обрабатывать) - chan *Event. В итоге, у меня началось безобразие: в каналы стали попадать события разного типа, причём возникало такое ощущение, что весь пул горутин в каждом канале пытается обработать одно событие, да ещё и не того типа, всё это ломалось на глобальном локе в Редисе, и из всей пачки stalledEvents обрабатывалась только одна запись.

Долго пытался понять в чём причина этого бардака и наткнулся на такой пассаж https://golang.org/doc/effective_go.html#channels со слов "The bug is that in a Go for loop, the loop variable is reused for each iteration, so the req variable is shared across all goroutines." В моём случае всё пофиксилось после вот такой правки:

for _, e := range stalledEvents {
     e := e
     streamName := e.GetStreamNameFromEventName()

Но почему? У меня в этом методе нет анономных функций и горутин, в которых мог бы быть конфликт из-за областей видимости и перетирания переменной в цикле. Ест только складывание ссылки на структуру в цикле, но есть ещё ранее запущенные горутины, которые и читают разные типы streamChannel, причём там путаницы нет - я проверял сравнивая указатели на канал, который они слушают.
вы берёте ссылку на e, не надо так
источник

VT

Vasiliy Toporov in Go-go!
> а можно сигнатуру GetStreamNameFromEventName
func (e *Event) GetStreamNameFromEventName() string {
 splittedName := strings.Split(e.Name, ":")
 if len(splittedName[0]) > 0 && splittedName[0] != e.Name {
   return splittedName[0]
 }

 return e.Name
}

>вы берёте ссылку на e, не надо так
Приходится, так как канал - это канал ссылок на структуры типа Event.
источник

AS

Andrei 🦉 Sergeev in Go-go!
Vasiliy Toporov
> а можно сигнатуру GetStreamNameFromEventName
func (e *Event) GetStreamNameFromEventName() string {
 splittedName := strings.Split(e.Name, ":")
 if len(splittedName[0]) > 0 && splittedName[0] != e.Name {
   return splittedName[0]
 }

 return e.Name
}

>вы берёте ссылку на e, не надо так
Приходится, так как канал - это канал ссылок на структуры типа Event.
сразу вопрос - а зачем вам передавать ссылки через канал? это резко повышает риск конкуретного изменения одного экземпляра Event
источник

AK

Anton Kucherov in Go-go!
Georgy Perevozchikov
Ребята, задача на миллион.

Есть компутер А и есть компутер Б. А хочет отправить Б файл объёма 10 гб.
Между компутераии А и Б есть ftp сервер объёма 1 гб. И ещё это не просто ftp сервер. Чтобы создать на нем файл нужно подождать N времени. По этому порезать файл на Н частей и передать по частям будет дорого.

Вопрос как передать эти 10 гб эффективно?

Было бы здорово если бы компутер А мог бы передать РАЗНЫЕ файлы не только компутеру Б ни и C  и Д и Е одновременно.

Знаю звучит дико. Но....

Типо можно ли на ftp создать файл например несколько мб куда компутер А будет непрерывно писать. Перезаписывая его а компутер Б качать?

А ля веб сокет на ftp файле?)
Один раз надо файл переслать? Или регулярно?
источник

VT

Vasiliy Toporov in Go-go!
>сразу вопрос - а зачем вам передавать ссылки через канал? это резко повышает риск конкуретного изменения одного экземпляра Event
Скажу честно: так получилось. В основном задумка была в том, что это уменьшит потребление памяти.
источник

VT

Vasiliy Toporov in Go-go!
Если я правильно понял, то проблема в том, что если из канала сообщение не будет обработано быстро, то при быстром итерировании ссылка на "е" будет изменена и в канале сообщения соответственно, тоже поменяются?
источник

AS

Andrei 🦉 Sergeev in Go-go!
Vasiliy Toporov
Если я правильно понял, то проблема в том, что если из канала сообщение не будет обработано быстро, то при быстром итерировании ссылка на "е" будет изменена и в канале сообщения соответственно, тоже поменяются?
не ссылка поменяется, а в память по этой ссылке for поместит следующее значение
источник

VT

Vasiliy Toporov in Go-go!
Да, всё, теперь я понял, в чём проблема. Спасибо.
источник

М

МишанЯ in Go-go!
Ребят, как правильно запустить web сервер Go на VPS?
источник

RS

Roman Sharkov in Go-go!
/dev/null
и желательно без нижнего подчеркивания)
это кто сказал?
источник

RS

Roman Sharkov in Go-go!
Sergey Cherkesov
Всем салют.
Вопрос - кто-нибудь в курсе зачем в файлах с имлементацией делают пустую инициализацию структуры. Типа

var _ MyStructInterface = &myStruct{} // ??????????

func New() *myStruct { return .. }
type myStruct struct {}
func (ms myStruct) SayMew() {}

так греют какой-нибудь кеш? что-то показывают линтеру или компилятору?
Явно указать что ты пытался имплементировать, чтоб после смены сигнатур методов не прошла сборка?
статическая проверка в compile-time имплементирует ли *myStruct интерфейс MyStructInterface
источник

DD

Deka Dancer in Go-go!
Vasiliy Toporov
> а можно сигнатуру GetStreamNameFromEventName
func (e *Event) GetStreamNameFromEventName() string {
 splittedName := strings.Split(e.Name, ":")
 if len(splittedName[0]) > 0 && splittedName[0] != e.Name {
   return splittedName[0]
 }

 return e.Name
}

>вы берёте ссылку на e, не надо так
Приходится, так как канал - это канал ссылок на структуры типа Event.
Можно мутексом лочить при работе
источник

VI

Vadim Inshakov in Go-go!
Владимир Столяров
не совсем, rate.Limit это частота, а вот burst - количество событий за период
то есть, при t=1с и burst=3 можно будет либо 3 раза за одну секунду вызвать Allow() либо 1 раз за секунду AllowN(3)
то есть если у меня такая мидлварь для ограничения запросов

func RateLimit(next http.Handler, limiter *rate.Limiter) http.Handler {
 return http.HandlerFunc(func(w

http.ResponseWriter, r *http.Request) {
   if limiter.Allow() == false {
     http.Error(w, http.StatusText(429), http.StatusTooManyRequests)
     return
   }
   next.ServeHTTP(w, r)
 })
}

то мне burst больше, чем limit не нужен? AllowN() в мидлваре вообще бессмысленен, потому что всегда приходит ровно один запрос. то есть это для каких-то других случаев?
источник

ВС

Владимир Столяров... in Go-go!
да, обычно я и сам делал просто rate.NewLimiter(rate.Limit(t), 1)
источник

ВС

Владимир Столяров... in Go-go!
видимо нужно когда события как-то аггрегированно приходят
источник

VI

Vadim Inshakov in Go-go!
Владимир Столяров
видимо нужно когда события как-то аггрегированно приходят
понятно, спасибо большое, а то в доках мало объяснений что для чего
источник