Типичная проблема, возникающая при разработке распределенных систем, состоит в следующем. Допустим, пользователь посылает системе запрос, и запрос этот обрабатывался очень долго. При этом внутри системы запрос распался на запросы к нескольким внутренним микросервисам, которые в свою очередь могли также превратиться в несколько подзапросов, возможно, даже выполняемых параллельно. Как в такой ситуации выяснить, где тормозит система? Метрики не дают ответа на данный вопрос, поскольку в целом вся система работает нормально, затупил только отдельный запрос конкретного пользователя. Логи могут дать ответ, но они разбросаны по множеству машин, время на которых не синхронизировано. На помощь приходит Jaeger.
Badger — это реализация LSM tree на языке Go. Не будет преувеличением сказать, что это как RocksDB, только написанный с нуля на другом языке программирования. Библиотека основана на WiscKey paper [PDF], обмазана кучей всевозможных тестов, неплохо показывает себя на бенчмарках, ну и в целом производит впечатление серьезного проекта. Мне захотелось познакомиться с библиотекой поближе. Поэтому я написал на ней простенькую key-value СУБД с REST-интерфейсом.
В рамках поста Устанавливаем связку из Prometheus и Grafana мы познакомились с Prometheus и разобрались с его настройкой. Теперь давайте выясним, как отправить в него каких-нибудь метрик из нашего собственного приложения. Писать будем на языке Go, но я почти уверен, что для других языков существуют аналогичные библиотеки.
На первый взгляд, модульные тесты в Go пишутся очень просто. Создаем файл с именем пакет_test.go
, в нем объявляем функции с именами TestЧтоТестируем
, говорим go test
, ну и считай готово. Однако на деле все оказывается чуточку сложнее. Например, оказывается, что из коробки в языке нет ни ассертов, ни моков. А когда ты начинаешь генерировать моки, они внезапно начинают участвовать в подсчете покрытия кода тестами. В общем, давайте разберемся, как все устроено на самом деле.
В прошлых постах вы могли прочитать о том, как сериализовать объекты в языке C++, используя формат Protobuf, а также в языке Scala, используя Thrift. Была рассмотрена даже такая эзотерика, как формат MessagePack и работа с ним на языке Haskell. Давайте же теперь выясним, как делается сериализация в языке Go. Для этого мы воспользуемся форматом CBOR и библиотекой codec.
При разработке веб-приложений и всяких там бэкендов иногда возникает необходимость запустить кусок кода ровно на одном сервере, а не на всех. Типичный случай — это когда вам нужно запустить фоновую миграцию схемы базы данных. Однако бывают и другие сценарии, некоторые из которых упоминались в заметке Пример использования акторов-одиночек в Akka. Задача выбора одного «главного» сервера из N довольно просто решается при помощи подхода под названием leader lease, речь о котором и пойдет далее.
Я потихоньку слежу за развитием языка Go и в последнее время он начинает нравится мне все больше и больше. Недавно дошли руки написать небольшую программку, генерирующую шоуноты к подкасту по списку ссылок. Ну и, поскольку это все-таки Go, программка была сделана многопоточной.
В этой заметке я опишу реализацию простого REST API для телефонной книги, созданной в прошлый раз. Должен сразу оговориться, что интерфейс к базе данных был изменен. Во-первых, прошлая реализация была направлена скорее на демонстрацию возможностей пакета database/sql, чем на решение задачи, а во-вторых, она была подвержена SQL-инъекциям.
Пожалуй, основной областью применения Go на сегодня является написание серверных приложений. А их трудно представить в отрыве от какой-нибудь базы данных. В этой заметке я опишу создание простой телефонной книги с интерфейсом командной строки и PostgeSQL в качестве хранилища.
Рантайм Go содержит встроенный профайлер, но по умолчанию он выключен. Существует несколько способов его эксплуатации, самый «низкоуровневый» — через библиотеку runtime/pprof. Russ Cox, один из главных разработчиков Go, разместил хорошую статью по ее использованию. Однако я бы порекомендовал пакет-обертку profile, он чуть проще в настройке.