420 likes | 434 Views
Дмитрий Вьюков, dvyukov@ Google. Пишем функциональное, надежное и быстрое веб-приложение на Go. План. Зачем новый язык? Привет, Мир! Пишем веб-приложение Инструментарий. История. Конец 2007: Rob Pike, Ken Thompson и RobertGriesemer начали проектировать Ноябрь 2009: open-source release
E N D
Дмитрий Вьюков, dvyukov@ Google Пишем функциональное, надежное и быстрое веб-приложение на Go
План • Зачем новый язык? • Привет, Мир! • Пишем веб-приложение • Инструментарий
История • Конец 2007: Rob Pike, Ken Thompson и RobertGriesemer начали проектировать • Ноябрь 2009: open-source release • Март 2012: релиз Go1 • Go1.1, Go1.2, Go1.3
Зачем? Дизайн мотивирован нашими нуждами в Google: • Large-scale системы • Large-scale разработка • Время сборки • Эффективность • Масштабируемость • Concurrency • Безопасность
Что такое Go? • Простой язык, который легко изучить и читать • Статически-типизированный, но “чувствуется” как динамический • Компилируется в машинный код, но быстрая разработка • Быстрый и масштабируемый • Сборка мусора • Встроенная поддержка concurrency • Стандартная библитека “с включенными батарейками” • Open-source (400+ контрибьюторов)
Кто использует? • Google: dl.google.co • Google: vitess (MySQL scaling) • Google: flywheel (mobile proxy) • BBC Worldwide • Canonical • Heroku • Nokia • SoundCloud • Apple • Yandex • BitBucket • dotCloud • github • gov.uk • Heroku • Intel • Iron.io • Mozilla • Percona • Zynga • Docker • Packer
Привет, Мир! • package main • import "fmt" • func main() { • fmt.Printf("Привет, Мир!\n") • }
Привет, Net! • func main() { • ln, err := net.Listen("tcp", "localhost:11500") • if err != nil { • log.Fatal(err) • } • for { • c, err := ln.Accept() • if err != nil { • log.Fatal(err) • } • fmt.Fprintf(c, "Привет, Net!\n") • c.Close() • } • }
Интерфейсы • fmt.Fprintf(c, "Привет, Net!\n") • Duck typing! • type Writer interface { • Write(p []byte) (n int, err error) • } • type netFD struct {...} • func (fd *netFD) Write(p []byte) (nn int, err error)
Эхо-сервер func main() { ln, err := net.Listen("tcp", "localhost:11500") if err != nil { log.Fatal(err) } for { c, err := ln.Accept() if err != nil { log.Fatal(err) } io.Copy(c, c) } }
Привет, concurrency! func main() { ln, err := net.Listen("tcp", "localhost:11500") if err != nil { log.Fatal(err) } for { c, err := ln.Accept() if err != nil { log.Fatal(err) } go io.Copy(c, c) } }
Сoncurrency Горутины: go doFoo(a, b) go conn.Handle() go func() { doFoo(a, b) notifyCompletion() }()
Channels Каналы: c := make(chan int) c <- 42 fmt.Println(<-c)
Select select { case inChan <- v: fmt.Println("Отправлено") case <-timeoutChan: fmt.Println("Потрачено") }
Запрос нескольких бэкендов • func multiGet(urls []string) (results []*http.Response) { • res := make(chan *http.Response) • for _, url := range urls { • go func(url string) { • resp, _ := http.Get(url) • res <- resp • }(url) • } • for _ = range urls { • results = append(results, <-res) • } • return • }
Запрос с таймаутом • t := time.After(time.Second) • for _ = range urls { • select { • case resp := <-res: • results = append(results, resp) • case <-t: • return • } • }
Файловер • func getFailover(primary, secondary string) *http.Response { • res := make(chan *http.Response, 1) • go func() { • res <- requestServer(primary) • }() • select { • case resp := <-res: • return resp • case <-time.After(50 * time.Millisecond): • } • go func() { • res <- requestServer(secondary) • }() • return <-res • }
Батарейки включены encoding/{json, xml, gob, base64} compress/{gzip, bzip2, flate} crypto/{aes, des, sha1, tls, x509} net/{http, rpc, smtp} database/sql html/template regexp flag
Флаги • package main • import "flag" • var ( • httpAddr = flag.String("http", ":8080", "address to serve http") • apiAddr = flag.String("api", ":8081", "address to serve api") • ) • func main() { • flag.Parse() • ...
HTTP сервер • import "net/http" • func main() { • ... • http.HandleFunc("/", handleWelcome) • http.HandleFunc("/chat", handleChat) • go http.ListenAndServe(*httpAddr, nil) • ...
Websocket сервер • import "code.google.com/p/go.net/websocket" • func main() { • ... • http.Handle("/ws", • websocket.Handler(handleWebsocket)) • ...
API сервер • import "net" • func main() { • ... • ln, err := net.Listen("tcp", *apiAddr) • if err != nil { • log.Fatal(err) • } • go func() { • for { • c, err := ln.Accept() • if err != nil { • log.Fatal(err) • } • go handleApi(c) • } • }()
Бизнес логика • type Message struct { • From string • Text string • } • type Client interface { • Send(m Message) • } • var ( • clients = make(map[Client]bool) • clientsMutex sync.Mutex • )
Бизнес логика (2) • func registerClient(c Client) { • clientsMutex.Lock() • clients[c] = true • clientsMutex.Unlock() • } • func unregisterClient(c Client) { • clientsMutex.Lock() • delete(clients, c) • clientsMutex.Unlock() • }
Бизнес логика (3) • func broadcastMessage(m Message) { • clientsMutex.Lock() • for c := range clients { • c.Send(m) • } • clientsMutex.Unlock() • }
HTML шаблоны • var chatPageTempl = template.Must(template.New("").Parse(` • <html> • <body> • <b>Hi, {{.Name}}!</b> • <form> • <input type="text" id="chattext"></input> • <input type="button" value="Say" • onclick="sendMessage()"></input> • </form> • <textarea readonly=1 rows=20 id="alltext"></textarea> • </body> • </html> • `))
HTTP обработчики • func handleChat(w http.ResponseWriter, r *http.Request) { • name := r.FormValue("name") • log.Printf("serving chat page for '%v'", name) • type Params struct { • Name string • } • chatPageTempl.Execute(w, &Params{name}) • }
Websocket обработчик • type WSClient struct { • conn *websocket.Conn • enc *json.Encoder • } • func (c *WSClient) Send(m Message) { • c.enc.Encode(m) • }
Websocket обработчик (2) • func handleWebsocket(ws *websocket.Conn) { • c := &WSClient{ws, json.NewEncoder(ws)} • registerClient(c) • dec := json.NewDecoder(ws) • for { • var m Message • if err := dec.Decode(&m); err != nil { • log.Printf("error reading from websocket: %v", err) • break • } • broadcastMessage(m) • } • unregisterClient(c) • }
Сборка Нулевая конфигурация, нет MAKEFILE $ go run myprog.go $ go build myprog $ go get code.google.com/p/go.new/websocket
Пишем код $ go fmt mysource.go $ go vet - статический анализ кода $ godoc - документация goimports/gocode/oracle: поддержка IDE
Тестируем $ go test $ go test -cover $ go test -race Информативные крэш-репорты
Оптимизируем $ go test -bench $ go test -bench -cpu=1,2,4 $ go test -benchmem $ go test -cpuprofile $ go test -memprofile $ go test -blockprofile
Оптимизируем (2) - Профайлер горутин - Трейсер GC - Трейсер планировщика - Трейсер аллокатора памяти - Статистика аллокатора памяти - Дамп кучи
Мониторинг import "expvar" var ( expMsgRecv = expvar.NewInt("msg_recv") expMsgSend = expvar.NewInt("msg_send") ) func broadcastMessage(m Message) { ... expMsgRecv.Add(1) expMsgSend.Add(int64(len(clients))) }
Что дальше? http://golang.org http://tour.golang.org golang-nuts@ Книги: • An Introduction to Programming in Go • Network Programming with Go • Programming in Go: Creating Applications for the 21st Century
Другие доклады по Go • 16:35 Common: Go: аналитика Рунета в реальном времени • 17:45 Python: Go на Google App Engine - просто, надёжно, быстро и недорого. • 19:30 Python: Juju и MaaS - эффективные инструменты развёртывания масштабных систем на "железе" и в "облаках".
Q&A Спасибо! Дмитрий Вьюков, dvyukov@ http://golang.org