Статьи

12 необходимых пакетов Go и библиотек

Go – удивительный язык с большим количеством импульсов, и он сосредоточен на простоте. Этот подход очевиден в его стандартной библиотеке, которая предоставляет все необходимое, но не намного.

К счастью, у Go есть активное сообщество, которое создает и разделяет множество сторонних библиотек. В этом уроке я познакомлю вас с 12 лучшими пакетами и библиотеками Go. Некоторые из них имеют относительно узкую область применения и могут быть добавлены в любые проекты, в то время как другие представляют собой огромные проекты, которые вы можете включить в массивные, масштабные распределенные системы.

Прежде чем углубиться в сами библиотеки, позвольте мне познакомить вас с Awesome Go , очень активным и курируемым списком библиотек Go и другими ресурсами. Вы должны посещать время от времени и проверить, что нового.

Go имеет массивы, фрагменты и карты, но не имеет заданной структуры данных. Вы можете имитировать набор с помощью карты bools, но хорошо иметь фактический тип данных с правильными операциями и семантикой. Вот где приходит golang-set . Вот базовый пример создания нового набора, добавления элементов и тестирования на членство:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main
 
import (
    “fmt”
    “github.com/deckarep/golang-set”
)
 
 
func main() {
    basicColors := mapset.NewSet()
    basicColors.Add(“Red”)
    basicColors.Add(“Blue”)
    basicColors.Add(“Green”)
 
    if basicColors.Contains(“Green”) {
        fmt.Println(“Yay! ‘Green’ is a basic color”)
    } else {
        fmt.Println(“What a disappointment! ‘Green’ is not a basic color”)
    }
 
 
    if basicColors.Contains(“Yellow”) {
        fmt.Println(“Yay! ‘Yellow’ is a basic color”)
    } else {
        fmt.Println(“What a disappointment! ‘Yellow’ is not a basic color”)
    }
}
 
Output:
 
Yay!
What a disappointment!

Обратите внимание, что имя пакета “mapset”. В дополнение к основам вы выполняете все операции над множествами, такие как объединение, пересечение и разность. Вы также можете перебирать установленные значения:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
package main
 
import (
    “fmt”
    “github.com/deckarep/golang-set”
)
 
 
func main() {
    basicColors := mapset.NewSet()
    basicColors.Add(“Red”)
    basicColors.Add(“Blue”)
    basicColors.Add(“Green”)
 
    otherColors := mapset.NewSetFromSlice([]interface{}{“Orange”, “Yellow”, “Indigo”, “Violet”})
    rainbowColors := basicColors.Union(otherColors)
 
    for color := range rainbowColors.Iterator().C {
        fmt.Println(color)
    }
}

Давайте продолжим с цветовой темой. При написании программ командной строки полезно использовать цвета, чтобы выделить важные сообщения или отличить ошибки, успехи и предупреждения.

Пакет цветов дает простой способ добавить цвет в ваши программы (посмотрите, что я там делал?). Он использует управляющие коды ANSII и также поддерживает Windows! Вот быстрый пример:

01
02
03
04
05
06
07
08
09
10
package main
 
import (
    “github.com/fatih/color”
)
 
func main() {
    color.Red(“Roses are red”)
    color.Blue(“Violets are blue”)
}

Пакет цветов поддерживает смешивание цветов с фоновыми цветами, стилями, такими как полужирный или курсив, и разбрызгивание цветов с выводом нецветных цветов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
package main
 
import (
    “github.com/fatih/color”
    “fmt”
)
 
func main() {
    minion := color.New(color.FgBlack).Add(color.BgYellow).Add(color.Bold)
    minion.Println(“Minion says: banana!!!!!!”)
 
    m := minion.PrintlnFunc()
    m(“I want another banana!!!!!”)
 
    slantedRed := color.New(color.FgRed, color.BgWhite, color.Italic).SprintFunc()
    fmt.Println(“I’ve made a huge”, slantedRed(“mistake”))
}

Пакет цветов имеет и другие полезные функции. Идите и исследуйте больше.

Теперь это очень простой пакет, который предоставляет удобную обертку для стандартного временного пакета и позволяет легко работать с различными конструкциями даты и времени вокруг текущего времени.

Например, вы можете получить начало текущей минуты или конец воскресенья, ближайший к текущему времени. Вот как использовать «сейчас»:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main
 
import (
    “github.com/jinzhu/now”
    “fmt”
)
 
func main() {
 
    fmt.Println(“All the beginnings…”)
    fmt.Println(now.BeginningOfMinute())
    fmt.Println(now.BeginningOfHour())
    fmt.Println(now.BeginningOfDay())
    fmt.Println(now.BeginningOfWeek())
    fmt.Println(now.BeginningOfMonth())
    fmt.Println(now.BeginningOfQuarter())
    fmt.Println(now.BeginningOfYear())
 
}
 
Output:
 
All the beginnings…
2017-06-04 16:59:00 -0700 PDT
2017-06-04 16:00:00 -0700 PDT
2017-06-04 00:00:00 -0700 PDT
2017-06-04 00:00:00 -0700 PDT
2017-06-01 00:00:00 -0700 PDT
2017-04-01 00:00:00 -0700 PDT
2016-12-31 23:00:00 -0800 PST

Вы также можете анализировать время и даже добавлять свои собственные форматы (что потребует обновления известных форматов). Тип Now встраивает time.Time , поэтому вы можете использовать все методы time.Time непосредственно для объектов Now .

Инструмент gen генерирует для вас код, в частности код с учетом типов, который пытается устранить пробел в отсутствии шаблонов или обобщений в Go.

Вы аннотируете свои типы специальным комментарием, и gen генерирует исходные файлы, которые вы включаете в свой проект. Никакой магии во время выполнения. Давайте посмотрим на пример. Вот аннотированный тип.

1
2
3
4
5
// +gen slice:”Where,Count,GroupBy[int]”
type Person struct {
    Name string
    Age int
}

Запуск gen (убедитесь, что он на вашем пути) генерирует person_slice.go :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Generated by: gen
// TypeWriter: slice
// Directive: +gen on Person
 
package main
 
// PersonSlice is a slice of type Person.
type PersonSlice []Person
 
// Where returns a new PersonSlice whose elements return true for func.
func (rcv PersonSlice) Where(fn func(Person) bool) (result PersonSlice) {
    for _, v := range rcv {
        if fn(v) {
            result = append(result, v)
        }
    }
    return result
}
 
// Count gives the number elements of PersonSlice that return true for the passed func.
func (rcv PersonSlice) Count(fn func(Person) bool) (result int) {
    for _, v := range rcv {
        if fn(v) {
            result++
        }
    }
    return
}
 
// GroupByInt groups elements into a map keyed by int.
func (rcv PersonSlice) GroupByInt(fn func(Person) int) map[int]PersonSlice {
    result := make(map[int]PersonSlice)
    for _, v := range rcv {
        key := fn(v)
        result[key] = append(result[key], v)
    }
    return result
}

Код предоставляет LINQ-подобные методы для работы с типом PersonSlice . Это легко понять и хорошо задокументировано.

Вот как вы это используете. В основной функции определена PersonSlice . Функция age() выбирает поле age из аргумента Person . Сгенерированная GroupByInt() берет функцию age() и возвращает людей из среза, сгруппированного по возрасту (34 – это просто Джим, но 23 имеет и Джейн, и Кайла).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main
 
import (
    “fmt”
)
 
// +gen slice:”Where,Count,GroupBy[int]”
type Person struct {
    Name string
    Age int
}
 
func age(p Person) int {
    return p.Age
}
 
func main() {
    people := PersonSlice {
        {“Jim”, 34},
        {“Jane”, 23},
        {“Kyle”, 23},
    }
 
    groupedByAge := people.GroupByInt(age)
 
    fmt.Println(groupedByAge)
}
 
 
Output:
 
map[34:[{Jim 34}] 23:[{Jane 23} {Kyle 23}]]

Го известен своей спартанской природой. Программирование баз данных не отличается. Самые популярные библиотеки БД для Go довольно низкоуровневые. Gorm привносит в Go мир объектно-реляционного отображения со следующими функциями:

  • Ассоциации (имеет один, имеет много, принадлежит, много ко многим, полиморфизм)
  • Обратные вызовы (до / после создания / сохранения / обновления / удаления / поиска)
  • Предварительная загрузка (готовая загрузка)
  • операции
  • Составной первичный ключ
  • SQL Builder
  • Авто Миграции
  • лесоруб
  • Расширяемый, писать плагины на основе обратных вызовов GORM

Но это не охватывает все. Если вы пришли из Python, не ожидайте магии SQLAlchemy. Для более причудливых вещей, вам придется перейти на более низкий уровень. Вот пример того, как использовать Gorm с sqlite. Обратите внимание на встроенный gorm.Model в структуре Product.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main
 
import (
    “github.com/jinzhu/gorm”
    _ “github.com/jinzhu/gorm/dialects/sqlite”
)
 
type Product struct {
  gorm.Model
  Code string
  Price uint
}
 
func main() {
  db, err := gorm.Open(“sqlite3”, “test.db”)
  if err != nil {
    panic(“failed to connect database”)
  }
  defer db.Close()
 
  // Migrate the schema
  db.AutoMigrate(&Product{})
 
  // Create
  db.Create(&Product{Code: “L1212”, Price: 1000})
 
  // Read
  var product Product
  db.First(&product, 1) // find product with id 1
  db.First(&product, “code = ?”, “L1212”)
 
  // Update – update product’s price to 2000
  db.Model(&product).Update(“Price”, 2000)
 
  // Delete – delete product
  db.Delete(&product)

Одной из наиболее важных задач при работе с реляционными базами данных является управление схемой. В некоторых организациях изменение схемы БД считается «страшным» изменением. Пакет Goose позволяет выполнять изменения схемы и даже миграцию данных при необходимости. Вы можете goose up и goose down чтобы идти вперед и назад. Следите за своими данными, хотя, и убедитесь, что они не будут потеряны или повреждены.

Goose работает путем создания версий вашей схемы и использования файлов миграции, соответствующих каждой схеме. Файлы миграции могут быть командами SQL или командами Go. Вот пример файла миграции SQL, который добавляет новую таблицу:

01
02
03
04
05
06
07
08
09
10
— +goose Up
CREATE TABLE person (
    id int NOT NULL,
    name text,
    age int,
    PRIMARY KEY(id)
);
 
— +goose Down
DROP TABLE person;

Комментарии -- +goose up и -- +goose down сообщают goose, что нужно сделать, чтобы улучшить или понизить схему.

Glide – менеджер пакетов для Go. Под одной GOPATH вас может быть много программ, которые имеют конфликтующие зависимости. Решение состоит в том, чтобы каждая программа управляла своим собственным каталогом поставщиков зависимостей пакетов. Glide помогает с этой задачей.

Вот особенности скольжения:

  • Пакеты поддержки версий, включая поддержку Semantic Versioning 2.0.0.
  • Поддержка псевдонимов пакетов (например, для работы с вилками github).
  • Устранить необходимость в импровизированных операторах импорта.
  • Работайте со всеми инструментами go.
  • Поддержите все инструменты VCS, которые поддерживает Go (git, bzr, hg, svn).
  • Поддержка пользовательских локальных и глобальных плагинов.
  • Кэширование репозитория и данных для повышения производительности.
  • Сглаживание зависимостей, устранение различий в версиях и избежание многократного включения пакета.
  • Управляйте и устанавливайте зависимости по требованию или продавцу в вашей системе контроля версий.

Зависимости хранятся в glide.yaml , и glide предоставляет несколько команд для управления зависимостями:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
create, init Initialize a new project, creating a
                       glide.yaml file
    config-wizard, cw Wizard that makes optional suggestions
                       to improve config in a glide.yaml file.
    get Install one or more packages into
                       `vendor/` and add dependency to
                       glide.yaml.
    remove, rm Remove a package from the glide.yaml
                       file, and regenerate the lock file.
    import Import files from other dependency
                       management systems.
    name Print the name of this project.
    novendor, nv List all non-vendor paths in a
                       directory.
    rebuild Rebuild (‘go build’) the dependencies
    install, i Install a project’s dependencies
    update, up Update a project’s dependencies
    tree (Deprecated) Tree prints the
                       dependencies of this project as a tree.
    list List prints all dependencies that the
                       present code references.
    info Info prints information about this
                       project
    cache-clear, cc Clears the Glide cache.
    about Learn about Glide
    mirror Manage mirrors
    help, h Shows a list of commands or help for
                       one command

Ginkgo – это среда тестирования BDD (Behavior Driven Development). Это позволяет вам писать тесты в синтаксисе, напоминающем английский, и позволяет менее техническим специалистам просматривать тесты (и их результаты) и проверять, соответствуют ли они бизнес-требованиям.

Некоторым разработчикам нравится этот стиль спецификации теста. Он интегрируется со встроенным пакетом тестирования Go и часто сочетается с Gomega . Вот пример теста Гинкго + Гомега:

1
2
3
4
actual, err := foo()
Ω(err).Should(BeNil())
Ω(actual).ShouldNot(BeNil())
Ω(actual.result).Should(Equal(100))

Etcd – надежный распределенный магазин Key-Value. Сервер реализован на Go, и клиент Go взаимодействует с ним через gRPC .

Основное внимание уделяется следующему:

  • Просто: четко определенный пользовательский API (gRPC).
  • Безопасный: автоматический TLS с опциональной аутентификацией сертификата клиента.
  • Быстро: 10 000 записей в секунду.
  • Надежно: правильно распространяется с помощью Raft.

Вот пример подключения к серверу, установки значения и его получения, включая таймауты и очистку.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
func test_get() {
    cli, err := clientv3.New(clientv3.Config{
        Endpoints: endpoints,
        DialTimeout: dialTimeout,
    })
    if err != nil {
        log.Fatal(err)
    }
    defer cli.Close()
 
    _, err = cli.Put(context.TODO(), “foo”, “bar”)
    if err != nil {
        log.Fatal(err)
    }
 
    ctx, cancel := context.WithTimeout(context.Background(),
                                       requestTimeout)
    resp, err := cli.Get(ctx, “foo”)
    cancel()
    if err != nil {
        log.Fatal(err)
    }
    for _, ev := range resp.Kvs {
        fmt.Printf(“%s : %s\n”, ev.Key, ev.Value)
    }
    // Output: foo : bar
}

NSQ – отличная распределенная очередь. Я успешно использовал его в качестве основного строительного блока для крупных распределенных систем. Вот некоторые из его особенностей:

  • Поддержка распределенных топологий без SPOF.
  • Горизонтально масштабируемый (без посредников, легко добавляйте больше узлов в кластер).
  • Доставка сообщений с низкой задержкой (производительность).
  • Комбинированная маршрутизация сообщений с балансировкой нагрузки и многоадресным стилем.
  • Excel как для потоковых (с высокой пропускной способностью), так и для ориентированных на работу (с низкой пропускной способностью) рабочих нагрузок.
  • В основном в памяти (за пределами отметки верхнего уровня сообщения прозрачно хранятся на диске).
  • Служба обнаружения времени выполнения для потребителей, чтобы найти производителей (nsqlookupd).
  • Безопасность транспортного уровня (TLS).
  • Формат данных не зависит.
  • Несколько зависимостей (простота развертывания) и разумная ограниченная конфигурация по умолчанию.
  • Простой протокол TCP с поддержкой клиентских библиотек на любом языке.
  • HTTP-интерфейс для статистики, действий администратора и производителей (для публикации не требуется клиентская библиотека).
  • Интегрируется со statsd для инструментовки в реальном времени.
  • Надежный интерфейс администрирования кластера (nsqadmin).

Вот как опубликовать сообщение в NSQ (обработка ошибок исключена):

01
02
03
04
05
06
07
08
09
10
11
12
13
package main
 
import (
  “github.com/bitly/go-nsq”
)
 
func main() {
  config := nsq.NewConfig()
  p, _ := nsq.NewProducer(“127.0.0.1:4150”, config)
 
  p.Publish(“topic”, []byte(“message”))
  p.Stop()
}

А вот как потреблять:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main
 
import (
  “sync”
  “fmt”
  “github.com/bitly/go-nsq”
)
 
func main() {
  wg := &sync.WaitGroup{}
  wg.Add(1)
 
  config := nsq.NewConfig()
  q, _ := nsq.NewConsumer(“topic”, “channel”, config)
  handler := nsq.HandlerFunc(func(message *nsq.Message) error {
      fmt.Printf(“Got a message: %v”, message)
      wg.Done()
      return nil
  })
  q.AddHandler(handler)
  q.ConnectToNSQD(“127.0.0.1:4150”)
  wg.Wait()
}

Docker теперь является домохозяйством (если члены вашей семьи в основном люди DevOps). Вы можете не знать, что Docker реализован на Go. Обычно вы не используете Docker в своем коде, но это значительный проект, заслуживающий признания в качестве чрезвычайно успешного и популярного проекта Go.

Kubernetes – это платформа оркестрации контейнеров с открытым исходным кодом для облачных приложений. Это еще одна распределенная система монстров, реализованная в Go. Недавно я написал книгу « Освоение кубернетов», в которой подробно описываю самые продвинутые аспекты кубернетов. С точки зрения разработчика Go, Kubernetes очень гибок, и вы можете расширять и настраивать его с помощью плагинов.

Go это отличный язык. Его философия дизайна – быть простым и доступным языком. Его стандартная библиотека не такая всеобъемлющая, как некоторые другие языки, такие как Python.

Сообщество Go активизировалось, и есть много высококачественных библиотек, которые вы можете использовать в своих программах. В этой статье я представил 12 библиотек. Я призываю вас искать другие библиотеки, прежде чем приступить к реализации и реализовать все с нуля.