суббота, 28 декабря 2013 г.

Субботний вечер пропаганды маргинальных технологий: Go! Go! Go!

Практически сразу после предыдущего поста хотел написать про Go. За ним я следил с самого его объявление, всё таки делает его небезызвестная "Корпорация Добра", но в этом году несколько раз использовал и много какие проекты на нём плотно изучал. Хотел разобраться сильнее, чтобы более объективно/полно описать и поэтому откладывал. Но всё таки пришёл к тому, что основным, хотя даже и в топ 5 моих языков он не войдёт. Будет вспомогательным. Для чего, чуть ниже, но вначале небольшой дисклаймер. На Go суммарно я написал наверно максимум пару сотен банальных строк кода, поэтому этот пост будет очень небольшой и чтобы его дополнить расскажу про интересные проекты написанные на нём (не много будет пересекаться с предыдущим постом), так что профи - проходите мимо, но может заинтересует тех кто об этом языке не знал.

Начнём с истории о моих первых строчках кода на Go. Была банальная админская задача - собирать Nginx логи в MongoDB. Прошаренный одмин конечно возьмет готовую утилиту, например описанный в предыдущей статье - logstash, но как заметил Дима, которому сейчас приходится разбираться с моим творчеством - я любитель велосипедов. Так что хорошо что пришёл Дима, думаю он приведёт это всё к нормальному виду. Так вот, я решил написать скрипт, который это будет делать. Так как мы используем Puppet, то первым выбором стал Ruby. Но после полудня безуспешных попыток завести их драйвер для MongoDb - я сдался. И не надо только мне про этот кусок говна именуемый RVM. Он тоже добавляет несчастья, но в этом случае даже он не помог, так как тут проблема с бинарными зависимостями. Это вообще огромное проклятье как Ruby, так и няшного Python - нативные расширения. Но у Python, не смотря на всю его няшность, ещё надо разобраться между второй и третьей версией. Я был уже готов взяться за Java, ну или Groovy там какой. Но за это время я заразился всё таки одминством, поэтому не очень люблю ставить лишние пакеты на сервер. Так что хотел бы просто скриптик, один файл. И тут я вспомнил про Go. Он компилирует всё в один бинарник, включая виртуальную машину и все зависимости. В результате 3mb файл, который puppet с лёгкостью копирует его на все сервера и запускает. Работает прекрасно, даже когда перешли с Ubuntu 10.04 на 12.04 всё продолжало работать. Помню в студенческие времена, когда я ещё интересовался С++, были сильные баталии насчёт динамической и статической линковки, но для меня статическая линковка стала главным достоинством Go. Потом мне как-то попалась статья Go Replace! где с помощью Go, как не трудно догадаться, была написана маленькая утилитка для замены текста в файлах. Так что отличное для него применение - это вот такие вот маленькие вспомогательные утилиты, они не требуют зависимостей, работают достаточно быстро, просто счастье.

Это только один пример, который был очень близок к тому как я использую Go. Что же ещё есть клёвого в этом языке кроме статической линковки. Я бы назвал Go - современным C. У него очень простой и понятный С подобный синтаксис, но более современный. С рекордами, аннонимными функциями, но без излишеств типа монад и прочих умопорачений, как например Scala. Управлять памятью тоже самому не надо, но при этом он очень быстрый. В интернетах говорят что где-то около того же С, хотя кто им поверит. Я не проверял, но Nginx лог он парсит и загоняет в монгу очень быстро. Так что удобный, простой синтаксис и отсутствие ручного управления памятью тоже сильно повлияло на мой выбор его для утилит. Но не только мой выбор, но и другим он тоже нравится, поэтому для него предостаточно библиотек. Для MongoDB я легко нашёл удобную и работающую. Управления зависимостями/пакетами у него работает из коробки, но очень интересно. Нет репозиториев типа как у maven, или gems, а он берёт прямо сорцы с систем контроля версий. Драйвер для монги я ссылкой с github устанавливал. Есть мнение, что это как минимум странно. Но я так не считаю. Что gem, что pip они тоже уже умеют работать с [D]VCS. Тем более всегда можно сделать форк нужного проекта, с нужного места, даже что-то изменить и брать его со своего такого репозитория. Саму зависимость, как я выше говорил, он упаковывает вместе с вашим кодом в один бинарник. Так что на конечной машине действительно ничего не надо ставить, просто берёшь этот файл и он just works. Синтаксис конечно простой, но бывают приколы, например, когда в функцию с переменным числом аргументов надо передать массив, что в других языках нормально, тут приходится "приводить", что ли, вот так: fun(args...). Вообщем компилятор у него не просто статический, а очень строгий. Вы сразу на это наткнётесь. Например если у вас есть неиспользуемый импорт или локальная переменная - в большинстве других языков у вас будет просто предупреждение, причём скорее всего только в IDE или каком-нибудь style checker, то тут вас компилятор сразу огреет. Подозреваю можно отключить, но я не задавался такой целью. Это даже вполне правильно. Да и что отдельно удобно, это go run test.go, так можно запустить в работу ваш код, без компиляции, что круто, когда ты только его пишешь.

Отдельного упоминания стоят ещё две его концепции. Работа с ошибками у них сделана очень интересно и нестандартно для мейнстримных языков. Так как функции у них могут возвращать не только одно значение то многие возвращают ещё и ошибку. Но гораздо интереснее Defer, Panic и recover. Пересказывать не буду. Но мне очень нравится именование, это вам не throw new ex, а panic! Ну и конечно их модель concurrency основанная на их goroutines (зелёных потоках). Тут я тоже не большой дока, пересказывать не буду, хотя вот жаль что Андрей не выложил (или я пропустил) свои слайды с внутренней конференции, где он прекрасно кратко рассказал про это, так что вам порекомендую три презентации Rob Pike - 'Concurrency Is Not Parallelism', Rob Pike - Go Concurrency Patterns (2012) и свежак уже не от Пайка Advanced Go Concurrency Patterns. Правда Erlang программисты Go загнобят, он не может тягаться, нет ни дерева супервизоров, ни распределённых рутинов и о боже мой - shared memory. Я видел разные проекты которые пытаются это покрыть, но что-то загибаются они. Самый интересный это от Максима Трескина Go Erlang, но там уже скоро как год нет комитов, так что он наверно он его бросил (доклад кстати у него тоже был интересный Взаимодействие Go и C-библиотек. Go и Erlang). Я использую рутины сейчас, но по мелочи. Использовать их очень легко, этим и подкупает go fun() и всё. Сейчас делаем нагрузочное тестирование и я запускалку, которая запускает, извините за каламбур, нужное количество определённых скриптов эмулирующих действия пользователей на Go написал, каждый в отдельном рутине, вполне нормально работает. Раз уж упомянул эту задачу, ещё про две штуки мельком, так как они не про Go. DigitalOcean - прекрасен, да он не замена амазону, но когда нужны временные машинки - самое оно, там мы и запускаемся. Создать и поднять новую машину с нашего образа - 1 минута в среднем (но бывают и затыки на 5 минут). И очень дёшево, для тестов бешено рекомендую. И ещё mono - проект пытающийся повторить .net, только на нормальных ОС. Я к нему специфически относился, когда на одном интервью не узнал про Хamarin - это такая платформа на базе mono, когда пишешь на C#, а оно тебе делает приложения для iOS и Android и это даже работает, как и сказал видел живых людей что на ней успешно делают игры. Но больше я зауважал mono когда в текущей задаче припёрло. Скрипты, о которых я выше говорил, используют библиотеки самого продукта, который написан на C#, и поэтому хотя они и на Pyhton написаны, но запускаются под IronPython и импортируют .net dll'ки. Так вот, мне это дело очень легко, благодаря mono удалось завести на Linux, так как у DO нет Windows машин и это был шок и радость.

Но я немного ушёл от Go. Возвратимся же обратно. Теперь поговорим об интересных проектах и начнём с того, который мог бы заменить мой самописанный в начале поста скрипт. Я там уже говорил про logstash, но это странный продукт написанный на Ruby и запускаемый на JVM, а как я говорил тащить на сервера с Nginx JVM как-то не хочется. Тем более, как показал опыт Димы - поставить JVM через Capistrano, например, не так уж и легко. Но у нас есть аналог сделанный на Go и поэтому полностью самодостаточный - Heka, кто знаком с logstash'ом - то понимает, для остальных это такой обработчик/аггрегатор логов, который может получать и пушать их из/в разных источников. Есть ещё упомянутый в предыдущем посте проект с большим хайпом вокрух него - Docker, я так его плотно и не попробовал. Хотел упаковывать в него, это такой контейнер приложений, некоторые наши node.js приложения, которые меня достали, но забил, нет времени. Зато от них есть отличная презентация зачем же они это всё счастье делают именно на Go - Docker and Go: why did we decide to write Docker in Go?. Проект который близок к нему - это Packer, вернее он даже круче, я его попробовал и он мне нравится. В предыдущем посте я рассказывал про концепцию Immutable Server, когда у нас есть образ и все сервера мы не настраиваем (руками или puppet), а поднимаем с этого образа. Мне не нравиться, что непонятно что в этом образе спрятано и packer это проблему решает - он создаёт образы и для их создания может использовать shell скрипт, puppet или chef рецепт, вообщем у вас будут ещё и исходники образа, если это можно так назвать, к тому же он умеет их создавать в том же DigitalOcean, например. Так что хочу попробовать его ещё и для нашего образа.

Как-то на Go очень много получается именно системных/админских штучек. Вот ещё две. Serf - его я тоже уже упоминал в предыдущем посте, хотел использовать его для текущей задачи и он меня расстроил. Он отлично работает если вам надо обмениваться актуальной информацией о статусах машин, поднялась, упала. Для этого у них даже готовый пример есть. Но пока это всё. Он не позволяет посылать сообщения только определённым машинам или группам. Вообщем, ничего про его использования сказать не могу, разочаровался, но может в комментариях кто-нибудь расскажет практические примеры. А вот etcd - прекрасен чуть более чем полностью. Это такое распределенное хранилище (key-value) метаиформации. Написано, как и все предыдущие проекты на Go, поэтому быстрое и не требует сложной установки. Так что можно расскидать по всем машинам в кластере и на всех них будет появляться актуальная информация о них и других машинах, это как пример. Если нужны ещё примеры, то это замена java монстру с говорящим именем Zookeeper. Например отлично подойдёт для согласования информации о роутах проксирования между серверами с nginx, это то для чего я его думал использовать. Получается он локально вместе с nginx стоит и overhead на обращения к нему на каждом запросе небольшой. Но есть и способ круче, если это информация не очень часто меняется - confd. etcd не только хранит данные, но и может нотифицировать об их изменениях. Поэтому появился confd, который слушает эти сообщения и меняет конфиги исходя из них. Так что мы меняем значение в etcd, оно передаётся на все остальные сервера с ним, которые об этом уведомляют confd, он меняет локальный nginx конфиг и релоудает его. И раз зашёл разговор про хранилище данных, то обязательно посмотрите на InfluxDB - это очень молодой проект, api иногда меняется, стабильность и "production ready" тоже под вопросом, но очень интересный. Хранилище поверх прекрасной гугловской LevelDB оптимизированое для хранение time series (не знаю как красиво перевести) данных. Метрики, статистика, вы поняли надеюсь. Делать выборки можно с помощью прекрасного sql-like синтаксиса, что лучше всяких там mongodb json. И ориентированность на хранение именно такой информации спасёт вас от продумывания схемы хранения данных в вашей базе (как это уложить в ту же mongodb, чтобы она не тормазила при агрегации, на это у 10gen даже отдельная статья есть). Есть инсайд презентация как они это делают, правда на японском, но с картинками InfluxDB & LevelDB Inside-out. И в эту же копилку проект NSQ от известного сокрощателя ссылок. Это распределённая очередь. Чем вы спросите меня она лучше Кролика. Честно - не знаю, не использовал, но несколько статей было про неё, вот зацепился глаз. Говорят что не реально быстрая и скейлится, тут впору вспомнить mongodb с его коронной фразой web scale. Но оно вышло с продакшена очень нагруженого проекта, так что может действительно всё так. Опять таки, если у кого есть что сказать по существу - велком в комментарии.

Может показаться что в основном на Go системные проекты пишут, но на самом деле тут я описал те проекты которые попадались мне на глаза. Но есть люди которые пишут, например, web на нём. Даже в Эпам я видел вакансию, если бы уже не согласился на другой проект, может хохмы ради туда попробовал аплайнуться. И хотя я твиттор уже почикал, но там у меня был ярый фанат Go, который писал свой web framework на нём вдохновленный sinatra (не певцом, а web framwork на ruby), хотя мне он больше рельсы напомнил, так что прорекламирую его тут Сrater.

UPDATE: Как только опубликовал пост пошёл в feedly посмотреть что другие пишут, а там оказывается уже делают LINQ для Go!

6 комментариев:

  1. Ну не знаю, если уж писать системные штуки всякие на чём-то, то лучше это делать на хаскеле, ящитаю. Гошечька слишком невыразительна.

    ОтветитьУдалить
  2. Обоснуй! Хотя не надо, на хаскеле вообще ничего писать нельзя, там ни одна сторонняя библиотека не собирается http://juick.com/Zert/2626503 и даже стандартная библиотека говно http://juick.com/Zert/2626503#32 Ну или я не понял твоего троллинга, старею, сдаю...

    ОтветитьУдалить
  3. Вот те крест^w пруф: http://www.quora.com/Go-programming-language/Why-wasnt-Go-written-as-a-functional-language/answer/Tikhon-Jelvis

    ОтветитьУдалить
  4. О кстати насчёт багов. У тебя фамилия с ошибкой написана. Можешь этот камент удалить.

    ОтветитьУдалить
  5. пля. поудалял акаунты в соцсетях и дискас сума сошёл, не знаю откуда он эту фамилию взял. До этого была правильная. Пасиб. За пул реквесты тоже, откопал же стюардессу :)

    ОтветитьУдалить