Successfully reported this slideshow.
Your SlideShare is downloading. ×

Go meetup smotri+ 23.04.2015

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
Retro vs Volley
Retro vs Volley
Loading in …3
×

Check these out next

1 of 19 Ad

More Related Content

Slideshows for you (19)

Viewers also liked (20)

Advertisement

Similar to Go meetup smotri+ 23.04.2015 (20)

Advertisement

Go meetup smotri+ 23.04.2015

  1. 1. Использование  Go в  бэкенде приложения   Смотри+ Михаил  Салосин
  2. 2. Приложение Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+ 2
  3. 3. Что  использовали • Go • PostgreSQL • Ruby on  Rails  – ActiveAdmin,  импорт  статистики • Python  – системные  тесты • Memcached • Chef • Zabbix • Graylog2 • Slate 3Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+
  4. 4. Выбор  протокола • Данные  на  клиентах  должны  обновлять  в   реальном  времени • Данные,  синхронизируемые  с  клиентами,  не   удаляются • Статистика  и  составы  команд  получаются   обычными  GET  запросами • 100  тыс.  пользователей 4Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+
  5. 5. Выбор  протокола • Websocket – избыточно • Server-­‐Sent  Events  (SSE)  – в  самый  раз! 5Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+
  6. 6. Server-­‐Sent  Events 6Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+ Content-­‐Type:  text/event-­‐stream event:  Person data:  {“id”:  10,  name:  ”Илья  Ковальчук”}nn
  7. 7. Server-­‐Sent  Events 7Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+
  8. 8. Server-­‐Sent  Events 8Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+ func (h *APIHandler) UpdatesLive(rw http.ResponseWriter, req *http.Request) { updates := make(chan interface{}, 100) h.DataUpdates.Subscribe(updates) defer h.DataUpdates.Unsubscribe(updates) h.setSseHeaders(rw) h.pingAndFlush(rw, strconv.FormatInt(time.Now().UnixNano(), 10)) for { select { case data, ok := <-updates: if !ok { return } switch v := data.(type) { case int64: // unix time ping h.pingAndFlush(rw, strconv.FormatInt(v, 10)) case reform.Struct: h.writeSseStruct(rw, req, v) rw.(http.Flusher).Flush() } } } }
  9. 9. Server-­‐Sent  Events 9Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+ func (*APIHandler) setSseHeaders(rw http.ResponseWriter) { rw.Header().Set("Content-Type", "text/event-stream; charset=utf-8") } func (h *APIHandler) writeSseStruct(rw http.ResponseWriter, req *http.Request, str reform.Struct) { b, err := json.Marshal(str) _, err = fmt.Fprint(rw, "event:"+str.View().Name()+"n") _, err = fmt.Fprint(rw, "data:") _, err = rw.Write(b) _, err = fmt.Fprint(rw, "nn") } func (*APIHandler) pingAndFlush(rw http.ResponseWriter, data string) { _, err := fmt.Fprintf(rw, ":%snn", data) rw.(http.Flusher).Flush() }
  10. 10. Механизм  отправки  обновлений 10Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+
  11. 11. PostgreSQL:  Listen/Notify 11Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+ CREATE OR REPLACE FUNCTION data_updated() RETURNS trigger AS $$ BEGIN IF TG_OP = 'INSERT' OR OLD <> NEW THEN PERFORM pg_notify('data_updates', TG_TABLE_NAME || ' ' || NEW.id); END IF; RETURN NULL; END; $$ LANGUAGE plpgsql; CREATE TRIGGER on_season_update AFTER INSERT OR UPDATE ON seasons FOR EACH ROW EXECUTE PROCEDURE data_updated();
  12. 12. PostgreSQL:  Listen/Notify func ListenForDataUpdates(config *utils.DbConfig) (*utils.Fanout, error) { f := utils.NewFanout() l := pq.NewListener("user=smotri-api dbname=smotri", 50*time.Millisecond, 10*time.Second) err := l.Listen("data_updates") err = l.Ping() go func() { pingTicker := time.Tick(15 * time.Second) for { var n *pq.Notification select { case t := <-pingTicker: un := t.UnixNano() logger.Printf("data_updates: ping %d", un) f.Publish(un) continue case n = <-l.Notify: if n == nil { logger.Printf("data_updates: reconnected") continue } } ... f.Publish(str) } } return f, nil } 12
  13. 13. Golang:  Fan-­‐out 13Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+
  14. 14. Golang:  Fan-­‐out 14 type Fanout struct { m sync.Mutex s map[chan<- interface{}]struct{} c bool } func NewFanout() *Fanout { return &Fanout{s: make(map[chan<- interface{}]struct{})} } func (f *Fanout) Connected() { f.m.Lock() f.c = true f.m.Unlock() } func (f *Fanout) Disconnected() { f.m.Lock() f.c = false for c := range f.s { delete(f.s, c) close(c) } f.m.Unlock() }
  15. 15. Golang:  Fan-­‐out 15 func (f *Fanout) Subscribe(c chan<- interface{}) { f.m.Lock() defer f.m.Unlock() if !f.c { panic(errors.New("fanout: not connected")) } f.s[c] = struct{}{} } func (f *Fanout) Unsubscribe(c chan<- interface{}) { f.m.Lock() _, ok := f.s[c] if ok { delete(f.s, c) close(c) } f.m.Unlock() } func (f *Fanout) Publish(v interface{}) { f.m.Lock() for c := range f.s { select { case c <- v: default: delete(f.s, c) close(c) } } f.m.Unlock() }
  16. 16. Инфраструктура 16Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+
  17. 17. Плюсы  Go • Rich  http  library • Каналы • Race  detector • Минимализм  и  простота 17Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+
  18. 18. Минусы  Go • if  err  !=  nil 18Михаил  Салосин.   Использование  Go в  бэкенде приложения   Смотри+
  19. 19. Спасибо! P.S.  WE  ARE  HIRING!

×