Консольные приложения на go

41
Консольные приложения на Go Андрей Смирнов, Go Meetup July 2014

Upload: andrey-smirnov

Post on 16-Jun-2015

6.041 views

Category:

Engineering


4 download

DESCRIPTION

Доклад с Golang Meetup 24.07.2014

TRANSCRIPT

Page 1: Консольные приложения на Go

Консольные приложения на Go

Андрей Смирнов, Go Meetup July 2014

Page 2: Консольные приложения на Go

aptly• Система управления репозиториями Debian-пакетов

• Зеркалирование репозиториев, управление репозиториями своих пакетов

• Создание snapshotов репозиториев, слияние, перемещение пакетов

• Публикация как Debian-репозиториев

Page 3: Консольные приложения на Go

Почему Go?• Fun!

• Большие объемы метаданных (30Мб/архитектура)

• Статическая компиляция

• Отличная HTTP-библиотека

• Там нет genericов? (контейнеры)

Page 4: Консольные приложения на Go

Как вышло

• Отличный набор библиотек (всех!)

• Высокая производительность

• Аккуратно работать с памятью

• Язык позволяет правильно структурировать программу

Page 5: Консольные приложения на Go

Параллельныйdownload

Page 6: Консольные приложения на Go

Задача

• Скачивать файлы в несколько потоков (асинхронно)

• Скачивать файл и ожидать результата

Page 7: Консольные приложения на Go

Скачиватель

channel: queue

Worker 1(goroutine)

Worker 2(goroutine)

Worker 3(goroutine)

Worker 4(goroutine)

Page 8: Консольные приложения на Go

Возврат результата

func (d *Downloader) Download(url, destination string) error !

Page 9: Консольные приложения на Go

Возврат результата

func (d *Downloader) Download(url, destination string) error !

!func (d *Downloader) Download(url, destination string) <-chan error !!

Page 10: Консольные приложения на Go

Возврат результата

func (d *Downloader) Download(url, destination string) error !

!func (d *Downloader) Download(url, destination string) <-chan error !!

func (d *Downloader) Download(url, destination string, result chan<- error)

Page 11: Консольные приложения на Go

Синхронный вариант

func (d *Downloader) DownloadWait(url, destination string) error { ch := make(chan error) d.Download(url, destination, ch) return <-ch }

Page 12: Консольные приложения на Go

Скачивание по спискуlist := ... !results := make(chan error, len(list)) !for _, l := range list { d.Download(l.url, l.destination, results) } !for i := 0; i < len(list); i++ { err := <-results if err != nil { fmt.Printf("Download error: %s\n") } }

Page 13: Консольные приложения на Go

Приостановкаfunc NewDownloader(N int) *Downloader {!! return &Downloader{!! ! queue: make(chan *Task, 1000),!! ! pause: make(chan struct{}),!! ! unpause: make(chan struct{}),!! ! threads: N,!! }!}!!func (downloader *Downloader) Pause() {!! for i := 0; i < downloader.threads; i++ {!! ! downloader.pause <- struct{}{}!! }!}!!!! for i := 0; i < downloader.threads; i++ {!! ! go func() {!! ! ! for {!! ! ! ! select {!! ! ! ! case <-downloader.pause:!! ! ! ! ! <-downloader.unpause!! ! ! ! case task := <-downloader.queue:!! ! ! ! ! downloader.handleTask(task)!! ! ! ! }!! ! ! }!! ! }()!! }!

Page 14: Консольные приложения на Go

Прогресс

Page 15: Консольные приложения на Go

Прогресс

Page 16: Консольные приложения на Go

Архитектура

ProgressBaratomic N 200 msredraw

goroutine pb

goroutine main

updateupdate

update

Page 17: Консольные приложения на Go

Архитектура

ProgressBaratomic N 200 msredraw

goroutine pb

goroutine main

updateupdate

update

channel

goroutine console

write to stdout

Page 18: Консольные приложения на Go

io.MultiWriter!resp, err = http.Get(url) outfile, err = os.Create(destination) _, err = io.Copy(outfile, resp.Body)

Page 19: Консольные приложения на Go

io.MultiWriter!resp, err = http.Get(url) outfile, err = os.Create(destination) _, err = io.Copy(outfile, resp.Body)

resp, err = http.Get(task.url) outfile, err = os.Create(temppath) !_, err = io.Copy( io.MultiWriter(outfile, progressbar), resp.Body)

Page 20: Консольные приложения на Go

Writer

type Writer interface { Write(p []byte) (n int, err error) } !func (p* ProgressBar) Increment(n int) { ... } !func (p* ProgressBar) Write(p []byte) (n int, err error) { n = len(p) p.Increment(n) return }

Page 21: Консольные приложения на Go

CheckSummertype ChecksumWriter struct { hashes []hash.Hash } !func NewChecksumWriter() *ChecksumWriter { return &ChecksumWriter{ hashes: []hash.Hash{md5.New(), sha1.New(), sha256.New()}, } } !func (c *ChecksumWriter) Write(p []byte) (n int, err error) { for _, h := range c.hashes { h.Write(p) } ! return len(p), nil }

Page 22: Консольные приложения на Go

Ограничение скорости• Задача: ограничить общую скорость скачивания

flow = flowcontrol.NewWriter(downloadLimit) !... !_, err = io.Copy( io.MutliWriter(outfile, flow, progress, checksummer), resp)

Page 23: Консольные приложения на Go

Вложенные команды• Или история и о первом pull request в aptly

• Что получилось:

• aptly mirror create …

• aptly mirror list

• aptly snapshot create from mirror …

Page 24: Консольные приложения на Go

commander

• Простой способ создавать подкоманды

• История про первый pull-request или версионирование библиотек Go

• Большое количество патчей

• Альтернатива (лучше!) - cobra

Page 25: Консольные приложения на Go

GC

• mark-and-sweep

• precise (1.3), parallel, concurrent sweep (1.3)

Page 26: Консольные приложения на Go

aptly• Обновление зеркала репозитория

• Парсинг мета-информации о пакетах, обработка, сохранение в БД

• 40Мб/архитектура

• При создании зеркала используется 800Мб памяти! 😭

Page 27: Консольные приложения на Go

Анализ• runtime-статистика об использовании кучи

• runtime.ReadMemStats():

• HeapSys

• HeapAlloc

• HeapIdle

Page 28: Консольные приложения на Go
Page 29: Консольные приложения на Go
Page 30: Консольные приложения на Go

Что делать?

• Долго работающие функции: освобождать ненужные объекты (x = nil)

• Повторное использование объектов вместо выделения новых

Page 31: Консольные приложения на Go

Было

func (p *Package) Encode() []byte {! var buf bytes.Buffer!! encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})! encoder.Encode(p)!! return buf.Bytes()!}!

Page 32: Консольные приложения на Go

Сталоvar (!! encodeBuf bytes.Buffer!! codecHandle = &codec.MsgpackHandle{}!)!!func (p *Package) Encode() []byte {!! encodeBuf.Reset()!!! encoder := codec.NewEncoder(&encodeBuf, codecHandle)!! encoder.Encode(p)!!! return encodeBuf.Bytes()!}!

Page 33: Консольные приложения на Go

А что дальше?

• Профилируем программу по памяти и процессору

• (Много думаем)

• Придумываем решение!

Page 34: Консольные приложения на Go
Page 35: Консольные приложения на Go
Page 36: Консольные приложения на Go

Сборка и релиз

• aptly - кроссплатформенный инструмент

• Статическая компиляция

• go cross compiling

• Запуск системных тестов

Page 37: Консольные приложения на Go

Решение “в лоб”

• Виртуалки CentOS/Debian/FreeBSD/32/64

• Сборка: установим go с помощью gvm

• Запуск системных тестов на каждой платформе

• Распространение: просто скачать исполняемый файл

Page 38: Консольные приложения на Go

Создание пакетов ОС

• Сложно упаковать “корректно” со всеми зависимостями

• “Бинарные” пакеты: fpm

Page 39: Консольные приложения на Go

Контейнеры• Фобия? (Страх до начала разработки)

• Slices + package sort = 90% решений

• В реальность модуль из 141 строки с общими операциями над []string

• Сложные алгоритмы не выражаются в терминах genericов

• Шанс на микрооптимизацию

Page 40: Консольные приложения на Go

Вопросы?

• @smira

[email protected]

• Skype: smirnov.andrey

• http://smira.ru/

Page 41: Консольные приложения на Go

Ссылки• aptly: github.com/smira/aptly

• ProgressBar: github.com/cheggaaa/pb

• flowcontrol: code.google.com/p/mxk/go1/flowcontrol

• commander: github.com/gonuts/commander

• cobra: github.com/spf13/cobra