Мое первое взаимодействие с Go

Несколько месяцев назад я решил выучить новый язык программирования. Я уже знаком с C#, JavaScript (браузер и Node.JS) и SQL. После нескольких часов гугления я решил изучить Go. В этой статье я хочу описать свой первый микросервис, написанный на Go, какие библиотеки я использовал, с какими проблемами столкнулся и многое другое.

Введение

Если вы не знакомы с Golang, вот краткий обзор того, что это такое.

Go — это язык программирования с открытым исходным кодом, поддерживаемый Google. Это легко изучить и начать работу. Он имеет растущую экосистему партнеров, сообществ и инструментов.

Крупнейшими компаниями, использующими Go, являются Google, Netflix, Dropbox, Uber, Meta, Twitch и т. д. С помощью Go вы можете создавать облачные и сетевые службы, веб-приложения и интерфейсы командной строки.

Прежде чем начать, я попытался найти преимущества использования Go, и вот они:

  • им действительно легко пользоваться. Доступно только 25 ключевых слов;
  • это быстро, быстрее, чем C# или JavaScript;
  • он статически типизирован. Вы можете проверять типы во время компиляции;
  • это молодой язык, моложе C#, Python, Java, JavaScript;
  • он имеет большую поддержку сообщества. Вы можете найти все, что вам нужно.

На данный момент я не могу выделить какие-либо недостатки, потому что все, за что критикуют Golang, сделано намеренно. Например, обработка ошибок значительно отличается от C#, где для обработки исключений необходимо указать блок try/catch. В Go вы возвращаете ошибку как результат функции в качестве последнего параметра, и если он не отсутствует — ошибка произошла. Но это заставляет вас использовать ошибки, даже если вы этого не хотите (если вы используете возвращаемые параметры — вы должны присвоить их все переменным или явно игнорировать).

До Go 1.18 он не поддерживал дженерики, но теперь вы можете использовать их, если они вам нужны. Тем не менее, Go не поддерживает параметры по умолчанию и перегрузку функций. Это также было сделано намеренно, чтобы упростить чтение кода и понимание стека вызовов. Так что мне трудно найти какие-то подводные камни в Go.

Как я научился Го?

Когда я знакомлюсь с новыми технологиями и мне это интересно, я пытаюсь найти книгу, которую я могу использовать для обучения. На этот раз я купил книгу Джона Боднера «Learning Go: An Idiomatic Approach to Real World Go Programming».

Вы можете найти его на Amazon по этой ссылке. Это отличная книга, в которой подробно описываются основные функции Golang, она помогает настроить локальную среду для разработки Go и показывает лучшие практики и шаблоны использования Golang.

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

Самая первая идея Go Microservice

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

Я создал для него следующие функциональные требования:

  • он должен масштабироваться как по горизонтали, так и по вертикали;
  • он должен быть надежным и устойчивым к любому сбою;
  • данные должны быть постоянными;
  • у него должны быть журналы, чтобы иметь возможность отслеживать HTTP-запросы;
  • у него должны быть метрики для понимания нагрузки, задержки и других критических показателей;
  • он должен тестироваться с помощью автотестов;
  • он должен шифровать конфиденциальные данные (пароль);
  • КИ должен присутствовать;

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

Инфраструктура

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

Я не буду описывать весь стек преимуществ, который я использовал, потому что это длинная история. Я дам небольшое представление о каждом инструменте.

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

Для сбора журналов я использовал стек ELK, что означает Elasticsearch, Logstash и Kibana. Также для сбора логов из файла мне понадобился Filebeat. Это отличное решение для агрегирования, сбора, хранения и визуализации журналов ваших приложений.

Я использовал Prometheus для сбора метрик и Grafana для визуализации данных, собранных из приложения.

Для балансировки входящего трафика я использовал HAProxy, потому что он прост в использовании и настройке.

Я использовал HashiCorp Consul для обнаружения сервисов, потому что он помогает отслеживать работоспособность вашего приложения, знать, где оно размещено, и многое другое.

Для создания необходимой мне инфраструктуры я использовал Docker и Docker Compose, чтобы иметь возможность хранить инфраструктуру как код (IaaS) и быстро создавать все, что мне нужно для локальной разработки.

Для целей CI я использовал GitHub Actions для создания и тестирования кода, который я зафиксировал.

Идем дальше!

Библиотеки и фреймворки на стороне приложения

Для обработки HTTP-запросов я решил использовать фреймворк Gin. Он имеет отличную документацию, прост в использовании и быстр. Что еще мне нужно для моего веб-приложения? Ничего!

Для ведения журнала я использовал Zap, разработанный Uber, который помогает вам собирать структурированные журналы, которые затем можно индексировать в Elasticsearch.

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

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

Код приложения

Весь процесс разработки описывать не буду (код можно найти по ссылке ниже). Я лучше опишу самые большие проблемы, с которыми я столкнулся во время внедрения:



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

Покажу один из тестов, генерирующих ссылку из пароля:

Здесь я создал все зависимости, необходимые для создания ссылки: база данных (для теста я использовал SQLite), шифратор (шифрование/дешифрование тестировалось в другом файле), регистратор (я создал регистратор только для тестов). ), и так далее. Я решил использовать заглушки вместо макетов для реализации нужного мне поведения, но в этом случае единственная заглушка, которую я использовал, — это регистратор.

Второй проблемой, с которой я столкнулся, было внедрение зависимостей. Если вы знакомы с C#, то знаете, что DI — это встроенная функция платформы .NET. Но в Go используется другой подход — вы создаете все зависимости в основном методе фабричными методами и передаете их в другой экземпляр. Это легко, но для меня это то, к чему я должен был привыкнуть.

Вот одна из зависимостей:

Здесь я только что представил общедоступный интерфейс Encoder, реализованный в структуре encoder, и метод структуры NewEncoder, который возвращает экземпляр, реализующий текущий интерфейс.

В Go интерфейсы реализуются неявно путем реализации интерфейсных методов. Было непросто понять после C#, если вы понимаете, о чем я, но мне понравилось.

Вы можете найти пример основного пакета ниже:

Наконец, это было не так сложно, но мне нужно было время, чтобы понять это после перехода на новый язык.

Заключение

Мое первое взаимодействие с Golang было удовольствием. Язык был разработан с минимальным количеством функций, но достаточным для создания современных приложений. Он также имеет отличную поддержку сообщества, поэтому вы всегда можете найти поддержку для ваших проблем. Первые шаги были большим вызовом, но захватывающим, поэтому я продолжу изучать Go и попытаюсь реализовать с его помощью другие вещи.

Ресурсы