Содержание

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


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


Статья начинает короткую серию, освещающаю то, что я узнал в процессе. Этот первый пост представляет собой общее введение, описывающее текущее положение дел, проблемы и то, почему я считаю Go хорошим выбором. Последующие статьи будут более детальными и содержать больше кода. Мне любопытно, насколько мой опыт коррелирует с вашим; может быть я в чем-то ошибаюсь, так что не стесняйтесь комментировать.


Если вас интересует только код, он тут.


Введение


Раньше моих базовых знаний HTML, CSS и JavaScript было достаточно для моих скромных нужд в сайтостроении. Большинство приложений, которые я когда-либо создавал, были сделаны с помощью mod_python, напрямую используя механизм публикации обработчиков (прим.пер.: пример можно посмотреть тут). Забавно, что будучи ранним последователем Python, я также немало поработал с Rails. В течение последних нескольких лет я сосредоточился на инфраструкте (больших) данных, которая вовсе не является веб-разработкой, хотя необходимость в веб-интерфейсах тут — не редкость. Фактически, приложение, которым я сейчас занимаюсь, является приложением для работы с данными, но оно не опенсорсное и то, что оно делает, не имеет значения для данной статьи. В общем, это должно прояснить, с какой стороны я на все это смотрю.


Python и Ruby


Еще год назад я бы рекомендовал Python или Ruby в качестве среды веб-приложения. Может быть есть и другие подобные языки, но с моей точки зрения, в мире доминируют Python и Ruby.


Большую часть времени главной задачей веб-приложения было конструирование веб-страниц с помощью компоновки конечного HTML на стороне сервера. Как Python, так и Ruby очень хорошо подходят для извлечения данных из БД и превращению их в кучу HTML-кода с помощью шаблонов. Существует множество фреймворков/инструментов на выбор, например, Rails, Django, Sinatra, Flask и т.д. и т.п.


И хотя эти языки имеют определенные существенные ограничения, такие как GIL, легкость, с которой они решают проблему генерации HTML, намного ценнее компромиссов, на которые приходится идти.


GIL


GIL (Global Interpreter Lock) залуживает отдельного упоминания. Безусловно, это самое большое ограничение любого решения на Python или Ruby, но это очень скользкая тема, люди чаще предпочитают делать вид, что проблемы нет. А если уж речь об этом зашла, эмоции обычно бьют через край, в сообществах Ruby и Python идут бесконечные обсуждения на тему GIL.


Для тех, кто незнаком с этой проблемой — GIL позволяет выполнятся только одной вещи за раз. Когда вы создаете потоки и они ”выглядят” как параллельно выполняющиеся, на самом деле интерпретатор все еще выполняет инструкции последовательно. Это означает, что один процесс может использовать только один CPU.


Существуют альтернативные реализации, например, основанные на JVM, но они нечасто применяются. Я точно не знаю почему, возможно они не полностью совместимы или, вероятно, не поддерживают корректно C-расширения, и у них при этом все еще может быть GIL. Не уверен, но насколько я могу судить, обычно все-таки используется реализация на C. Чтобы сделать интерпретатор без GIL, придется его полностью переписать, а это уже может изменить поведение языка (в моем наивном понимании), и поэтому мне кажется, что GIL останется.


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


Обычно это делается с помощью дополнительного ПО, такого как Unicorn/Gunicorn, при этом каждый процесс слушает свой собственный порт и запускается позади какого-то балансировщика соединения, типа Nginx и/или Haproxy. Альтернативно это может быть сделано через Apache и его модули (такие как mod_python или mod_wsgi), в любом случае это сложно. Такие приложения обычно полагаюся на сервер базы данных в качестве арбитра для любых, чувствительных к конкурентности, задач. При реализации кэширования, чтобы не хранить множество копий одного и того же на одном и том же сервере, требуется хранилище с разделяемой памятью, типа Memcached или Redis, а обычно оба. Также такие приложения не могут делать фоновую обработку, для этого существует отдельный набор инструментов, такой как Resque. И потом все эти компоненты требуют мониторинга, чтобы быть уверенным, что все это работает. Логи должны быть консолидированными, и для них есть свои дополнительные инструменты. Учитывая неизбежную сложность этой настройки, также требуется наличие менеджера конфигурации, такого как Chef или Puppet. И тем не менее, эти наборы, как правило, не способны поддерживать большое количество долговременных соединений — проблема известная как C10K.


В итоге простое веб-приложение с базой данных требует целую кучу составных частей, прежде чем оно сможет обслуживать страницу «Hello World!». И почти все это из-за GIL.


Появление одностраничных приложений


Все дальше и дальше в прошлое уходит генерация HTML на сервере. Последняя (и правильная) тенденция заключается в построении пользовательского интерфейса и рендеринге полностью на стороне клиента, с помощью JavaScript. Приложения, чей пользовательский интерфейс полностью управляется JS, иногда называют одностраничным приложением и, на мой взгляд, за ними будущее, нравится нам это или нет. В таких приложениях сервер только обслуживает данные, обычно в виде JSON, не создавая HTML-кода. В этом случае та огромная сложность, введенная в первую очередь для возможности использования популярного скриптового языка [для создания веб-прилолжения], оказывается ненужной. Особенно учитывая, что Python или Ruby приносят мало выгоды, когда весь вывод — это JSON.


Взгляд на Golang


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


Программы на Go представляют собой бинарники, которые нативно запускаются, так что не требуется ничего языкоспецифического устанавливать на сервер. Исчезает проблема обеспечения правильной версии среды исполнения, требуемой приложением; отдельной среды исполнения нет — она встроена в бинарник. Программы на Go могут легко и элегантно запускать задачи в фоне, поэтому нет нужды в инструментах типа Resque. Эти программы запускаются как единственный процесс, так что кэширование становится тривиальным, а значит, Memcached или Redis не нужны. Go может управлять неограниченным количеством параллельных соединений, нивелируя надобность в фронтэндной защите, такой как Nginx.


С Go высокая многослойная башня из Python, Ruby, Bundler, Virtualenv, Unicorn, WSGI, Resque, Memcached, Redis, и т.д., и т.п. уменьшается до всего лишь одного бинарника. Единственный сторонний компонент, который обычно все еще нужен, — это база данных (я бы посоветовал PostgreSQL). Тут важно отметить, что все эти инструменты по прежнему можно использовать, но с Go можно обойтись и без них.


Время запуска такой Go-программы будет, скорее всего, на порядок превосходить любое приложение на Python/Ruby, потребует меньше памяти и строк кода.


Хорошо, а есть популярный фреймворк?


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


Надо понимать, зачем вообще были созданы фреймворки. В мире Python/Ruby так случилось потому, что эти языки не были изначально спроектированы для обслуживания веб-страниц, и для решения этой задачи необходимо было множество внешних компонентов. То же самое можно сказать и про Java, который, как и Python, и Ruby, стары как веб, каким мы его знаем, или даже немного старше.


Насколько я помню, ранние версии Python "из коробки" не предоставляли ничего для работы с базой данных, не было шаблонов, поддержка HTTP была запутанной, работа с сетью — нетривиальной, даже шифрование тогда было незаконным и в общем, много чего еще отсутствовало. Фреймворк обеспечивал все эти необходимые кусочки и устанавливал характерные для языка правила разработки для всех распространенных вариантов веб-приложений.


Go, с другой стороны, создавался людьми, которые уже имели опыт и разбирались в веб-разработке. Он включает в себя практически все необходимое. Один-два внешних пакета могут понадобиться для решения некоторых конкретных задач, типа OAuth, но ни в коем случае эта пара пакетов не является "фреймворком".


Если все вышеприведенное касаемо фреймворков звучит не достаточно убедительно, полезно рассмотреть кривую обучения для фреймворков и риски. Мне понадобилось около двух лет, чтобы наладить отношения с Rails. Фреймворки могут стать заброшенными и устаревшими, а переносить приложение на новый фреймворк тяжело, а иногда и невозможно. Учитывая, как быстро все меняется в информационных технологиях, фреймворк уж точно не следует выбирать легкомысленно.


Я хотел бы особо выделить инструменты и фреймворки, которые пытаются имитировать идиомы, общие для Python, Ruby или сред JavaScript. Все, что выглядит, или ощущается, или претендует на роль «Rails for Go», включая такие техники, как инъекции, динамическая публикация методов и т.п., которые сильно зависят от рефлексии, не вписывается в идеологию Go, поэтому лучше от такого держаться подальше.


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


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


Как насчет базы данных и ORM?


Аналогично фреймворкам, ORM'ы в Go не сильно распространены. Для начала, Go не поддерживает объекты — то, что обозначено O в аббревиатуре ORM.


Я знаю, если вместо того, чтобы пользоваться удобным User.find(:all).filter..., которое обеспечивается чем-то вроде ActiveRecord, писать SQL вручную — это нечто неслыханное в некоторых сообществах, но я все-таки думаю, что такое отношение должно измениться. SQL — прекрасный язык. Иметь дело с SQL напрямую — это не так уж сложно, а взамен мы получаем больше свободы и возможностей. Пожалуй, самой утомительной частью такой прямой работы является копирование данных из курсора базы данных в структуры, но здесь очень пригодится проект sqlx.


Заключение


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


  • Минимальная зависимость от сторонних пакетов.
  • Без веб-фреймворка.
  • PostgreSQL в качестве БД.
  • Одностраничное приложение.

Продолжение

Поделиться с друзьями
-->

Комментарии (43)


  1. comerc
    27.05.2017 11:48

    Прекрасная статья для начинающих и подоспела вовремя. Последние выходные мая — лучше не придумаешь, чтобы написать свое первое приложение на Go.


  1. Merlen_Gross
    27.05.2017 12:23
    +4

    Tornado решает проблему C10K. Автор слишком быстро отбросил Python.


    1. TyVik
      27.05.2017 17:58
      +5

      Автор, похоже, не догадывается, что проблема C10K уже не актуальна, и все решают проблему C10M :) Да и тот же uvloop в python вполне может тягаться с go, а местами даже и выигрывать.


      1. kilgur
        27.05.2017 18:28

        Если внимательно почитать статью по вашей ссылке, то там есть такой текст:

        We use Python 3.5, and all servers are single-threaded. Additionally, we use GOMAXPROCS=1 for Go code, nodejs does not use cluster, and all Python servers are single-process.

        У Go нет GIL. Когда вы захотите «заюзать» сервер целиком со всеми его ядрами, начнется «балансировщик запросов, разделяемый кэш, ...» и далее по тексту. В Go вам просто не надо будет выставлять GOMAXPROCS=1 и оно «как-то само» по всем ядрам «расползется»…
        Я, разумеется, утрирую… но доля истины в моем комментарии есть.


        1. TyVik
          27.05.2017 19:04

          Чудес не бывает. Как-то само, оно, конечно, расползается, но управляет всем по-прежнему Go. В случае python ничто не мешает запустить изначально по процессу на ядро — результат будет тот же самый. GIL работает в рамках одного процесса (блокирует именно потоки выполнения). К тому же только часть пользовательского кода работает за GIL — есть ряд библиотек (тот же NumPy, Pandas), которые на время отключают GIL.


        1. qRoC
          28.05.2017 10:44

          Когда вы захотите «заюзать» сервер целиком со всеми его ядрами, начнется «балансировщик запросов, разделяемый кэш, ...» и далее по тексту.

          Если вы захотели «заюзать» сервер целиком, значит в этом есть необходимость, а значит уже совсем скоро вы «захотите» добавить ещё один сервер, и в итоге не важно go это или python, но балансировщик и разделяемый кеш в любом случае будет. Так что Ваше утверждение не звучит как минус.


      1. IgnisNoir
        27.05.2017 18:54

        Условия сравнения были далеко не честными и го ограничивали.


      1. JekaMas
        02.06.2017 09:03

        Ерунда какая-то с точки зрения бенчмарков. Особенно http — давайте возьмем лучшую по производительности версию для python и один из наиболее медленных вариантов на golang.
        Я пишу и на go и на python так что на мой взгляд такие сравнения вредны своим непрофессиональным подходом.


  1. RidgeA
    27.05.2017 12:44
    +1

    фреймворк является необязательным и не рекомендуется.

    Да, на Golang нет особой необходимости в сложных фреймворках.
    Но мне нужна была функциональность middleware для сервиса — я выбрал https://github.com/labstack/echo. Да, можно сделать самому, но зачем?..
    Не знаю, насколько верное решение, время покажет.

    Спасибо за статью.


  1. oxidmod
    27.05.2017 14:33
    +4

    Может быть есть и другие подобные языки, но с моей точки зрения, в мире доминируют Python и Ruby.


    Не хочу вас расстраивать, но в мире веб-дева доминирует php)


    1. comerc
      27.05.2017 23:43

      … но это неточно


      1. comerc
        28.05.2017 12:44

        или вот еще — более конкретные данные


        1. k4ir05
          28.05.2017 18:13

          Ну да, тут он вообще с SQL сравнивается. )
          А в первом примере JS лидирует, имхо, только за счёт развития SPA. На бэкенде всё-равно PHP в приоритете.


  1. bano-notit
    27.05.2017 15:06

    Интересно это конечно всё, но отсутствие фреймворков — проблема, причём реальная. SPA — очень спорная часть жизни веба, с одной стороны это конечно хорошо и тихий переход к вебу как к платформе, но именно это же и убивает лёгкость веба. Тогда уж легче и экономнее будет установить нативное приложение, чем хранить в кеше 1000 строк js кода. Так что в этом моменте я бы поспорил, хотя сам очень люблю SPA.


    А вот фреймворки реально нужны, в любом случае. И не потому, что яп не спроектирован под веб, а потому, что хочется иметь модульность проекта, лёгкость работы с ним и низкий порог входа в проект. Фреймворки в каком-то смысле создают стандартны написания и комьюнити, а оно в свою очередь профессионалов, с которыми легко работать именно в контексте этого фреймворка. Ведь в какой-то момент команда может поменяться полностью, и мало кто захочет копаться в нативном громоздком коде, написанным какими-то людьми несколько лет назад по каким-то своим правилам.


  1. vansickle
    27.05.2017 16:37

    Для начала, Go не поддерживает объекты — то, что обозначено O в аббревиатуре ORM.


    Что вы имели в виду под «Go не поддерживает объекты»?


    1. kilgur
      27.05.2017 17:05

      Цитата из википедии: «Наличие инкапсуляции достаточно для объектности языка программирования, но ещё не означает его объектной ориентированности — для этого требуется наличие наследования.» Инкапсуляция в Go есть, да, а вот наследования нет. На мой взгляд тут автор слегка «переборщил» — наследование не самая важная часть ORM.


      1. TyVik
        27.05.2017 18:02
        +2

        Наследование таблиц/моделей очень помогает при реализации связей One-to-one, например. У нас в проекте есть 3 вида пользователей, каждый из которых использует свой механизм авторизации (требование законов). Так вот базовая часть каждого типа хранится в одной таблице.


      1. IgnisNoir
        27.05.2017 18:52
        +1

        Ну если я правильно помню то O в ORM относится как к объекту в целом. Как говорит Википедия оно связывает базы данных с концепциями объектно-ориентированных языков программирования. Но это не означает прямое использование ООП. В Го даже не одна и даже не три ORM библиотеки и не ручаюсь за все но GORM работает просто великолепно


      1. danforth
        30.05.2017 13:57

        Наследование, а точнее подобие наследования, есть через анонимные структуры. Единственное но, в аргументы к функциям нельзя принимать значение родителя, т.е. не прокатит, скажем, принимать Animal (структуру) даже если структура Dog встраивает Animal.

        Вот как это выглядит: https://play.golang.org/p/f5m6WNseR8

        Как видно на примере, я определил Animal и его интерфейс, потом создал структуру Cat и Dog. Они приняли в себя структуру Animal анонимно, т.е. утрированно унаследовали поля и методы Animal. Далее проитерировал по слайсу моих домашних питомцев и вызвал методы объектов.

        В Go есть ООП, но не такое, как мы привыкли видеть. С другой стороны, никогда не будет сотен слоев абстракций, в которых черт ногу сломит.

        Ещё хотелось бы напомнить, что строго определения и стандарта ООП нету.


        1. kilgur
          30.05.2017 14:45

          Спасибо, но это не подобие наследования, это композиция. Другой подход. В каких-то кейсах лучше наследования, в каких-то — хуже. В Go нет наследования, есть композиция. Позволительно ли называть язык объектноориентированным, если он не поддерживает наследование, но имеет интерфейсы и композицию, — я не знаю. Кто-то считает, что наследование — это фундаментальная черта ООП, кто-то считает, что и композиции достаточно. Лично для меня — этот вопрос не важен; если в языке нет инструмента, какой смысл рассуждать об этом? «Если бы у бабушки был <censored>, она была бы дедушкой» )
          Скажите, а с какой целью вы в своем примере встраиваете интерфейс IAnimal в структуру Animal? В Go нет надобности как-то объявлять, что структура будет соответствовать какому-либо интерфейсу — если реализовали у структуры все методы интерфейса, значит структура ему соответствует.
          Добавлено: и да, в вашем примере нет «анонимных структур» )


          1. danforth
            30.05.2017 15:26
            +1

            Да, это композиция. И да, анонимных структур нету, есть анонимные поля у структур. Как либо объявлять имплементацию интерфейса нет необходимости, вы правы, достаточно реализовать все его методы интерфейса, имплементация неявна. Я пишу на нескольких языках, и не всегда могу быстро переключить контекст и начать писать на Go и мыслить на Go.
            Я ответил под вашим постом, потому что многие заявляют как факт: «В Go нету ООП». Окей, пусть сначала принесут сюда стандарт ООП, для начала.


  1. unabl4
    27.05.2017 18:39
    +3

    В Ruby 3.0 GIL будет убран. Это основное направление работы в данный момент.
    И да, Ruby нигде сейчас не доминирует и даже успел перестать быть мейнстримом.


    1. jemboa
      28.05.2017 22:01
      +1

      Этому есть какое-то подтверждение? Я слышал только о добавлении guild http://olivierlacan.com/posts/concurrency-in-ruby-3-with-guilds/, но это совсем не избавление от gil.


  1. neoxack
    27.05.2017 20:29
    +1

    Эти программы запускаются как единственный процесс, так что кэширование становится тривиальным, а значит, Memcached или Redis не нужны.

    Эмм, а аутентификация и сессии пользователей?


    1. kilgur
      27.05.2017 20:48
      -1

      Имеется в виду то, что кэширование можно организовать средствами Go. В противовес приложениям, вынужденным запускаться в виде нескольких процессов, для которых необходим внешний кэш, чтобы не дублировать кэшируемые данные.
      На один сервер запускается один экземпляр веб-приложения в случае Go. Соответственно, сессии пользователей можно хранить в рамках этого же процесса (map, в самом простом случае).
      Разумеется, при горизонтальном масштабировании (на несколько серверов) кэш придется вынести за пределы приложения.


      1. dem0n3d
        27.05.2017 21:11
        +3

        А ещё кеширование можно организовать средствами Си и вызывать из Go. Ощущается странная мания внести вообще всё внутрь своего приложения. Перечисленные инструменты разрабатывались под конкретные задачи и справляются с ними хорошо, так зачем делать велосипед? А отказ от фреймворков подразумевает что делать его придётся с нуля. HTTPS тоже сами делать будете?


        1. kilgur
          27.05.2017 21:30

          Можно и так, если вам это зачем-то нужно. И никто не запрещает использовать Memcache или Redis (или еще что-нибудь современно-экзотическое). Если в этом есть необходимость, пожалуйста — есть готовые библиотеки.


          1. dem0n3d
            27.05.2017 21:38
            +2

            Ну смотрите: сейчас кто то проникнется вашими идеями, начнёт проект на Go. А потом проект взлетит. А потом внезапно придёт осознание что redis таки нужен. А потом nginx с HTTPS. А потом фоновые задачи тоже неплохо бы вынести отдельно (ну там горизонтальное масштабирование и т.п.). И даже жуткий Puppet (ну или не такой жуткий Ansible). И в итоге все описанные преимущества сойдут на нет. А что имеем в итоге? "Ручной" SQL. Ручное управление сессиями. Да вообще всё ручное. И все детали реализации на поверхности.


            1. kilgur
              27.05.2017 22:26

              Давайте все-таки начнем с того, что идеи не мои. Хотя я ими и проникся в процессе перевода и более-менее разделяю. Поэтому постараюсь ответить. HTTPS есть встроенный, если хочется letsencrypt, можно «дернуть» пакет для обновления сертификатов из готовых фреймворков. В конце концов, поставить nginx перед сервисом — дело 5 минут (20 с «гуглением», если не делали такого раньше). Другое дело, если ради повышения производительности (или отказоустойчивости) начинаем горизонтально масштабироваться. Если я правильно понимаю вас, это как раз тот самый случай, когда «внезапно придёт осознание что redis таки нужен». И тут вы, разумеется, правы. Но дело в том, что еще на этапе «redis нам нафик не нужен» можно кэширование организовать интерфейсом, отделив от конкретной реализации, так сказать, сделать задел на будущее. Понадобится вынести кэш в redis для масштабирования или «распила монолита», измените реализацию интерфейса, остальной код трогать не придется. Зато на начальном этапе (пока еще не взлетело) имеем весьма простой деплой и конфигурирование. В простейшем приложении из статьи пример такого подхода — cfg.UI.Assets.
              Я к тому, что вообще, вы правы, конечно. Но в частности — не совсем. Ведь, до какой-то степени приложение можно масштабировать и вертикально. И в случае Go вам не придется прилагать для этого особых усилий. В случае, например, того же python вам изначально придется делать то, о чем мы говорим (балансер, кэш, фон, ...) — без этого будет грустно смотреть на загрузку сервера.
              Возможно, я не прав — все-таки я сейчас больше админ…


              1. dem0n3d
                27.05.2017 22:43
                +3

                Есть такая замечательная штука как Ansible (несправедливо не упомянутая в статье), которая будет делать всё это за вас. А есть ещё не менее прекрасный Docker. Но на самом деле это всё мелочи. Я обязательно прочитаю остальные части (кстати, неплохо бы их осмысленно озаглавить), и тогда уже задам вопросы по существу. И да, не заметил что перевод.


              1. Spalf
                28.05.2017 11:27

                Справедливости ради, стоит заметить что Керниган и Донован в своей книге отдельно отмечают, что не нужно использовать встроенные механизмы Go для организации очередей и key-value хранилищ, несмотря на соблазн, а обратится к специализированным решениям.


            1. JPEG
              27.05.2017 23:35
              +4

              Да, описанный подход со всем внутри одного процесса был очень популярен у явистов в начале веба, когда сервера были большими (в сравнении с количеством посетителей). Сегодня компьютеры стали маленькими (опять же в сравнении с количеством посетителей), но их стало очень-очень много. Тут приходит на ум модель гугла с большими кластерами на относительно дешевом железе со всякой отказоустойчивостью через горизонтальное масштабирование. А горизонтальное масштабирование требует разделения ресурсов в кластере. Именно оттуда и растут ноги у nginx, memcached, rabbitmq, etc — они помогают размазать нагрузку по сотням машин. Часть машин содержит размазанный кеш, часть собирает из него пресловутый HTML, часть раздаёт горячие данные, часть холодные.

              Отсюда вывод: да, можно сделать монолит, который будет в 4 раза быстрее на одной машине, чем вот тот стек, что описан в статье, но расти ему будет некуда.

              А еще есть база данных, в которую всегда упирается веб, как его не готовь. Вот тут хорошо описано phgrey: Чек-лист по выживанию сайта.


              1. TimsTims
                28.05.2017 02:46
                -1

                Пример с гуглом не подходит — создатели Го как раз работают в Гугле, и создавали язык специально чтобы разгрузить свои сервера. И у них это в целом получилось, язык нашел свою нишу и используется в Гугле


                1. JPEG
                  28.05.2017 11:40
                  +1

                  Сразу вопрос. Какой тип серверов в кластере они смогли разгрузить? Если у вас есть ссылка на статью о том, как им это помогло, то я буду рад проапгрейдиться :)


                  1. TimsTims
                    28.05.2017 15:53
                    -1

                    А у вас подписка на Гугл закончилась?
                    «Golang in google» = первые результаты
                    https://www.quora.com/How-is-Go-used-at-Google


  1. QtRoS
    27.05.2017 21:05

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


  1. dem0n3d
    27.05.2017 22:04
    +6

    Решил написать развёрнутый комментарий. Читал статью с большим интересом, подумал: вдруг она убедит меня в том, что Go нужен? Но по ходу статьи все более становилось похоже что автор убеждает себя. Аргументы, скажем так, несостоятельные. Пройдёмся подробнее.


    1. Многопоточность. GIL — это особенность реализации. Она действительно не позволяет использовать многопоточное программирование, но польза от него в веб-приложениях крайне сомнительная (разве что вам действительно необходимо в рамках одного запроса делать какие-то трудоёмкие и параллелящиеся операции; при этом если используется библиотека типа sklearn или numpy, то там параллелизм будет). Дропбоксовцы пилили свою реализацию питона — "Pyston", и кажется даже без GIL (но это не точно). Она была очень быстрой в бенчмарках, но вот прирост производительности в реальном веб-приложении (ради чего всё и затевалось) оказался неприлично маленьким, в итоге её забросили. Так что не GIL'ом единым. В реальных приложениях всякие UWSGI справляются с задачей превосходно. Кстати, в приведённой статье про C10K перечислены Nginx, Tornado и т.д. как решения данной проблемы. В любом случае, ничего сложного в этом нет.


    2. Фоновые задачи. Да, без них никуда. И да, их как правило следует писать как часть приложения. Но я не вижу ничего плохого (и даже наоборот) в использовании Celery или Resque (да, с redis в качестве бэкенда).

    В этом случае та огромная сложность, введенная в первую очередь для возможности использования популярного скриптового языка [для создания веб-прилолжения], оказывается ненужной.

    Что?


    Особенно учитывая, что Python или Ruby приносят мало выгоды, когда весь вывод — это JSON.

    Ну современные СУБД умеют отдавать JSON (тот же Postgres), зачем вообще нужна прослойка в виде Go? Даёшь веб-приложения на PL/SQL!


    1. ORM не нужен. Ок… Только не забывайте предохраняться от SQL-инъекций. При миграции деликатно промолчу.
    2. Фреймворк не нужен. Ок… Только не забывайте предохраняться от XSS, CSRF и т.д.

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

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


    То, что начинается с лексического псевдонима для одной строки JavaScript становится слоем в слоях транспайлеров, минимайзеров, поверх хелперов, скрытых где-то в подзависимостях.

    Я понимаю что это реверанс в сторону нода, но на клиенте от транспайлинга JS тоже откажемся? Что дальше, нафиг ES6? Вернёмся к ActiveX?


    А ещё в статье вообще не описаны недостатки Go. Я сам с ним не знаком, но на мой взгляд это в первую очередь своеобразный синтаксис и ООП (точнее, его отсутствие), вместо которого применяется нечто вроде monkey-патчинга, который с моей точки зрения является особо опасным антипаттерном.


    Итог: Я так и не увидел ни одного преимущества Go. Перспектива пилить велосипед на каждый чих меня скорее отталкивает. И всё ради чего? Аргумент "патаму что сложна" меня как то не удовлетворяет.


    1. comerc
      27.05.2017 23:21

      Q: Is Gorilla a framework?
      A: No, it is a toolkit. Just use what you need with your favorite framework or the http package.

      http://www.gorillatoolkit.org/pkg/



    1. danforth
      28.05.2017 07:54
      -2

      У Go есть ООП.


  1. ncix
    27.05.2017 23:43
    +5

    Я знаю, если вместо того, чтобы пользоваться удобным User.find(:all).filter..., которое обеспечивается чем-то вроде ActiveRecord, писать SQL вручную — это нечто неслыханное в некоторых сообществах, но я все-таки думаю, что такое отношение должно измениться. SQL — прекрасный язык.
    Хочется пожать автору руку. Много лет назад тренд развития средств разработки почему-то пошел таким образом, чтобы спрятать SQL с глаз долой, обложив сверху разными прослойками (ORM), дающими лишь жалкое подобие возможностей SQL.


    1. TimsTims
      28.05.2017 02:53

      А я все заставить себя не мог перейти на такой orm, думал лучше сам запрос наберу, да наджойню и пофильтрую. Оказывается, не ошибся в целом )


  1. firstbyte
    31.05.2017 07:15

    Сам около полугода разрабатываю разного рода сервисы и миддлвари на Go. Всё идёт хорошо.
    Что нужно на мой взгляд учесть и с чем сам часто сталкивался:


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


    Программировать нужно очень аккуратно. Зависающий при каких-то экзотических условиях мутекс или неиспользование оных, а также некорректное использование тикеров обеспечит вам стабильные падения части вашего ресурса. Поэтому нужно очень тщательно всё тестировать и пользоваться go build -race.


    Автоперезапуск упавшей части веб-приложения решается путём создания из них системных сервисов с автоперезапуском при падениях.


    А если планируете вести простой блог для себя или сайт-портфолио, то обратите внимание на Hugo, свяжите его с Caddy с плагином http.hugo и наслаждайтесь статическим сайтом, который автоматически генерируется из исходников в виде markdown-страниц