Пишем функциональное, надежное и быстрое...
description
Transcript of Пишем функциональное, надежное и быстрое...
Пишем функциональное, надежное и быстрое веб-
приложение на GoДмитрий Вьюков, dvyukov@
План
• Зачем новый язык?• Привет, Мир!• Пишем веб-приложение• Инструментарий
История Конец 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)
Selectselect {
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.orghttp://tour.golang.orggolang-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 - эффективные инструменты развёртывания масштабных систем на "железе" и в "облаках".