Список желаний Go 2012

В 2012 году я работал в одной из первых студий мобильных игр и на платформе ngmoco. Моя команда управляла платформой игрового сервера, написанной на Ruby on Rails, и мы приложили огромные усилия к ее масштабированию. Мой босс Дэйв опробовал множество новых технологий, чтобы увидеть, есть ли у них потенциал улучшить нашу ситуацию. Он наткнулся на относительно новый язык Google под названием Go. Команда Go объявила, что они работают над выпуском версии 1.0 в ближайшем будущем. Нас действительно привлекли возможности параллелизма и повышение производительности, которое можно было получить от компилируемого языка. Простой синтаксис и полнофункциональная стандартная библиотека также были отличными бонусами.

Мой первый проект был частью нашего более масштабного перехода на микросервисы. Это был 2011 год, микросервисы были на 100% самодельными, и нам нужно было то, что сегодня назвали бы «шлюзом API». Первоначальный проект должен был проксировать весь наш монолит для выполнения аутентификации (OAuth1 и новый блестящий OAuth2). Я ухватился за этот проект, потому что он звучал весело, и приступил к изучению Go.

Создание рабочего прототипа не заняло много времени. Нам пришлось написать много вещей с нуля, например, наш собственный клиент Redis, библиотеки OAuth1, OAuth2 и JWT и, конечно же, логгер. Мы также написали собственный HTTP-сервер и обратный прокси. Несмотря на все это, проект занял всего несколько месяцев, поскольку стандартная библиотека обладала функциональностью, которая помогла нам пройти большую часть пути. С самого начала Go работал на таком уровне производительности и стабильности, который вселял в нас уверенность, что мы сможем запустить его в производство.

Встреча и список пожеланий

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

Грациозное завершение работы HTTP-сервера

В то время AWS была довольно новой компанией, поэтому у нас был собственный центр обработки данных и аппаратные балансировщики нагрузки. Мы запускали Go сразу за балансировщиками нагрузки, и при развертывании обновлений мы не хотели отбрасывать ни один активный запрос. В Go http.Server не было возможности остановить прослушиватель и дождаться сброса текущих ответов. В итоге мы написали собственный HTTP-сервер, который добавил возможность разветвить/выполнить запущенный процесс и напрямую взять на себя управление работающим прослушивателем, повторно открыв файловый дескриптор. Благодаря этому мы никогда не отказывались от запроса на развертывание.

Брэд в то время занимался сопровождением пакета http и осознавал необходимость этого, но был занят реализацией HTTP/2. Эта функция была добавлена ​​в версию 1.8 в 2017 году с http.Server.Shutdown.

Управление зависимостями

Будучи выходцами из Ruby, мы были пользователями Bundler. Если вы не знакомы с Bundler, вы, вероятно, знакомы с каким-либо другим аналогичным менеджером пакетов. Я думаю, что это был первый широко используемый инструмент, который использовал решатель SAT и вездесущий двухфайловый стиль, где первый файл имеет некоторые правила зависимостей, а второй блокирует зависимости от точных версий.

Я помню, как Брэд был очень озадачен тем, зачем нам это может понадобиться. Это было в те дни, когда все зависимости располагались в одном каталоге GOPATH. Он предложил нам продать весь GOPATH, но мы уже прошли через боль, связанную с поддержкой такой установки Ruby до Bundler. Изначально вендоринг был простым, но у нас возникли огромные проблемы с обновлением вендорных зависимостей после того, как они были отделены от вышестоящих репозиториев git.

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

На протяжении многих лет я использовал подмодули git, Vendoring, Dep, Glide и, наконец, модули Go. Потребовалось некоторое время, чтобы запустить модули go, которые работают очень похоже на Bundler. Модули впервые были выпущены в качестве эксперимента в go1.11 в 2018 году и включены по умолчанию в go1.16 в 2021 году. Модули — это именно то решение, о котором я просил еще в 2012 году.

Регистрация уровня

Пакет журналов, включающий уровни, был последним запросом, который мы сделали на собрании. Начиная с версии go 1.21, выпущенной в 2023 году, стандартная библиотека включает пакет slog, который имеет уровни, а также является структурированным регистратором. Четыре включенных уровня (Отладка, Информация, Предупреждение и Ошибка) соответствуют именно тем уровням, которые я использую на практике.

Еще в 2012 году мы сделали то, что тогда делали почти все, и написали собственный пакет журналирования. Причудливые системы агрегаторов журналов, которые мы имеем сегодня, тогда только появлялись на сцене, но я уверен, что у Google уже были инструменты мониторинга журналов, которые работали с log.Printf/ln стандартной библиотеки.

Хотя всегда существовало множество сторонних средств журналирования с поддержкой уровня, проблема в том, что их слишком много. Зачастую сложным библиотекам необходимо вести собственное журналирование, а отсутствие «стандартного» интерфейса означало, что интеграция этих библиотек в более крупную систему всегда была затруднена. Часто каждый пакет должен предоставлять некоторый интерфейс прокладки, чтобы их внутреннее ведение журналов можно было сделать совместимым с logrus, zap, нулевым журналом и т. д. Хуже того, некоторые проекты просто встраивают свой любимый регистратор. Заставить их всех работать вместе в одном приложении часто невозможно, и в итоге вы получаете беспорядок из разных форматов журналирования и отсутствующего контекста. Теперь, когда есть стандартный регистратор библиотек, я надеюсь, что проекты быстро перейдут на этот интерфейс, и эта проблема останется в зеркале заднего вида.

Иногда желания сбываются

Команда Go постоянно работает над удовлетворением потребностей сообщества в стандартной библиотеке. Если вы посмотрите на прогресс, бесспорные улучшения библиотеки HTTP были реализованы относительно быстро. У управления зависимостями было несколько фальстартов, и потребовалась огромная работа в течение нескольких лет, чтобы наконец прийти к превосходным модулям системы Go, которые есть сегодня. Логгирование уровней, похоже, не должно было быть самой сложной проблемой для решения. Команда Go, похоже, откладывает некоторые решения на рассмотрение сообщества, когда не существует общепринятого стандарта. Я помню, как Брэд спросил: «Какие уровни будет поддерживать стандартная библиотека?» Простой вопрос, но на него сложно ответить без исследований и большого вклада сообщества. Ни того, ни другого Go не было разработано на стадии 1.0. Хотя может показаться, что 11 лет — это долгий срок ожидания, все мои желания в конечном итоге сбылись.