Вечер буднего дня, как же не заняться написанием статьи-заметки. В которой хочу поделиться впечатлениями о знакомстве с Go. Все что написано ниже, субъективное мнение автора. Данная статья будет полезна тем кто хочет сесть за изучение Go и окажется мало полезной для разработчиков на на нем.
Предыстория
Основной род моей деятельности — решение бизнесовых задач в среде сурового энтерпрайза на java, а по своей натуре хочется узнавать новое и интересное, для поддержки навыков, обучаться новому и не стоять на месте (этакий линейный->делец). И по этой причине давно пал взгляд на docker.
И вот на текущем проекте местами начал внедрять практики использования docker в среде разработчиков и тестировщиков. А где появляется docker, появляются контейнеры и об этих контейнерах хочется знать все от CPU до Network I/O. Для всего этого сначала пользовался стандартной связкой docker ps
и docker stats
. Были некоторые неудобства из-за того что это две отдельные утилиты, но в целом было сонно, пока не наткнулся на ctop. Думаю многим кто использует docker он знаком.
Проект
Запустив ctop на машине увидел красивые like-htop контейнеры со статистикой и возможностью посмотреть детальную информацию по контейнеру, с поиском или сортировкой. В общем я был крайне доволен.
До определенного момента, первое что хотелось бы увидеть в ctop — это healthcheck контейнера, но его не было. Потом всплыл docker swarm mode и для которого тоже хотелось бы видеть статистику по сервисам из ctop.
И тут въедливый читатель может задаться вопросом, а причем тут Go, если все про docker и личное мнение автора.
Имея опыт написания/допиливания под себя линуксовых утилит я подумал, ну раз это в терминале и это для консоли значит там должен быть python и сейчас быстренько добавим все что хочется.
Зайдя в репозиторий, удивился увидев там Go (хотя логично, docker на нем написан). Вспомнив чьи-то слова из доклада про Go, что его можно выучить за вечер, продолжил начинания в доработке программы. Почитав документацию и по запускав примеры с этого сайта сел за проект.
Примеры кода
При знакомстве с Go где-то проскочила фраза что он "лаконичен и прост в поддержке" и я соглашусь с этим высказываниями.
Первое что выглядело непривычным, но удобным — это GOPATH. Про настройку и сами понятия очень хорошо описали здесь.
Также показался интересным подход к организации и структуризации пакетов и уровне доступа. В частности объявление public и private методов через заглавную и строчные буквы и имплементация интерфейсов. И еще был несказанно рад указателям, как человек из мира java.
Управление зависимостями, по крайней мере на этом проекте не вызвало особых трудностей (хоть Go и ругали за отсутствие пакетного менеджера). Все что указывается в конструкции import
собирается в папке GOPATH в директории scr и зависимости уже можно использовать. Отдельно стоит отметить, что зависимость может быть прямо на проект с GitHub и она не сильно отличается от зависимости на какой-то собственный модуль (логика импорта сведена к минимуму):
import (
"fmt"
"strings"
"sync"
"github.com/bcicen/ctop/connector/collector"
api "github.com/fsouza/go-dockerclient"
"github.com/bcicen/ctop/config"
"context"
"github.com/bcicen/ctop/entity"
"github.com/docker/docker/api/types/swarm"
)
Что касается ctop — он оказался достаточно прост по своей структуре, за что отдельное спасибо его майнтенеру.
Структура проекта делится на три большие части:
- connector
- cwidgets
- grid/cursor
connector — интерфейс для подключения к разным сервисам предоставляемым информацию о контейнерах. Например, docker, opencontainer, kubernates и другие.
В директории connector создан класс main.go
, в котором описан интерфейс нашего коннектора:
package connector
type Connector interface {
All() container.Containers
Get(string) (*container.Container, bool)
}
А чтобы его заимплементить, нам нужно создать еще один файл, к примеру docker.go
и для метода NewDocker()
указать возвращаемый тип интерфейса и вернуть адрес для него.
А потом реализовать все перечисленные методы. Понравилось, что в рамках одного класса можно описать несколько реализаций интерфейса и это является нормальной практикой.
package docker
type Docker struct {
client *api.Client
containers map[string]*container.Container
needsRefresh chan string
lock sync.RWMutex
}
func NewDocker() Connector {
client, err := api.NewClientFromEnv()
if err != nil {
panic(err)
}
cm := &Docker{
client: client,
containers: make(map[string]*container.Container),
needsRefresh: make(chan string, 60),
lock: sync.RWMutex{},
}
return cm
}
func (cm *Docker) All() (containers container.Containers) {
cm.lock.Lock()
for _, c := range cm.containers {
containers = append(containers, c)
}
containers.Sort()
containers.Filter()
cm.lock.Unlock()
return containers
}
func (cm *Docker) Get(id string) (*container.Container, bool) {
cm.lock.Lock()
c, ok := cm.containers[id]
cm.lock.Unlock()
return c, ok
}
В скобках после func
указывается для какой структуры реализуется метод, выглядит очень удобно.
cwidgets — отдельные графические элементы из которых собирается готовый интерфейс.
grid/cursor — пакет который в себе собирает все вместе для вывода на экран и обеспечиваем методы доступа и управления.
Реализации отображения healthcheck оказалась довольна простая. В docker.go
добавили атрибут health
в мета информацию, который читаем через api аналогично функционалу docker inspect <CONTAINER>
. И в зависимости от значения меняем цвет текста в колонке NAME
.
А вот добавление swarm mode получилось более интересным.
В режиме swarm в появляются понятия node
, service
и task
. И между ними необходимо отображаться взаимосвязи, кто где находится. А это в ctop предусмотрено не было. И вот тут действительно почувствовал хорошую поддерживаемость кода. Структура контейнера содержала методы доступа к мета информации и графическому интерфейсу, которые легко выносятся в отдельный интерфейс, что и нужно для новых введенных понятий,. Интерфейс выглядит так:
package entity
import (
"github.com/bcicen/ctop/logging"
"github.com/bcicen/ctop/connector/collector"
"github.com/bcicen/ctop/models"
)
var (
log = logging.Init()
)
type Entity interface {
SetState(s string)
Logs() collector.LogCollector
GetMetaEntity() Meta
GetId() string
GetMetrics() models.Metrics
}
Здесь объявляется общая для всех классов реализующих этот интерфейс переменная log
и описываем контракт интерфейса: метод изменения состояния элемента, доступ к логу конкретного элемента через переменную log
, доступ к мета информации элемента и доступ к его метрикам.
После описания интерфейса, реализации его для всех структр и замены старого container
на новый entity
в методах grid
, всплыл один интересный вопрос. Docker на данный момент не предоставляет возможности получить информацию об использовании ресурсов с удаленной ноды средствами api. Поэтому для себя остановился на реализации вывода структуры swarm кластера и healthcheck запущенных тасков.
Впечатление и заключение
В целом от использования Go в проекте остался очень доволен, язык действительно сделан практичным и лаконичным. Он чем-то напомнил смесь Python и C++, из которых постарались взять все самое лучшее. Выучить его действительно можно за вечер. Но чтобы начать писать на нем за вечер надо иметь достаточный бэкграунд, и советовать его первым языком для изучения я бы не стал. К пониманию его удобства надо наверное придти. Также для себя выделил нишу для языка в виде написания сервисов (он наверное для этого и проектировался).
Что касается ctop, то пулл-реквест с отображением healthcheck майнтенер принял, сейчас планирую закончить отображение структуры swarm. Буду рад предложениям как реализовать считывание метрик контейнеров с удаленных нод.
Комментарии (53)
SirEdvin
29.08.2017 10:21+1(хотя логично, docker на нем написан)
А docker-compose написал на python, хотя причин я не понимаю до сих пор)jarosite
29.08.2017 15:34можно сказать что так исторически сложилось, потому что он основан на исходниках фига (http://www.fig.sh/), который до этого назывался plum.
laughman
29.08.2017 10:43Что лучше, Go или D? В качестве некстгена плюсов? Без гуя и с ним? В треугольнике винда-линукс-андроид?
beduin01
29.08.2017 11:15+2>Что лучше, Go или D?
Вот наглядный пример.calg0n
30.08.2017 11:46-3Как ни странно, слева понятно что происходит, а справа какая-то магия.
DarkEld3r
30.08.2017 13:02+1Ну я вот D не знаю совершенно, а код справа вполне понятен.
calg0n
30.08.2017 14:03-1Вполне, ещё не значит что понятен. Go многое впитал в себя от Си и это видно по коду. D судя по тому что я вижу позаимствовал магию от Ruby и с наскока данный код не будет понятен тем кто с Ruby не сталкивался. Так что тут 2 языка со разными корнями. Но Си с как по мне более прямолинеен и последователен, как собственно и Go.
DarkEld3r
30.08.2017 14:49+1Вот только Ruby я знаю ещё меньше чем D, а код всё равно понятен. (:
Так-то соглашусь, что если у человека есть опыт исключительно С, то да, Go будет очевидно проще. Но как по мне, изучать свой инструмент всё равно надо, а дальше можно бесконечно спорить и кривой обучения и пороге входа.
jacob1237
30.08.2017 15:39+1Не знаю причем тут магия, но то что Вы видите в строке 7, называется Uniform Function Call Syntax, который позволяет писать функции не по-отдельности, а через точку, в таком случае первый аргумент функции будет являться тем объектом, к которому Вы эту функцию присовокупили (иными словами foo(a); это аналог a.foo();): https://tour.dlang.org/tour/ru/gems/uniform-function-call-syntax-ufcs
И эта особенность как раз сильно сокращает время на чтение и понимание кода. А также дает некоторые интересные возможности.
calg0n
30.08.2017 16:10-1Это конечно всё круто, но то что вы описываете и есть особенности языка, в Ruby тоже самое можно сделать насколько я знаю, и периодически сталкиваясь с Ruby, знаю не понаслышке о его магических способностях. Поэтому D и напомнил мне Ruby. По синтаксису Go можно сразу сказать что тут всё прямолинейно как в Си и для тех кто сталкивался с Си (я думаю те кто учился на IT специальностях по любому с ним сталкивались) легко освоит Go ибо там всё также просто и прямолинейно.
Siemargl
29.08.2017 12:26D не умеет Андроид пока и не является промышленным.
Go не для GUI.beduin01
29.08.2017 12:34D умеет Android через LDC «Full Android support, incl. emulated TLS»
Siemargl
29.08.2017 13:28Да, похоже работы в этом направлении к чему то привели — новинка LDC 2017г.
Но выглядит пока не очень. JNI и чистый OpenGL. Ждем GUI-libs
2. Нет, генерация кода html не является GUI
pfihr
29.08.2017 12:46А gui на вэб-формах html/css/js уже не gui? Пример реализации можно увидеть в syncthing.
raydac
29.08.2017 13:16сейчас GUI нередко через веб интерфейс делают, при таком Go справляется не хуже остальных имхо, еще большой плюс что есть приличное количество библиотек для организации консольного интерфейса и даже некоторые аналоги старого доброго Turbo Vision
mrobespierre
29.08.2017 12:30моё мнение такое: Go — язык нишевой, он хорош для создания небольших сервисов (демонов), ориентированных на работу с сетью (в. т.ч. и веб-приложений) и консольных клиентов к ним (или просто утилит). и в этой нише он «лучший» (да, я гофер, да я уже пробовал php, python, ruby и даже perl). в «треугольнике винда-линукс-андроид» лучше D (если вы уверены, что D — некстген C++, я, например, не уверен, и брал бы плюсы, при всех их минусах)
raydac
29.08.2017 13:17а чем он так сильно от Java отличается что на последней можно большое, а на Go нельзя? а то я может что то не заметил
mrobespierre
29.08.2017 16:26можно, можно большое, и я нигде не писал обратного, про java тоже не писал, я лишь отвечал человеку на вопрос
Что лучше, Go или D?
и подчеркнул то, в чём Go, на мой взгляд, хорош
недавно здесь на Хабре была статья про веб-приложение, кажется, на asm, но «можно» != «хорош в чём-то»
farcaller
29.08.2017 15:46-1Но ведь куча крупного серверного софта написана на го?
mrobespierre
29.08.2017 16:19Да, они на Go, да «проекты большие», но если вы таки посмотрите на исходный код, то увидите, что 2 из 3 ваших примеров состоят из 10(!)+ небольших сервисов и клиентов к ним, хотя можно было обойтись и 2-мя. Concource — да, состоит из «всего» 4-х кусков, но это никак не опровергает мой тезис «Go хорош для небольших демонов».
farcaller
29.08.2017 16:232 из 3 это k8s и docker? Docker раньше был одним процессом (плюс commandline клиент), его разделили из-за необходимости масштабирования компонентов независимо друг от друга. Рантайм отдельно, управление отдельно. Благодаря такому разделению в нем теперь есть такая вещь как live restart (перезапуск управляющего демона без перезапуска контейнеров). Звучит как хорошая причина для разделения?
На тему k8s, я хотел бы услышать ваше мнение за счет чего можно сократить количество демонов. Но независимо от их числа, вы прямо так считаете тот же kubelet "небольшим"?
pawlo16
31.08.2017 13:49Большие сервисы тоже можно как показывает практика гигантов индустрии :)
Для не слишком навороченных программ под винду Go вполне себе годится благодаря github.com/lxn/walk — качественная тонкая обёртка для стандартных win api контролов в декларативном стиле.
я бы тоже взял С++, поскольку возится с не поддерживаемыми биндингами нет ни какого желания
apro
29.08.2017 12:45+3В качестве nextget плюсов по-моему пока только Rust выступает. Все-таки у обоих D и Go есть GC. Поэтому если вам не нужна zero cost abstraction, то стоит задуматься может подойдут не компилируемые в native языки, типа Java, C# и т.д.
TargetSan
29.08.2017 14:20Не-компилируемые хотят рантайм. Например, была задачка — собрать тонкий Node.JS сервис из толстой нативной либы и засунуть в докер. Подружить этот зоопарк, чтобы собирался одной кнопкой — нетривиально, хоть и решаемо. А потом корректно проставить всё в контейнер и вычистить мусор. Куда проще скрутить и запаковать монолитный бинарь.
jacob1237
30.08.2017 15:28В качестве "некстгена плюсов" однозначно D, но к сожалению в текущей реализации на 100% плюсы он заменить не может, если конечно не писать без использования сборщика мусора. Хоть это и возможно, но все равно без GC теряются дефолтные фишки языка такие ассоциативные массивы, делегаты и т.д. (в будущем возможно будут какие-то изменения касательно отвязки библиотеки от GC). В принципе и для этих примитивов есть замена без GC, если понадобится.
Сейчас D напоминает скорее нечто среднее между C# и Java, но только не требующее никаких виртуальных машин и рантаймов, т.к. исполняемый файл можно статически слинковать со всеми необходимыми библиотеками.
Для большинства прикладных задач сборщик мусора ничем не мешает и даже очень сильно помогает.
Ну и плюс если хочется совсем низкий уровень или откат к C, есть режим Better-C.
Так что если не пишите какие-то супербыстрые сетевые сервисы, то из этих двух однозначно D (хотя для сетевых сервисов в D есть фреймворк vibe.d)
beduin01
29.08.2017 11:13+5На фоне Go даже Python выглядит более совершенным. По факту в Go простоту довели до примитивизма и похерили все наработки в области разработки языков программирования за последние 50 лет.
В итоге язык больше обрубок какой-то напоминает. Про серьезные проекты на нем читать вообще смешно. Ну да. Запилили на нем REST сервис который JSON отдает. Замечательно! Просто прорыв. А что дальше? Язык не поддерживает элементарные обобщения и обработку данных на нем вести практически невозможно. В итоге кроме как JSON-ом плеваться ничего другого не остается.
SirEdvin
29.08.2017 12:28+4В этом и смысл. Поэтому большинство любителей Golang — автоматически становятся любителями микросервисов, потому что большие монолиты на нем не напишешь. А микросервисы, без всего этого ООП и боли отлично пишутся.
Раньше, если вам нужен был микросервис, у вас был только Python или C++, не очень крутой выбор. А тут теперь и Golang докинули.0xd34df00d
30.08.2017 20:59+1Раньше, если вам нужен был микросервис, у вас был только Python или C++, не очень крутой выбор.
Я микросервисы на хаскеле пишу :(
mrobespierre
29.08.2017 12:39обработку данных на нем вести практически невозможно
а можно примеры, пожалуйста, не могу представить такие данные, что не обработать в Go
кроме как JSON-ом плеваться ничего другого не остается
так сейчас время такое и большинство бизнес-процессов вокруг этого построено, разве нет?beduin01
29.08.2017 12:43Да к примеру простое суммирование превращается в ад. Аж 40 строк. Нафига спрашивается? Со всеми остальными данными тоже самое. Приходится писать простыни. Код ради кода.
>так сейчас время такое и большинство бизнес-процессов вокруг этого построено, разве нет?
Сейчас время BigData и очень часто на сервере нужно что-то посчитать и обработать прежде чем отдать.pfihr
29.08.2017 12:51-1Я могу такой код еще 6 раз скопировать, и он будет еще больше казаться. Сравненип кода абсолютно неверное, нет использования интерфейсов, например.
IncorrecTSW
29.08.2017 12:52+2Пример на скрине честно говоря из пальца высосан. int64sum очевидно покрывает все описанные там кейсы, всю ту простыню можно свести в несколько строк.
ertaquo
29.08.2017 12:52Слева у вас код дублируется аж четыре раза. Без этого ненужного дублирования код сокращается ровно до 10 строк, что не так уж и много.
mrobespierre
29.08.2017 13:36Да к примеру простое суммирование превращается в ад. Аж 40 строк. Нафига спрашивается?
автор того сниппета с сайта dlang очевидно болен, не думаю, что здесь есть что обсуждать
Сейчас время BigData и очень часто на сервере нужно что-то посчитать и обработать прежде чем отдать
вот в этом гошечке просто нет равных ибо идеоматичный код:
1. автоматически масштабируется на любое количество доступных ядер (будь их хоть 2, хоть 64)
2. практически не блокируется
3. в принципе быстро работает: даже при одном потоке Go, в среднем, быстрее Java и NodeJS (популярный нынче выбор для бэкендов) и во много раз (до 10) быстрее Python3.
4. имеет небольшое и прогнозируемое потребление памятиimanushin
29.08.2017 14:21в принципе быстро работает: даже при одном потоке Go, в среднем, быстрее Java и NodeJS (популярный нынче выбор для бэкендов) и во много раз (до 10) быстрее Python3.
Есть сайт для сравнения скорости разных платформ: https://www.techempower.com/benchmarks/#section=data-r14&hw=ph&test=json
Примечательно, что если кто-то считает, что решение неэффективно — он всегда может это исправить.
Итак, судя по этому сравнению, на всех типичных задачах Go медленнее, чем Java или C++ .
Отсюда вопрос: а на каких бенчмарках Python оказался медленнее в 10 раз, чем Go? На каких бенчамарках Java оказалась медленнее, чем Go?
IncorrecTSW
29.08.2017 15:52
calg0n
30.08.2017 11:49+1Упрощение, разделение и микросервисы — наше всё! Для этого го и предназначен.
raydac
29.08.2017 12:54+1как язык Go достаточно банален, никаких новых концепций особо незаметно, но там есть две плюшки из-за которых данная платформа мне импонирует
1. на выходе исполняемый файл не требующий с потребителя «скачать еще сто мегабайт неведомой фигни и проинсталлировать обеспечив что бы уже не стояла неведомая фигня в путях»
2. кросс-компиляция осуществляется без плясок с бубном, всеголишь настройкой пары параметров и под линуксом вполне билдятся версии под винду и мак
mrobespierre
всё это здорово конечно, но вы несколько раз используете слово «класс», которых в Go нет, и смешиваете в кучу packages (суть модули) и interfaces (тип данных, как byte или string), из-за чего я не всё понял
sah4ez32 Автор
Использование словам "класс" — это наследство от java, так как первое знакомство, он местами проскакивает. На самом деле да, тут используются структуры, что непривычно по началу.
SirEdvin
Проблема в том, что в go привычных классов и вместо наследывания предлагают встраивание (например, тут и тут). Это позволяет некоторым, например мне, считать, что Golang больше крутой мейнстримный процедурный язык, который был нужен многим, потому что у них был только C, но подходящий для узкого круга задач.
А отсутствие шаблонных типов превращает работу там, где они нужны в мешанину из interface{}. Хотя вот в go 2.0 вроде они будут.
Emacs232
спасибо, Ваше мнение очень важно для нас