Go, go, go… Первые впечатления

    Вечер буднего дня, как же не заняться написанием статьи-заметки. В которой хочу поделиться впечатлениями о знакомстве с 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. Буду рад предложениям как реализовать считывание метрик контейнеров с удаленных нод.

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

    Ваше мнение о Go

    • 31.7%Пишу, очень доволен196
    • 8.2%Пишу, не очень доволен51
    • 41.4%Не писал, но хочу начать256
    • 18.4%Не писал и не собираюсь114
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 55

      +6
      всё это здорово конечно, но вы несколько раз используете слово «класс», которых в Go нет, и смешиваете в кучу packages (суть модули) и interfaces (тип данных, как byte или string), из-за чего я не всё понял
        –3

        Использование словам "класс" — это наследство от java, так как первое знакомство, он местами проскакивает. На самом деле да, тут используются структуры, что непривычно по началу.

          +1

          Проблема в том, что в go привычных классов и вместо наследывания предлагают встраивание (например, тут и тут). Это позволяет некоторым, например мне, считать, что Golang больше крутой мейнстримный процедурный язык, который был нужен многим, потому что у них был только C, но подходящий для узкого круга задач.


          А отсутствие шаблонных типов превращает работу там, где они нужны в мешанину из interface{}. Хотя вот в go 2.0 вроде они будут.

            –3
            спасибо, Ваше мнение очень важно для нас
        0

        del

          +1
          (хотя логично, docker на нем написан)
          А docker-compose написал на python, хотя причин я не понимаю до сих пор)
            0

            Собственно первая надежда что ctop на python оттуда и зарадилась.

              0
              можно сказать что так исторически сложилось, потому что он основан на исходниках фига (http://www.fig.sh/), который до этого назывался plum.
              0
              Что лучше, Go или D? В качестве некстгена плюсов? Без гуя и с ним? В треугольнике винда-линукс-андроид?
                +2
                В треугольнике винда-линукс-андроид?

                Java. Вы, конечно, можете попробовать делать это на Go, но я бы не советовал. И они пока тоже.

                  0
                  Или можно еще попробовать Kotlin.
                  +2
                  >Что лучше, Go или D?
                  Вот наглядный пример.
                    –3
                    Как ни странно, слева понятно что происходит, а справа какая-то магия.
                      +1

                      Ну я вот D не знаю совершенно, а код справа вполне понятен.

                        –1
                        Вполне, ещё не значит что понятен. Go многое впитал в себя от Си и это видно по коду. D судя по тому что я вижу позаимствовал магию от Ruby и с наскока данный код не будет понятен тем кто с Ruby не сталкивался. Так что тут 2 языка со разными корнями. Но Си с как по мне более прямолинеен и последователен, как собственно и Go.
                          +1

                          Вот только Ruby я знаю ещё меньше чем D, а код всё равно понятен. (:


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

                            +1

                            Не знаю причем тут магия, но то что Вы видите в строке 7, называется Uniform Function Call Syntax, который позволяет писать функции не по-отдельности, а через точку, в таком случае первый аргумент функции будет являться тем объектом, к которому Вы эту функцию присовокупили (иными словами foo(a); это аналог a.foo();): https://tour.dlang.org/tour/ru/gems/uniform-function-call-syntax-ufcs


                            И эта особенность как раз сильно сокращает время на чтение и понимание кода. А также дает некоторые интересные возможности.

                              –1
                              Это конечно всё круто, но то что вы описываете и есть особенности языка, в Ruby тоже самое можно сделать насколько я знаю, и периодически сталкиваясь с Ruby, знаю не понаслышке о его магических способностях. Поэтому D и напомнил мне Ruby. По синтаксису Go можно сразу сказать что тут всё прямолинейно как в Си и для тех кто сталкивался с Си (я думаю те кто учился на IT специальностях по любому с ним сталкивались) легко освоит Go ибо там всё также просто и прямолинейно.
                                –1

                                Хочу заметить, что даже для тех кто сталкивался с Java и Python, и не сталкивался с Си, Go показался достаточно дружелюбным по дизайну.

                                  –1
                                  Согласен :)
                              –1
                              По-моему D по своему синтаксису очень сильно похож скорее на C#, поэтому для людей которые знакомы с C# этот язык придется очень по душе и переход может быть очень легок. Но если сравнивать Go и D, как мне кажется D хорош, но относительно не популярен, литературы не так уж и много, полноценной IDE нет (понятно что кодить можно и в блокноте, но новичку на стадии изучения гораздо удобнее пользоваться подсказами самой IDE, а Go в свою очередь прост в понимании, удобен, есть прекрасная IDE Gogland от Jetbrains и в целом всё работает «из коробки», установил — и все твое время направлено на реализацию идеи (в D приходилось «шаманить», и даже сталкиваться с трудностями, например статической линковкой 32-х битных сторонних библиотек под Windows, для новичка это порой трудно и может напрочь отпугнуть). Я бы советовал смотреть в сторону Go, хотя можете на своем опыте проверить оба варианта.
                        0
                        D не умеет Андроид пока и не является промышленным.
                        Go не для GUI.
                          0
                          D умеет Android через LDC «Full Android support, incl. emulated TLS»
                            0
                            Да, похоже работы в этом направлении к чему то привели — новинка LDC 2017г.
                            Но выглядит пока не очень. JNI и чистый OpenGL. Ждем GUI-libs

                            2. Нет, генерация кода html не является GUI
                            0

                            А gui на вэб-формах html/css/js уже не gui? Пример реализации можно увидеть в syncthing.

                              0
                              сейчас GUI нередко через веб интерфейс делают, при таком Go справляется не хуже остальных имхо, еще большой плюс что есть приличное количество библиотек для организации консольного интерфейса и даже некоторые аналоги старого доброго Turbo Vision
                              0
                              моё мнение такое: Go — язык нишевой, он хорош для создания небольших сервисов (демонов), ориентированных на работу с сетью (в. т.ч. и веб-приложений) и консольных клиентов к ним (или просто утилит). и в этой нише он «лучший» (да, я гофер, да я уже пробовал php, python, ruby и даже perl). в «треугольнике винда-линукс-андроид» лучше D (если вы уверены, что D — некстген C++, я, например, не уверен, и брал бы плюсы, при всех их минусах)
                                0
                                а чем он так сильно от Java отличается что на последней можно большое, а на Go нельзя? а то я может что то не заметил
                                  0
                                  можно, можно большое, и я нигде не писал обратного, про java тоже не писал, я лишь отвечал человеку на вопрос
                                  Что лучше, Go или D?

                                  и подчеркнул то, в чём Go, на мой взгляд, хорош

                                  недавно здесь на Хабре была статья про веб-приложение, кажется, на asm, но «можно» != «хорош в чём-то»
                                  –1

                                  Но ведь куча крупного серверного софта написана на го?

                                    0
                                    Да, они на Go, да «проекты большие», но если вы таки посмотрите на исходный код, то увидите, что 2 из 3 ваших примеров состоят из 10(!)+ небольших сервисов и клиентов к ним, хотя можно было обойтись и 2-мя. Concource — да, состоит из «всего» 4-х кусков, но это никак не опровергает мой тезис «Go хорош для небольших демонов».
                                      0

                                      2 из 3 это k8s и docker? Docker раньше был одним процессом (плюс commandline клиент), его разделили из-за необходимости масштабирования компонентов независимо друг от друга. Рантайм отдельно, управление отдельно. Благодаря такому разделению в нем теперь есть такая вещь как live restart (перезапуск управляющего демона без перезапуска контейнеров). Звучит как хорошая причина для разделения?


                                      На тему k8s, я хотел бы услышать ваше мнение за счет чего можно сократить количество демонов. Но независимо от их числа, вы прямо так считаете тот же kubelet "небольшим"?

                                    0
                                    Большие сервисы тоже можно как показывает практика гигантов индустрии :)

                                    Для не слишком навороченных программ под винду Go вполне себе годится благодаря github.com/lxn/walk — качественная тонкая обёртка для стандартных win api контролов в декларативном стиле.

                                    я бы тоже взял С++, поскольку возится с не поддерживаемыми биндингами нет ни какого желания
                                    +3

                                    В качестве nextget плюсов по-моему пока только Rust выступает. Все-таки у обоих D и Go есть GC. Поэтому если вам не нужна zero cost abstraction, то стоит задуматься может подойдут не компилируемые в native языки, типа Java, C# и т.д.

                                      0

                                      Не-компилируемые хотят рантайм. Например, была задачка — собрать тонкий Node.JS сервис из толстой нативной либы и засунуть в докер. Подружить этот зоопарк, чтобы собирался одной кнопкой — нетривиально, хоть и решаемо. А потом корректно проставить всё в контейнер и вычистить мусор. Куда проще скрутить и запаковать монолитный бинарь.

                                      0

                                      В качестве "некстгена плюсов" однозначно D, но к сожалению в текущей реализации на 100% плюсы он заменить не может, если конечно не писать без использования сборщика мусора. Хоть это и возможно, но все равно без GC теряются дефолтные фишки языка такие ассоциативные массивы, делегаты и т.д. (в будущем возможно будут какие-то изменения касательно отвязки библиотеки от GC). В принципе и для этих примитивов есть замена без GC, если понадобится.


                                      Сейчас D напоминает скорее нечто среднее между C# и Java, но только не требующее никаких виртуальных машин и рантаймов, т.к. исполняемый файл можно статически слинковать со всеми необходимыми библиотеками.


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


                                      Так что если не пишите какие-то супербыстрые сетевые сервисы, то из этих двух однозначно D (хотя для сетевых сервисов в D есть фреймворк vibe.d)

                                      +5
                                      На фоне Go даже Python выглядит более совершенным. По факту в Go простоту довели до примитивизма и похерили все наработки в области разработки языков программирования за последние 50 лет.

                                      В итоге язык больше обрубок какой-то напоминает. Про серьезные проекты на нем читать вообще смешно. Ну да. Запилили на нем REST сервис который JSON отдает. Замечательно! Просто прорыв. А что дальше? Язык не поддерживает элементарные обобщения и обработку данных на нем вести практически невозможно. В итоге кроме как JSON-ом плеваться ничего другого не остается.

                                        +4

                                        В этом и смысл. Поэтому большинство любителей Golang — автоматически становятся любителями микросервисов, потому что большие монолиты на нем не напишешь. А микросервисы, без всего этого ООП и боли отлично пишутся.
                                        Раньше, если вам нужен был микросервис, у вас был только Python или C++, не очень крутой выбор. А тут теперь и Golang докинули.

                                          +1
                                          Раньше, если вам нужен был микросервис, у вас был только Python или C++, не очень крутой выбор.

                                          Я микросервисы на хаскеле пишу :(

                                          0
                                          обработку данных на нем вести практически невозможно

                                          а можно примеры, пожалуйста, не могу представить такие данные, что не обработать в Go
                                          кроме как JSON-ом плеваться ничего другого не остается

                                          так сейчас время такое и большинство бизнес-процессов вокруг этого построено, разве нет?
                                            0
                                            Да к примеру простое суммирование превращается в ад. Аж 40 строк. Нафига спрашивается? Со всеми остальными данными тоже самое. Приходится писать простыни. Код ради кода.

                                            >так сейчас время такое и большинство бизнес-процессов вокруг этого построено, разве нет?
                                            Сейчас время BigData и очень часто на сервере нужно что-то посчитать и обработать прежде чем отдать.
                                              –1

                                              Я могу такой код еще 6 раз скопировать, и он будет еще больше казаться. Сравненип кода абсолютно неверное, нет использования интерфейсов, например.

                                                +1

                                                А как это будет выглядеть с интерфейсами?

                                                +2
                                                Пример на скрине честно говоря из пальца высосан. int64sum очевидно покрывает все описанные там кейсы, всю ту простыню можно свести в несколько строк.
                                                  0

                                                  Слева у вас код дублируется аж четыре раза. Без этого ненужного дублирования код сокращается ровно до 10 строк, что не так уж и много.

                                                    –3
                                                    x:=0
                                                    for _,y:=range []int{1,2,3,4,5} {x+=y}
                                                    fnt.Println(x)

                                                    В одну строку
                                                      +3
                                                      И для каждого отдельного типа придется писать отдельную функцию.
                                                        –3

                                                        нет, интерфейсы и рефлексию никто не отменял

                                                          +4

                                                          Не отменял. Но это то же, что было в Java до 1.5.

                                                      0
                                                      Да к примеру простое суммирование превращается в ад. Аж 40 строк. Нафига спрашивается?

                                                      автор того сниппета с сайта dlang очевидно болен, не думаю, что здесь есть что обсуждать
                                                      Сейчас время BigData и очень часто на сервере нужно что-то посчитать и обработать прежде чем отдать

                                                      вот в этом гошечке просто нет равных ибо идеоматичный код:
                                                      1. автоматически масштабируется на любое количество доступных ядер (будь их хоть 2, хоть 64)
                                                      2. практически не блокируется
                                                      3. в принципе быстро работает: даже при одном потоке Go, в среднем, быстрее Java и NodeJS (популярный нынче выбор для бэкендов) и во много раз (до 10) быстрее Python3.
                                                      4. имеет небольшое и прогнозируемое потребление памяти
                                                        0
                                                        в принципе быстро работает: даже при одном потоке 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?

                                                      –2
                                                      Сейчас время BigData и очень часто на сервере нужно что-то посчитать и обработать прежде чем отдать.

                                                      в бигдате есть явно разделённые этапы, типично это
                                                      1. Выделение и преобразование данных
                                                      2. Скармливание данных в хранилище (Clickhouse, Vertica, Prometheus, очередной уродец из Java-стэка для любителей вёдер с болтами и т.п.)
                                                      3. Обработка данных средствами хранилища (какие-нибудь маркетологическеие метрики в Кликхаусе-Вертике, метрики для мониторинга в Прометеусе и т.п.)


                                                      Для выделения и преобразования данных дженерики не нужны. Для скармливания тоже. Обработка — тот же самый прометеус написан на Go. Дженерики могут быть как-то полезны только в простейших случаях.
                                                    +1
                                                    Упрощение, разделение и микросервисы — наше всё! Для этого го и предназначен.
                                                    +1
                                                    как язык Go достаточно банален, никаких новых концепций особо незаметно, но там есть две плюшки из-за которых данная платформа мне импонирует
                                                    1. на выходе исполняемый файл не требующий с потребителя «скачать еще сто мегабайт неведомой фигни и проинсталлировать обеспечив что бы уже не стояла неведомая фигня в путях»
                                                    2. кросс-компиляция осуществляется без плясок с бубном, всеголишь настройкой пары параметров и под линуксом вполне билдятся версии под винду и мак

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

                                                    Самое читаемое