Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Go meetup smotri+ 23.04.2015

30,516 views

Published on

Moscow Golang meetup presentation 23.04.2015 about Smotri+ application made by Mikhail Salosin

Published in: Software
  • Hello! Who wants to chat with me? Nu photos with me here http://bit.ly/helenswee
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

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!

×