Продолжаем серию статей про особенности, применение, плюсы и минусы языков, которые используются в «Криптоните». В этой статье наш инженер департамента инфраструктуры Алексей Косов расскажет про Golang.
Ранее наши разработчики делали обзоры Rust, Scala, JavaScript и Spark.
1. Особенности
Разработан в Google для решения задач самой компании. Со временем стало ясно, что это не узконаправленный продукт, а язык, имеющий большой потенциал. Поэтому Golang стал доступным для всех, но основным мейнтейнером языка всё ещё остаётся Google.
Go — молодой язык, который появился в 2009 году. По сравнению с Java (1995) или С++ (1983) он прямо ребёнок. Поэтому на Go так мало legacy-проектов.
Отсутствие ООП и классов. В Golang иной подход к объектно-ориентированному программированию. В нём нет классов, но есть методы и структуры; нет наследования, но есть композиция. Go — язык, с помощью которого можно достичь тех же результатов, что и на привычных объектно-ориентированных языках. Только реализуется всё через интерфейсы, структуры и эмбеддинг (embedding — встраивание, аналог наследования).
Параллельное программирование и горутины. В Go не используются тяжёлые потоки операционной системы, а применяются легковесные — горутины (goroutine). У языка есть собственный планировщик, который этими потоками управляет. Горутины упрощают реализацию многопоточных приложений: чтобы создать новый поток, нам нужно всего лишь поставить ключевое слово и написать вызываемую функцию. Для синхронизации и передачи данных между ними используются каналы (channels) и пакет «sync», содержащий в себе, к примеру, мьютексы (mutexes) и WaitGroup (тип, определяющий группу горутин как выполняющихся вместе и позволяющий задать блокировку выполнения функции до завершения всех горутин из группы).
Особенности компиляции (неиспользуемые импорты\переменные). Если мы объявим переменную, но впоследствии нигде её не используем, то в процессе компиляции мы получим ошибку, и приложение не соберётся. То же самое касается неиспользуемых импортов пакетов. За счёт этого уже на этапе разработки (а не рефакторинга) код становится чище. В других языках подход к удалению «мёртвого кода» (DCE) гораздо мягче. Обычно компиляторы просто выдают предупреждения, но позволяют проигнорировать их и скомпилировать код даже с неинициализированными переменными и неиспользуемыми импортами.
2. Где используется
Go — универсальный язык, который используется во многих популярных областях:
backend-разработка;
консольные утилиты;
разработка API;
web-интерфейсы;
инфраструктурные приложения (K8s, Docker, Helm).
Наша команда в «Криптоните» работает как раз с инфраструктурными приложениями. Ежедневно мы используем популярные приложения, которые написаны на Go: Kubernetes, Docker и Helm, в частности — Kubernetes-операторы. При работе с операторами мы можем при необходимости менять какие-то вещи под себя. Допустим, мы скачали проект с GitHub. Нашли баг или, например, по какой-то причине проект не взаимодействует с нашими продуктами так, как надо. Мы можем открыть код, разобраться в нём и поправить его часть так, как нам требуется. Если по каким-то причинам мы не можем поправить код сами, то направляем pull request.
Иными словами, наша команда находит готовые открытые продукты, которые мы встраиваем в нашу платформу. Так как они написаны на Golang, мы можем легко внести изменения. Это относится не только к Kubernetes-операторам, но и к другим приложениям, которые используются у нас на платформе. Например, SeaweedFS и MinIO, которые работают с Big Data.
Ещё мы используем Go-templates — шаблонизатор на языке Go. Например, при работе с Helm создаётся шаблон манифеста. С помощью темплейтов в него подставляются различные переменные, которые хранятся в отдельном values-файле. В итоге получается полноценный манифест. Темплейты — достаточно мощный инструмент. Единственный минус в том, что, по сравнению с Go, их код тяжелее воспринимается. Например, у них свой набор ключевых слов, которые лишь частично пересекаются с теми, что используются в языке. Также можно определить свои функции и использовать их в шаблонах, что не добавляет понимания, если шаблон не твой. Из-за вызова функций без круглых скобок длинные конструкции ещё больше напоминают просто набор слов, в котором сложно найти смысл. Всё это заправляется обилием фигурных скобок вперемешку с обычным текстом.
На прошлой работе у меня был опыт взаимодействия с Go-templates. Я разрабатывал мини-приложение, и мне нужно было написать модуль, который бы взаимодействовал с сетевым оборудованием. А именно: я проводил настройку на коммутаторах/роутерах, конфигурировал и получал с них данные. Для этого я использовал язык Go. Из-за того, что применялись различные модели оборудования, базой для составления набора команд у меня служили как раз Go-templates.
3. Плюсы и минусы
Плюсы
Высокая скорость компиляции и выполнения. Скорость компиляции — одна из самых высоких. Он изначально был заточен, чтобы быстро компилироваться. Скорость выполнения — не самая высокая, но Go держится где-то наверху среди других языков.
Простота языка. Go — небольшой язык (описание стандарта занимает около 100 страниц, в то время как у С++ — примерно полторы тысячи) с простым и понятным синтаксисом. В нём мало ключевых слов, особенностей, сложных конструкций и т.д. Поэтому он достаточно прост в изучении.
Простая реализация многопоточных приложений + лёгкость параллельного программирования. Подробнее я рассказал об этом в пункте про особенности.
Наличие сборщика мусора. Он позволяет эффективно использовать память и снимает некоторые обязанности с разработчика. Нам не нужно как, например, в С++, создавать руками конструкторы, деструкторы, что-то уничтожать и т.д. Общей памятью управляет именно сборщик мусора. Нам нужно только закрывать пулы соединений, а всё остальное почистит он сам. Это удобно, но имеет свои нюансы, о которых я расскажу в минусах.
Обработка ошибок. Этот пункт я тоже отнёс и в плюсы, и в минусы. Мне кажется, что в Golang более осознанная обработка ошибок, чем в других языках. В любом методе или функции, в которых потенциально может произойти ошибка, она (ошибка) должна быть одним из возвращаемых значений. Все ошибки мы должны обрабатывать явным образом: вызвали метод, он что-то вернул, и одним из значений может быть ошибка. После этого мы должны проверить, случилась она там, или нет. Чаще всего это проверяется путём сравнения значения ошибки с nil (нулевое значение для указателя, канала, функции, интерфейса, карты или среза). У нас нет возможности, как в других языках, обернуть всё в блок try, отловить блоком catch и пустить всё на самотёк.
Сборка в один файл. Тут всё просто: когда мы собираем приложение, у нас на выходе один исполняемый файлик. Во многих языках программирования помимо одного файла рядом нужно положить не один десяток библиотек. А там ещё зависимости, версии этих библиотек не те, и это всё нужно где-то хранить, не потерять. В случае же с Golang — просто один файл.
Минусы:
Наличие сборщика мусора. Всё-таки он влечёт за собой дополнительные расходы на потребление ресурсов ЦП и оперативной памяти.
Обработка ошибок. Итак, мы вызвали каждую функцию и получили переменную, в которой должна записаться ошибка. Однако ошибки там может и не быть. Поэтому после каждого вызова мы должны проверить её наличие и, если она есть, определить тип ошибки. Проблема в том, что после каждого вызова функции или метода у нас повторяется один и тот же статичный блок с проверкой. И на одну строчку вызова может быть 3-5 строчек проверки обработки ошибок, и так через строчку. Код превращается в портянку — первое время это сильно режет глаза, но потом привыкаешь. Этот минус скорее не про функциональность, а эстетику.
Неявная реализация интерфейсов. Сами интерфейсы — штука удобная, но только когда ты сам что-то разрабатываешь. Для того, чтобы реализовать интерфейс в Go, нет необходимости использовать ключевое слово, как, например, implements в Java. В Golang любой тип данных, который реализует все методы интерфейса, автоматически реализует сам интерфейс. Мне кажется, что неявная реализация интерфейсов затрудняет понимание отдельных частей кода и процесс реверс-инжиниринга, которым мы как раз занимаемся.
4. Обучение
У Golang низкий порог входа. Изучать его легче, чем Java, но сложнее, чем Python. Go — небольшой молодой язык, который, скорее всего, задумали простым изначально. Например, можно реализовать действие в одну строчку вместо десяти. У Go есть удобная система модулей — можно подключить уже созданные библиотеки и повторно использовать их. При этом они будут на том же языке и с тем же синтаксисом, а не в виде миллиона фреймворков, которые все написаны по-разному, как в JavaScript.
Потребность изучить Golang у меня возникла на работе, так как в проекте поменялся стек. Вместо Java- я стал Go-разработчиком. Так исторически сложилось, что я предпочитаю самообучение. Поэтому я пользовался metanit.com и habr.com, где можно пробежаться по основам. Прочитал книгу «Язык программирования Go» Донована и Кернигана, которая как раз подходит для начинающих. Ещё у Go большое количество библиотек и, поскольку это open source-сообщество, есть куча проектов на GitHub. Можно какой-нибудь из них позаимствовать или поучаствовать в разработке. А если есть вопросы — воспользоваться русскоязычным чатом в Telegram.
По поводу курсов: у меня есть пример из жизни. Знакомый купил годовой курс «Разработчик на Go» популярной образовательной платформы. Целый год они изучали всё, что только можно, но не Golang: базы данных, Docker, Git, сети; успели пройти базу по Python, С++, Java... и лишь в конце немного коснулись нужной темы. Всё, что они успели изучить по Go, можно было пройти за двухнедельный интенсив. Так что, если вы знаете хорошие курсы по Golang, делитесь в комментариях!
5. Pet-проекты
Здесь можно придумать всё, что угодно. Go — это больше про backend, но на нём можно сделать любой проект. Вопрос только в том, насколько это рационально. Например, можно потренироваться на Telegram боте. Он ничем вас не ограничит — не придётся искать сервер для хостинга, чтобы показать свой маленький домашний проект. Нужно лишь посмотреть как пользоваться Telegram API (по этой теме информации полно) и придумать идею. Можно начать с погоды в вашем городе, а потом усложнять до системы, в которой потребуются и базы данных, и подключение по API к каким-нибудь другим системам. Попробуйте двигаться от простого к сложному: развивайте и поддерживайте свой проект. Его демонстрация станет преимуществом на собеседовании. Код скажет о вас больше пачки дипломов.