learnopengl. Урок 2.1 — Цвета

Перевод очередного урока с сайта learnopengl.com. Недавно обнаружил на русском Уроки по OpenGL с сайта OGLDev, но некоторые из них требуют 4 версию opengl, а примеры кода слишком зависимы от предыдущих уроков и объектно-ориентированы. Поэтому, вниманию всех интересующихся opengl’ем новичков со стареньким железом предлагаю коротенькую статью о цвете, с которой начинается вторая часть обучающего курса от Joey de Vries:

Цвета

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

Меню

Часть 1

  1. OpenGL
  2. Создание окна
  3. Hello Window
  4. Hello Triangle
  5. Shaders
  6. Текстуры
  7. Трансформации
  8. Системы координат

Часть 2

  1. Цвета
  2. Основы освещения
  3. Материалы
  4. Карты освещения
  5. Источники света
  6. Множественное освещение

В реальном мире у каждого объекта есть свой собственный цвет, который может принимать практически любые значения. В цифровом же мире нам необходимо отобразить (бесконечные) цвета реальности посредством (ограниченного диапазона) цифровых значений, поэтому в цифровом виде могут быть воспроизведены не все существующие цвета. Однако мы можем отображать настолько огромное количество цветов, что вы, вероятно, все равно не заметите разницы. В цифровой форме цвета представляют совокупностью трех компонент: красного, зеленого и синего, обычно сокращенно называемой RGB (Red Green Blue). Используя различные комбинации всего лишь этих трех значений, мы можем передать почти любой существующий цвет. Например, чтобы получить коралловый цвет, мы зададим вектор цвета следующим образом:

glm::vec3 coral(1.0f, 0.5f, 0.31f);

Цвета, видимые нами в реальной жизни, это цвета не самих объектов, а цвет отраженного ими света; т.е. мы воспринимаем цвета, которые остаются не поглощёнными (отброшенными) объектом. Например, свет солнца воспринимается как белый свет, который состоит из всех цветов спектра (вы можете это видеть на картинке). Таким образом, если мы посветим белым светом на синюю игрушку, то она будет поглощать все составляющие белый свет цвета, кроме синего. Так как игрушка не поглощает синий цвет, он отразится, и этот отраженный свет, воздействуя на наши органы зрения, создаёт впечатление, что игрушка окрашена в синий цвет. Следующий рисунок иллюстрирует этот феномен на примере объекта кораллового цвета, отражающего исходные цвета с разной интенсивностью:

Отражение и поглощение цветов

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

Эти правила отражения света непосредственно применяются в компьютерной графике. Когда в OpenGL мы создаем источник света, то указываем его цвет. В предыдущем абзаце мы говорили о белом солнечном свете, поэтому давайте и нашему источнику света тоже зададим белый цвет. Если затем мы умножим цвет источника света на цвет объекта, то полученное значение будет отраженным цветом объекта (и, следовательно, цветом в каком мы воспринимаем объект). Вернемся к нашей игрушке (на этот раз кораллового цвета) и посмотрим, как графическими средствами вычислить её цвет, воспринимаемый наблюдателем. Для получения нужного нам цвета, произведем покомпонентное умножение двух цветовых векторов:

glm::vec3 lightColor(1.0f, 1.0f, 1.0f); glm::vec3 toyColor(1.0f, 0.5f, 0.31f); glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.5f, 0.31f);

Игрушка поглощает наибольшую часть белого света, а оставшееся количество красного, зеленого и синего излучения она отражает соответственно цвету своей поверхности. Это демонстрация того, как цвета ведут себя в реальном мире. Таким образом, мы можем задать цвет объекта вектором, характеризующим величины отражения цветовых компонент, поступающих от источника света. А что бы произошло, если бы для освещения мы использовали зеленый свет?

glm::vec3 lightColor(0.0f, 1.0f, 0.0f); glm::vec3 toyColor(1.0f, 0.5f, 0.31f); glm::vec3 result = lightColor * toyColor; // = (0.0f, 0.5f, 0.0f);

Как мы видим, в источнике света нет красной и синей составляющей, поэтому игрушка не будет их поглощать и/или отражать. Одну половину всего количества зеленого света игрушка поглощает, а вторую отражает. Поэтому цвет игрушки, который мы увидим будет темно-зеленый. Таким образом, если мы используем зеленый источник света, то отражаться и восприниматься будут только зеленые компоненты; никакие красные и синие оттенки не будут видны. В результате объект кораллового цвета неожиданно становится темно-зеленоватым. Давайте проведем еще один эксперимент с темным оливково-зеленым светом:

glm::vec3 lightColor(0.33f, 0.42f, 0.18f); glm::vec3 toyColor(1.0f, 0.5f, 0.31f); glm::vec3 result = lightColor * toyColor; // = (0.33f, 0.21f, 0.06f);

Перед вами пример получения странной окраски объекта из-за использования разноцветного освещения. Оказывается стать оригинальным колористом совсем не сложно.

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

Освещенная сцена

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

Первое, что нам понадобится — это освещаемый объект, в качестве которого мы будем возьмем уже знакомый нам по предыдущим урокам куб. Нам также будет нужен какой-нибудь яркий объект, для того, чтобы показать, где в 3D-сцене находится источник света. Для простоты источник света мы тоже представим кубом (ведь у нас уже есть координаты вершин, неправда ли?).

Поскольку создание VBO (vertex buffer object), установка указателей атрибутов вершин и выполнение всех остальных мудрёных операций для вас теперь не должно представлять сложности, то мы не будем на этом останавливаться. Если эти темы у вас по-прежнему вызывают трудности, то, прежде чем продолжить, я рекомендую вам ознакомиться с предыдущими уроками и, по возможности, выполнить предлагаемые упражнения.

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

#version 330 core layout (location = 0) in vec3 position;  uniform mat4 model; uniform mat4 view; uniform mat4 projection;  void main() {     gl_Position = projection * view * model * vec4(position, 1.0f); }

Убедитесь в том, что вы обновили данные вершин и указатели атрибутов в соответствии с новым вершинным шейдером (впрочем, если вы хотите, то можете оставить в VAO буфер с текстурными координатами и активный указатель на эти атрибуты; мы просто пока не будем их использовать, но и начать всё «с чистого листа» тоже не такая уж и плохая идея).

Поскольку мы собираемся добавить в сцену куб-лампу, то для неё нам потребуется создать новый VAO. Мы могли бы изобразить лампу тем же самым VAO, трансформировав матрицу модели, но в будущих уроках мы будем довольно часто менять данные вершин и атрибутов объекта-контейнера и при этом не хотим, чтобы изменения затрагивали объект лампы (в нём нас интересует только координаты вершин лампы), поэтому давайте создадим еще один VAO:

GLuint lightVAO; glGenVertexArrays(1, &lightVAO); glBindVertexArray(lightVAO); // Так как VBO объекта-контейнера уже содержит все необходимые данные, то нам нужно только связать с ним новый VAO  glBindBuffer(GL_ARRAY_BUFFER, VBO); // Настраиваем атрибуты (нашей лампе понадобятся только координаты вершин) glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(0); glBindVertexArray(0);

Этот код относительно прост. Теперь, когда мы создали и контейнер, и лампу-куб, осталось сделать еще одну вещь — фрагментный шейдер:

#version 330 core out vec4 color;    uniform vec3 objectColor; uniform vec3 lightColor;  void main() {     color = vec4(lightColor * objectColor, 1.0f); }

Фрагментный шейдер через uniform-переменные получает два параметра: цвета объекта и цвета источника света. Как мы уже обсуждали в начале этого урока, для получения воспринимаемого (т.е. отражаемого объектом) цвета, мы умножаем вектор источника света на вектор цвета объекта. И снова, исходный текст шейдера не должен вызывать вопросов.
Давайте зададим объекту коралловый цвет из предыдущего раздела:

// Не забудьте перед установкой uniform-переменных для соответствующей шейдерной программы вызвать glUseProgram  GLint objectColorLoc = glGetUniformLocation(lightingShader.Program, "objectColor"); GLint lightColorLoc  = glGetUniformLocation(lightingShader.Program, "lightColor"); glUniform3f(objectColorLoc, 1.0f, 0.5f, 0.31f); glUniform3f(lightColorLoc,  1.0f, 1.0f, 1.0f); // зададим цвет источника света (белый)

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

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

#version 330 core out vec4 color;  void main() {     color = vec4(1.0f); // Устанавливает все 4 компоненты вектора равными 1.0f }

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

Основное предназначение куба лампы — показать месторасположение источника света. Мы задаем где-то в сцене положение источника света, но это только координата точки, которая не имеет визуального представления. Чтобы действительно показать лампу, мы нарисуем в месте нахождения источника света куб. Рисование лампы осуществляется с помощью шейдера лампы, и это гарантирует нам, что куб лампы всегда останется белым, вне зависимости от условий освещения сцены.

Итак, давайте объявим глобальную переменную vec3, которая будет представлять положение источника света в координатах мирового пространства:

glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

Теперь, прежде чем нарисовать лампу, сдвинем её в позицию источника света, и немного уменьшим размер куба, чтобы убедиться, что лампа не слишком доминирует в сцене:

model = glm::mat4(); model = glm::translate(model, lightPos); model = glm::scale(model, glm::vec3(0.2f));

Получившийся в результате код рисования лампы должен выглядеть примерно так:

lampShader.Use(); // Установка uniform-переменных матриц модели, вида и проекции ... // Рисование куба лампы glBindVertexArray(lightVAO); glDrawArrays(GL_TRIANGLES, 0, 36);			 glBindVertexArray(0);

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

Предварительная сцена

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

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

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

ссылка на оригинал статьи https://habrahabr.ru/post/329592/

Уязвимость в Web API Lycamobile позволяет управлять любым номером оператора

image
Доброго времени суток. Меня зовут Алексей. Я занимаюсь разработкой под .NET, pentesting и reverse engineering Android apps.
В марте от одного уважаемого человека мне поступил заказ на исследование приложения play.google.com/store/apps/details?id=com.lycamobile.myaccounts и описание его private API. После непродолжительных манипуляций я понял логику запросов и уж было приступил к описанию, но заметил что при смене номера телефона в запросе сервер продолжает отдавать данные. Оказалось что сервер просто не проверяет авторизованы ли мы и вся авторизация в приложении не более чем формальность.
Немного цифр. Lycamobile работает в 21 стране мира, капитализация €1.6 billion.
Данная уязвимость работает в uk, de, usa. Для остальных стран было лень проверять.
Команды API можно узнать там же на сервере по адресу maccount.lycamobile.co.uk/MyAccountsAPIGBR.svc (как удобно)
Сам запрос выглядит так

POST /MyAccountsAPIGBR.svc/JSON/GetAccountBalance HTTP/1.1
X-Titanium-Id: 28683211-fe39-471b-8b31-fb6babc37e0c
Content-Type: application/json
X-Requested-With: XMLHttpRequest
User-Agent: Appcelerator Titanium/3.2.1 (7 plus; Android API Level: 17; en-US;)
Host: maccount.lycamobile.co.uk
Connection: Keep-Alive
Cookie: ASP.NET_SessionId=zl3jse3wcjlapok2jhxma42u (необязательный параметр)
Cookie2: $Version=1
Content-Length: 84

{«CountryCode»:«GBR»,«LanguageCode»:«en»,«BrandCode»:«LYCA»,«MSISDN»:«44742493хххх»}

А в ответ на придет что-то вроде

HTTP/1.1 200 OK
Cache-Control: private
Content-Length: 106
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Fri, 26 May 2017 20:57:58 GMT
Proxy-Connection: Keep-alive

{«Response»:{«ResponseCode»:«0»,«ResponseDesc»:«Success»},«Balance»:«10.56»,«ValidityDate»:«17\/02\/2023»}

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

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

Я обращался в службу поддержки на fb и email, просил контакты их безопасников. Однако бестолку, мои сообщения либо игнорировали, либо отвечали стандартными фразами. Такое халатное отношение к юзерам особенно в бурже это нонсенс.
Благодарю за внимание. Надеюсь среди нашего сообщества найдутся люди которые покажут данный материал нужным людям и баг пофиксят.
ссылка на оригинал статьи https://habrahabr.ru/post/329586/

Почти манифест дизайн-мышления

Большинство людей готово безмерно трудиться, чтобы избавиться от необходимости немного подумать. Простые и полезные инструменты для анализа системы, поиска новых решений и создания уникальных продуктов лежат у нас под ногами – остается их только взять и использовать. В Программе «Единая Фронтальная система» мы генерим огромное количество идей. Выявлять самые жизнеспособные из них и успешно решать существующие проблемы помогает дизайн-мышление.

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

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

Продукт в экосистеме

Раньше все усилия были направлены только на то, чтобы делать продукт, сейчас взгляд поменялся. Теперь наверху есть клиент, и он взаимодействует с сервисами через touch points — точки касания. Когда вы пользуетесь айфоном или социальной сетью, вы взаимодействуете не напрямую с продуктом, а уже опосредованно — через сервис, который осуществляет различные виды операций: в случае банковского сервиса это, например, всевозможные транзакции.

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

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

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

В поисках инсайта

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

Эмпатия – это попытка залезть в шкуру клиента и посмотреть на проблему его глазами. Основной инструмент, который помогает в этом, – наблюдение.

При проведении эмпатии используются методы глубинного интервью: выявляются боли и потребности клиента, составляются персона–модели – своеобразные аватары, которые объединяют в себе потребности социальной группы или выборки определенных клиентов, потому что это качественный анализ. Конечно, мы не в состоянии опросить миллионы и сотни тысяч клиентов, обычно это 6-8 человек, представителей какой-то яркой возрастной или социальной таргет-группы.

На основе опросов составляются портреты пользователей, которые являются носителями чаяний, боли и потребностей определенной категории людей – пенсионеров, студентов, бизнесменов. Мы выявляем базовые потребности, потом переходим на анализ и синтез. У нас есть ворох различных инсайтов и потребностей. Что с этим делать?

Лучший способ — разложить эти потребности и спроектировать определенные истории. Нужно попытаться получить определенный сценарий путешествия пользователя по сервису, с учетом потребностей, с которыми он приходит, и проблем, с которыми он столкнулся. Как правило, уже во время интервью можно выйти на законченные логические пары. Потом их нужно переложить на «маршруты пользователей» (Сustomer journey). Это как своеобразный сценарий к фильму.

У хорошего кино есть две основные составляющие: хороший фильм управляет нашими эмоциями и делает это с определенным ритмом. «Маршруты пользователей» — это четко запрограммированное «проведение» человека по истории, по определенным сценариям. Хороший режиссер и сценарист знает: чтобы напугать, нужно сначала развеселить. Когда мы прорисовываем маршрут пользователя, мы обязательно учитываем «пред-сервис» (что происходит до), сам сервис и пост-сервис (то, с какими впечатлениями остается человек, когда уходит из сервиса).

Мы живем в век социальных платформ, и тот фидбэк, который клиент оставляет о сервисе, очень важен. Такой акцент не случаен. Как правило, разработчик и проектировщик обращают внимание на основную канву сервиса, когда человек непосредственно общается с представителями сервиса или с интерфейсом устройства. Но впечатление, которое вы получаете о фильме, очень часто складывается в конце. Поэтому мы прописываем эмоциональную карту, делаем своеобразную энцефалограмму: есть положительная, отрицательная и нейтральная реакции. Мы узнаем, какую реакцию демонстрируют пользователи в точках касания с сервисом: «я долго ждал очереди», «менеджер перенаправил меня не на ту стойку», прорисовываем эмоциональную карту, которая позволяет выровнять состояние клиента до идеального: от нейтрального к положительному, от положительного к нейтральному. Обязательно нужно «расстукивать» клиента после предоставления сервиса.

Следующий шаг — генерация идей. Многие участвовали в мозговых штурмах, в результате которых появляется ворох идей. Каким образом их проверить и отразить? И тут на помощь приходят методы прототипирования. Прототип – быстрый способ понять проблему на ранних стадиях, пока еще не успели углубиться в детали. Можно просто собрать прототип сервиса на коленке, сделать игровую карту и провести пользователя по ней, генерируя различные ситуации с помощью игровых костей. Этот способ активно применял Генрих Альтшуллер в своих методиках.

После этого тестируем и улучшаем. Любой процесс, будь то исследовательская деятельность, проектирование или дизайн-мышление, процесс интеграционный. Когда мы находим проблему, тут же возвращаемся на этап назад и собираем новые прототипы, снова тестируем и улучшаем. Но есть одна маленькая проблема – когда мы исследуем опыт пользователей, получаем интересные сеты с их потребностями, генерим идеи и понимаем, что сервисы стали лучше работать, мы их очеловечили, возникают вопросы. А где точка инновации? Что нового мы сделали? Появляется немая пауза, и люди говорят: «Не знаю».

А ведь делать новое – это инстинктивный процесс, который похож на эволюцию. Если ты это не сделаешь — сделает твой конкурент.

Новое создают, чтобы быть первым и на некоторое время почувствовать себя одиноким. Вы получаете внимание «бизнес-ангелов», маленькое временное преимущество, чтобы создать платформу, сделать имя и начать эту платформу развивать. Вы вырываетесь из круга жесткой конкуренции и совершаете маленький цивилизационный скачок. Нового делается не так много: мы находимся на пике креативной деятельности, но реально инновационных продуктов, которые меняют сценарии нашей жизни, открывают новые возможности, очень мало. 50-й Instagram и Telegram, тюнинговые варианты старых приложений не в счет.

Так идти на поводу у пользователей или нет?

Так как же сделать что-то новое? Может, взять за основу инсайты пользователей или маркетинговые исследования? Пообщаться с пользователями, выявить предпочтения. Так почему это никак не помогает делать новые вещи? По большому счету клиент ничего не может сказать нам о будущем, разумеется, если это не братья Стругацкие. Наткнуться на хорошего футуролога среди участников опроса сложно. Маркетинг вообще иногда поразительно относится к новшествам. Например, когда к маркетологам пришли создатели системы, которая послужила толчком к развитию плееров, они сказали: «Какой дурак будет ходить по улице с наушниками?»

Маркетинг – великолепный инструмент для анализа существующей реальности, для позиционирования продукта, но он не умеет придумывать новое.

В 90-х годах британский совет по дизайну провел исследование: опросили пользователей, сделали продукты, которые точно соответствовали ожиданиям клиентов, в итоге 80% новинок не имели успеха на рынке. Если мы полагаемся на «хотелки» пользователя, то заканчивается это не очень хорошо Основной инструмент для создания нового – интерпретация данных, на базе которой формируется основная стратегия системы развития, то есть мы анализируем рынок и создаем идеи реального продукта. У этого метода есть определенные недостатки.

Можете представить себе создателя Instagram, который спрашивал у бабушек, как часто они фотографируют еду в течение дня? Вряд ли. Сейчас говорят, что Стив Джобс подключал методы дизайн-мышления в разработке своих продуктов, но это абсолютно не так. Многие культовые продукты и сервисы были созданы вне маркетинговой парадигмы.

Первый закон для создания новых продуктов – закон об эволюции продукта – код эволюции продукта. Мы анализируем пользователей и проявляем эмпатию, но мы ищем вектор развития системы, код эволюции продукта, анализируя существующие системы.

Эти методы – креативные шаблоны. Вообще в креативе шаблоны, шаблонное мышление – плохое дело. Но мы не художники, дизайнер не самовыражается художественными методами и исследует реальность, занимается проектированием систем для решения насущных проблем пользователя, человечества, осознанным улучшением жизни, созданием новых сценариев.

Не пилить, а изобретать: креативные шаблоны

Первый человек, который нам поможет – Генрих Альтшуллер. В конце 40-х он был офицером каспийского флота, инженером, которого арестовали по ложному доносу, бросили в тюрьму, не давали спать. Включали свет в камере, шумели, будили. На третий день, когда он уже понимал, что не выдержит без сна на допросе, он придумал себе такую сказку, будто попал в заколдованное королевство, где страшная ведьма сказала, что если он закроет глаза, то никогда отсюда не выберется. Он нашел противоречие в этом, сказал себе: я не могу закрывать глаза, но я хочу спать. Как думаете, как он его решил? Он спал с закрытыми глазами, но нарисовал себе на веках глаза и просил своего сокамерника с ним разговаривать. И только на второй неделе это заметила женщина-надзиратель.

На базе этого умозаключения он построил ряд теорий. Он создал систему ТРИЗ – теория решения изобретательских задач. Она основана на ряде парадигм, одна из них заключается в том, что системы развиваются как законы физики, вне зависимости от наших желаний. У них просто есть параллель развития.

Одно из правил развития системы заключается в том, что идеальная система – та, которой нет, а функции ее выполняются. Система становится невидимой, превращаясь в подсистему. Хороший пример — ноутбук (печатная машинка, магнитофон, видеокамера – их нет, но функции выполняются).

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

Альтшуллер уделял большое внимание противоречиям. Он выделял административные, технические и физические. Административные противоречия — самые легко устраняемые, решение вопросов происходит на личном уровне с помощью переговоров, например.

Технические противоречия устраняются технико-инженерными методами. Физические противоречия – новый вид, самые сложные, но и они решаются. Альтшуллер считал, что качество решения идей зависит от уровня компромиссности разрешения противоречий. Чем больше компромиссов мы допускаем, тем меньше качество решений.

Следующий человек, о котором поговорим, — Томас Эдисон. Он говорил: «Нормальному изобретателю нужно немного таланта и свалка, в которую можно запустить руки». Под свалкой он подразумевал идеи конкурентов. Он был очень интересным человеком. Начав работать в 12 лет кондуктором, он устроил пожар и был уволен, стал телеграфистом, был уволен, потому что чуть не устроил катастрофу двух поездов, третировал своих жен, был очень ревнив к заслугам своих подчиненных, никогда не платил по счетам. При этом он был абсолютно гениальный человек. Его гениальность заключалась в следующем шаблоне – «Калитка Эдисона» или шаблон замещения.

Это классическая история из изобретательства. Однажды Эдисону сказал приятель: «Да у тебя вроде бы 1200 патентов, а калитка во дворе еле открывается. Ты хотя бы смазал бы ее или придумал что-нибудь». Эдисон сказал ему: «Ты знаешь, да, я просто подумал, что эта калитка могла бы служить отличным рычагом для помпы, которая накачивает воду ко мне в бассейн». Другими словами, люди входили к нему во двор, открывали калитку, которая была привязана к помпе и закачивала воду в бассейн.

Эта идея была основана на интересном принципе. Продукт, который мы создаем, не один, а окружен продуктами-конкурентами и постоянно развивается в определенной среде. Среда постоянно меняется, и мы эволюционируем вместе с ней. При этом может происходить эволюция, а может и происходить мутация. ДНК-это эволюционный код продукта, это тот сверхсмысл, который заложен в самой идее продукта. И он тоже постоянно развивается, если он это не сделает, то мы погибнем. А что такое внешняя среда для нашего продукта? Это культурный контекст, клиенты, политический фон, технологии, информационная среда, экономика.

Полезно начертить схему системы, ввести в нее окружение, заменить внутренний компонент на окружение и сделать частью системы. Так же можно делать новые сервисы: смотреть на окружение и вводить его в систему. Это применимо практически в любых ситуациях.

Напоследок разберем важный кейс с Domino’s Pizza. Это достаточно старый поставщик пиццы, и он ничем не отличался от других, пока не ввел принцип: «Если мы не доставим пиццу вовремя, стоимость доставки снизится в два раза». До этого все доставляли вкусную пиццу, но никто не менял стоимость доставки в зависимости от срока. Это подход независимых атрибутов: цена не цена и время – это независимые атрибуты системы, они могут быть внутренние и внешние. А мы организуем между ними зависимость. Цена меняется в зависимости от времени доставки.

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

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

Итоги и советы

Сегодня мы рассказали о базовых креативных шаблонах, которые, скорее всего, вы постоянно применяете в своей деятельности. Поговорили о важности противоречий, о системе замещения, о шаблоне атрибутов. Мы сможем обсудить с вами это более подробно 3 июня на небольшом воркшопе, который состоится в рамках IT-конференции «Продвижение» в Телеграфе, сможем детальнее рассмотреть работу с шаблонами применительно к методам функционального анализа (это уже более углубленный метод проектирования функций системы) и возможности проектирования шаблонов (как простых методов) на более детальном методе разработки.

День в Телеграфе

Кстати, день в Телеграфе обещает быть насыщенным и полезным.

Финалисты технологического конкурса «Продвижение» продолжат борьбу за победу. Помимо презентаций команд, участников ждут выступления лидеров IT-индустрии по актуальным темам мира технологий и мастер-классы. Откроют конференцию выступления представителей топ-менеджмента «Сбербанка». Для профессионалов IT будут организованы выступления экспертов по темам front-end и back-end разработки. В экспозоне в течение дня для участников будут проходить различные демо в области искусственного интеллекта, виртуальной и дополненной реальности, а также будут представлены прототипы на основе технологии биометрии.

Студенты и соискатели смогут узнать о возможностях стажировки и развития карьеры в «Сбербанке» и  «СберТехе», заполнить анкеты и пройти собеседование на замещение вакантных позиций.

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

Книги для мышления и размышления

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

Брюс Стерлинг «Shaping Things» — известный фантаст, владеет футурологической лабораторией, в которой они исследуют будущее вещей. В книге рассматривает аспекты совершенствования системы в такой парадигме, что человек не создатель системы, а инструмент для ее совершенствования. У системы своя внутренняя логика жизни, и человек – творец теряет свою функцию и становится таким инструментом для совершенствования системы

Классика, читать обязательно: Альтшуллер Г.С. Алгоритм изобретения. Создал матрицу ТРИЗ, его методы легли в основу многих методов современных методов проектирования.

В книге «Creativity in product innovation» более детально рассматриваются аспекты применения креативных шаблонов, это ученики Альтшуллера, они активно развивают его теории.

Роберт Персинг «Дзен и искусство ухода за мотоциклом» — рекомендуем прочитать всем обязательно, культовая книга 70-х годов о философском аспекте восприятия мира через концепцию лирика и физика.

Еще советуем посмотреть фильм Кубрика «Космическая Одиссея» 2001 г.

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

А вы используете дизайн-мышление? Не стесняйтесь и задавайте свои вопросы, будем рады пообщаться с вами в комментариях к статье.
ссылка на оригинал статьи https://habrahabr.ru/post/329560/

Создание веб-приложения на Go в 2017 году. Часть 2

Содержание

  1. Часть 1
  2. Часть 2
  3. Часть 3
  4. Часть 4

Итак, наше приложение будет иметь две основные части: клиентская и серверная. (Какой сейчас год?). Серверная часть будет на Go, а клиентская — на JS. Давайте сначала поговорим о серверной части.

Go (сервер)

Серверная часть нашего приложения будет ответственной за первоначальное обслуживание всего необходимого для JavaScript и всего остального, типа статических файлов и данных в формате JSON. Это все, всего две функциональности: (1) статика и (2) JSON.

Стоит отметить, что обслуживание статики опционально: статику можно обслуживать в CDN, например. Но важно то, что это не проблема для нашего Go приложения — в отличие от Python/Ruby приложения, оно может работать наравне с Ngnix и Apache, обслуживающими статику. Делегирование раздачи статических файлов какому-то другому приложению для облегчения нагрузки особо не требуется, хотя и имеет смысл в некоторых ситуациях.

Для упрощения давайте представим, что мы создаем приложение, которое обслуживает список людей (только имена и фамилии), хранящийся в таблицы базы данных, и все. Код находится здесь — https://github.com/grisha/gowebapp.

Структура каталогов

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

Для веб-приложения, на мой взгляд, имеет смысл такой макет:

# github.com/user/foo  foo/            # package main   |   +--daemon/    # package daemon   |   +--model/     # package model   |   +--ui/        # package ui   |   +--db/        # package db   |   +--assets/    # здесь хранятся файлы JS и остальная статика

Верхний уровень: пакет main

На верхнем уровне у нас расположен пакет main, а его код — в файле main.go. Главный плюс в том, что при таком раскладе go get github.com/user/foo — это единственная команда, требуемая для установки всего приложения в $GOPATH/bin.

Пакет main должен быть минимальным, насколько это возможно. Единственный код, который тут находится, — это анализ аргументов команды. Если у приложения был бы конфигурационный файл, я поместил бы парсинг и проверку этого файла в еще один пакет, который, скорее всего, назвал бы config. После этого main должен передать управление пакету daemon.

Вот основа main.go:

package main  import (     "github.com/user/foo/daemon" )  var assetsPath string  func processFlags() *daemon.Config {     cfg := &daemon.Config{}      flag.StringVar(&cfg.ListenSpec, "listen", "localhost:3000", "HTTP listen spec")     flag.StringVar(&cfg.Db.ConnectString, "db-connect", "host=/var/run/postgresql dbname=gowebapp sslmode=disable", "DB Connect String")     flag.StringVar(&assetsPath, "assets-path", "assets", "Path to assets dir")      flag.Parse()     return cfg }  func setupHttpAssets(cfg *daemon.Config) {     log.Printf("Assets served from %q.", assetsPath)     cfg.UI.Assets = http.Dir(assetsPath) }  func main() {     cfg := processFlags()      setupHttpAssets(cfg)      if err := daemon.Run(cfg); err != nil {         log.Printf("Error in main(): %v", err)     } }

Приведенный код принимает три параметра: -listen, -db-connect и -assets-path, ничего особенного.

Использование структур для ясности

В строке cfg := &daemon.Config{} мы создаем объект daemon.Config. Его основная цель — представить конфигурацию в структурированном и понятном формате. Каждый из наших пакетов определяет свой собственный тип Config, описывающий необходимые ему параметры, и который может включать в себя настройки других пакетов. Мы видим пример этого в processFlags() выше: flag.StringVar(&cfg.Db.ConnectString, .... Здесь db.Config включен в daemon.Config. На мой взгляд, это очень полезный прием. Использование структур также оставляет возможность сериализации настроек в виде JSON, TOML или еще чего-нибудь.

Использование http.FileSystem для обслуживания статики

http.Dir(assetsPath) в setupHttpAssets — это подготовка к тому, как мы будем обслуживать статику в пакете ui. Сделано это именно так, чтобы оставить возможность для другой реализации cfg.UI.Assets (который является интерфейсом http.FileSystem), например, отдавать этот контент из оперативной памяти. Я расскажу об этом более детально позже, в отдельном посте.

В конце концов, main вызывает daemon.Run(cfg), который фактически запускает наше приложение и блокируется до момента завершения работы.

Пакет daemon

Пакет daemon содержит все, что связано с запуском процесса. Сюда относится, например, какой порт будет прослушиваться, здесь будет определен пользовательский журнал, а также все, что связано с вежливым перезапуском и т.д.

Поскольку задачей пакета daemon является инициализация подключения к базе данных, ему нужно импортировать пакет db. Он также отвечает за прослушивание TCP порта и запуск пользовательского интерфейса для этого слушателя, поэтому ему необходимо импортировать пакет ui, а поскольку пакету ui необходимо иметь доступ к данным, который обеспечивается пакетом model, ему также необходимо импортировать пакет model.

Скелет модуля daemon выглядит примерно так:

package daemon  import (     "log"     "net"     "os"     "os/signal"     "syscall"      "github.com/grisha/gowebapp/db"     "github.com/grisha/gowebapp/model"     "github.com/grisha/gowebapp/ui" )  type Config struct {     ListenSpec string      Db db.Config     UI ui.Config }  func Run(cfg *Config) error {     log.Printf("Starting, HTTP on: %s\n", cfg.ListenSpec)      db, err := db.InitDb(cfg.Db)     if err != nil {         log.Printf("Error initializing database: %v\n", err)         return err     }      m := model.New(db)      l, err := net.Listen("tcp", cfg.ListenSpec)     if err != nil {         log.Printf("Error creating listener: %v\n", err)         return err     }      ui.Start(cfg.UI, m, l)      waitForSignal()      return nil }  func waitForSignal() {     ch := make(chan os.Signal)     signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)     s := <-ch     log.Printf("Got signal: %v, exiting.", s) }

Обратите внимание, Config включает db.Config и ui.Config, как я уже упоминал.

Все действие происходит в Run(*Config). Мы инициализируем соединение с базой данных, создаем экземпляр model.Model и запускаем ui, передавая ему настройки, указатели на модель и слушателя.

Пакет model

Назначение model заключается в отделении того, как данные хранятся в базе данных от ui, а также в обеспечении бизнес-логики, которую может иметь приложение. Это мозг вашего приложения, если угодно.

Пакет model должен определить структуру (Model выглядит подходящим названием), а указатель на экземпляр этой структуры должен быть передан всем функциям и методам ui. В нашем приложении должен быть только один такой экземпляр — для дополнительной уверенности вы можете реализовать это программно с помощью синглтона, но я не думаю, что это так уж необходимо.

В качестве альтернативы, вы можете обойтись без структуры Model и просто использовать сам пакет model. Мне не нравится такой подход, тем не менее это вариант.

Модель также должна определять структуры для сущностей данных, с которыми мы имеем дело. В нашем примере это будет структура Person. Его члены должны быть экспортированы (именованы с заглавной буквы), потому что другие пакеты будут к ним обращаться. Если вы используете sqlx, здесь же необходимо указать тэги, которые привязывают элементы структуры к названиям колонок в БД, например db:"first_name".

Наш тип Person:

type Person struct {     Id          int64     First, Last string }

Тут нам не нужны тэги, потому что имена колонок соответствуют именам элементов структуры, а sqlx позаботится о регистре так, что Last соответствует колонке с именем last.

Пакет model НЕ должен импортировать db

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

type db interface {     SelectPeople() ([]*Person, error) }

Наше приложение делает не очень много, но мы знаем, что в нем перечислены люди, поэтому наша модель, скорее всего, должна иметь метод People() ([]*Person, error):

func (m *Model) People() ([]*Person, error) {     return m.SelectPeople() }

Чтобы все было аккуратно, код лучше размещать в разных файлах, например структура Person должна быть определена в person.go, и т.д. Но для удобочитаемости здесь представлена однофайловая версия нашего пакета model:

package model  type db interface {     SelectPeople() ([]*Person, error) }  type Model struct {     db }  func New(db db) *Model {     return &Model{         db: db,     } }  func (m *Model) People() ([]*Person, error) {     return m.SelectPeople() }  type Person struct {     Id          int64     First, Last string }

Пакет db

db — это фактическая реализация взаимодействия с базой данных. Здесь конструируются и исполняются операторы SQL. Этот пакет также импортирует model, п.ч. он должен будет создать эти структуры из данных базы данных.

В первую очередь, db должен предоставить функцию InitDB, которая установит соединение с базой данных, а также создаст необходимые таблицы и подготовит SQL запросы.

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

Мы используем PostgreSQL, что означает, что нам необходимо импортировать драйвер pq. Мы также будем полагаться на sqlx и нам нужна наша model. Вот начало реализации нашего db:

package db  import (     "database/sql"      "github.com/grisha/gowebapp/model"     "github.com/jmoiron/sqlx"     _ "github.com/lib/pq" )  type Config struct {     ConnectString string }  func InitDb(cfg Config) (*pgDb, error) {     if dbConn, err := sqlx.Connect("postgres", cfg.ConnectString); err != nil {         return nil, err     } else {         p := &pgDb{dbConn: dbConn}         if err := p.dbConn.Ping(); err != nil {             return nil, err         }         if err := p.createTablesIfNotExist(); err != nil {             return nil, err         }         if err := p.prepareSqlStatements(); err != nil {             return nil, err         }         return p, nil     } }

Экспортируемая функция InitDb() создает экземпляр pgDb, который является Postgres-реализацией нашего интерфейса model.db. Он содержит все необходимое для связи с базой данных, включая подготовленные запросы, и реализует необходимые интерфейсу методы.

type pgDb struct {     dbConn *sqlx.DB      sqlSelectPeople *sqlx.Stmt }

Ниже приведен код для создания таблиц и подготовки запросов. С точки зрения SQL тут все довольно упрощенно и, конечно, есть куда совершенствовать:

func (p *pgDb) createTablesIfNotExist() error {     create_sql := `         CREATE TABLE IF NOT EXISTS people (        id SERIAL NOT NULL PRIMARY KEY,        first TEXT NOT NULL,        last TEXT NOT NULL);      `     if rows, err := p.dbConn.Query(create_sql); err != nil {         return err     } else {         rows.Close()     }     return nil }  func (p *pgDb) prepareSqlStatements() (err error) {      if p.sqlSelectPeople, err = p.dbConn.Preparex(         "SELECT id, first, last FROM people",     ); err != nil {         return err     }      return nil }

Наконец, нам нужно предоставить метод, реализующий интерфейс:

func (p *pgDb) SelectPeople() ([]*model.Person, error) {     people := make([]*model.Person, 0)     if err := p.sqlSelectPeople.Select(&people); err != nil {         return nil, err     }     return people, nil }

Здесь мы используем преимущество sqlx для выполнения запроса и построения слайса из результатов, просто вызывая Select() (Обратите внимание: p.sqlSelectPeople имеет тип *sqlx.Stmt). Без sqlx нам надо было бы итерироваться по строкам результата, обрабатывая каждую с помощью Scan, что получилось бы более многословно.

Остерегайтесь одного очень тонкого момента. people можно было бы определить как var people []*model.Person и метод работал бы точно так же. Однако, если база данных вернет пустой набор строк, метод вернет nil, а не пустой слайс. Если результат данного метода позднее будет закодирован в JSON, то он станет null, а не []. Это может вызвать проблемы, если клиентская сторона не знает, как обращаться с null.

Вот и все для db.

Пакет ui

В конце концов, нам нужно обслуживать все это через HTTP, и это именно то, что делает пакет ui.

Вот очень упрощенный вариант:

package ui  import (     "fmt"     "net"     "net/http"     "time"      "github.com/grisha/gowebapp/model" )  type Config struct {     Assets http.FileSystem }  func Start(cfg Config, m *model.Model, listener net.Listener) {      server := &http.Server{         ReadTimeout:    60 * time.Second,         WriteTimeout:   60 * time.Second,         MaxHeaderBytes: 1 << 16}      http.Handle("/", indexHandler(m))      go server.Serve(listener) }  const indexHTML = ` <!DOCTYPE HTML> <html>   <head>     <meta charset="utf-8">     <title>Simple Go Web App</title>   </head>   <body>     <div id='root'></div>   </body> </html> `  func indexHandler(m *model.Model) http.Handler {     return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {         fmt.Fprintf(w, indexHTML)     }) }

Обратите внимание, что indexHTML почти ничего не содержит. Это почти на 100% весь HTML, который будет использовать наше приложение. Он немного изменится, когда мы приступим к клиентской части приложения, буквально на несколько строк.

Также следует отметить как определяется обработчик. Если эта идиома вам не знакома, стоит потратить несколько минут (или день), чтобы усвоить ее полностью, поскольку она очень распространена в Go. indexHandler() — это не сам обработчик, он возвращает функцию-обработчик. Это делается таким образом для того, чтобы мы могли передать *model.Model через замыкание, так как сигнатура функции-обработчика HTTP фиксирована и указатель на модель не является одним из ее параметров.

Пока мы в indexHandler() ничего не делаем с указателем на модель, но когда дойдем до фактической реализации списка людей, он нам понадобится.

Заключение

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

ссылка на оригинал статьи https://habrahabr.ru/post/329584/

Создание веб-приложения на Go в 2017 году

Содержание

  1. Часть 1
  2. Часть 2
  3. Часть 3
  4. Часть 4

Пару недель назад я начал разрабатывать еще одно веб-приложение, только уже на 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] (https://ru.wikipedia.org/wiki/Global_interpreter_lock), легкость, с которой они решают проблему генерации 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 в качестве БД.
  • Одностраничное приложение.

ссылка на оригинал статьи https://habrahabr.ru/post/329582/