Библиотека для броадкастов через каналы в go

September 2013 · 2 minute read

С каналами в Go можно делать много разных забавных вещей, но вот броадкасты из коробки на них не получишь. Поискав по интернету сделал пакет для броадкаста любых данных (interface{}) через каналы. Откуда-нибудь (логично из другой горутины, иначе зачем весь огород) приконнекчиваемся к broadcast-группе, общение идёт через два канала — на приём и передачу. На приём канал у каждого участника группы свой, на передачу — общий для всех. Диспетчер разруливает полученные сообщения и переправляет их остальным участникам. Посмотрел более эстетские варианты, где сообщения передаются по цепочке от участника участнику, попробовал свою реализацию в таком стиле — сложновато выходит по структурам данных, хотя идея красивая. В итоге реализовал простую схему с диспетчером.

http://github.com/grafov/bcast/

BTW, для случая когда надо уведомить остальные горутины о завершении к.-л. процесса удобно использовать close() — http://nf.wh3rd.net/10things/#11, но для постоянного обмена сообщениями этот способ не катит.

Пример использования пакета:

group := bcast.NewGroup() // create broadcast group
go bcast.Broadcasting(0) // accepts messages and broadcast it to all members

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

bcast.Broadcasting(2 * time.Minute) // if message not arrived during 2 min. function exits

Теперь можно соединяться из разных там горутин:

member1 := group.Join() // joined member1 from one routine
defer member1.Close() // optional close
member1.Send("test message") // broadcast message

Метод bcast.Send принимает тип interface{} поэтому можно передавать что угодно:

member1.Send(group) // передадим сам объект группы броадкастов, не уж знаю зачем

Коннектимся откуда-нибудь ещё:

member2 := group.Join() // joined member2 form another routine
val := member1.Recv() // broadcasted value received

Можно напрямую слушать входящий канал для получения данных. Это может быть удобно, когда хочется использовать select:

val := <-*member1.In // each member keeps pointer to its own input channel