Русский
Русский
English
Статистика
Реклама

Разработка

Перевод Управление зависимостями в Node.js

04.06.2021 16:20:58 | Автор: admin
Управление зависимостями это часть повседневной работы Node.js-программиста. Сегодня мы поговорим о разных подходах к работе с зависимостями в Node.js, и о том, как система загружает и обрабатывает зависимости.

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



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

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

Node.js поддерживает разные способы работы с модулями. В частности, одна из них, основанная на CommonJS, предусматривает применение ключевого слова require. При её использовании, перед тем, как некий функционал окажется доступным в программе, у платформы нужно затребовать подключение этого функционала.

Я исхожу из предположения о том, что вы уже владеете основами Node.js. Если нужно можете, прежде чем продолжать читать этот материал, посмотреть мою статью, посвящённую основам Node.js.

Подготовка приложения и эксперименты по экспорту и импорту


Начнём с простых вещей. Я создал директорию для проекта и, используя команду npm init, инициализировал проект. Затем я создал два JavaScript-файла: app.js и appMsgs.js. Ниже показан внешний вид структуры проекта в VS Code. Этот проект мы будем использовать в роли отправной точки наших экспериментов. Вы можете, прорабатывая этот материал, делать всё сами, а можете упростить себе работу, воспользовавшись готовым кодом. Его можно найти в репозитории, ссылку на который я приведу в конце статьи.

Структура базового проекта

В данный момент оба .js-файла пусты. Внесём в файл appMsgs.js следующий код:


Экспорт значений простых типов и объектов в appMsgs.js

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

В app.js воспользоваться тем, что экспортировано из appMsgs.js, можно, прибегнув к команде require:


Импорт модуля appMsgs.js в app.js

Система, выполнив команду require, вернёт объект, который будет представлять обособленный фрагмент кода, описанный в файле appMsgs.js. Мы назначаем этот объект переменной appMsgs, а затем просто пользуемся свойствами этого объекта в вызовах console.log. Ниже показан результат выполнения кода app.js.


Выполнение app.js

Команда require выполняет код файла appMsgs.js и конструирует объект, дающий нам доступ к функционалу, экспортируемому файлом appMsgs.js.

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

В результате оказывается, что мы, пользуясь конструкциями require и module.exports, можем создавать модульные приложения.

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

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


Экспорт функции из appMsgs.js

Теперь мы экспортируем из appMsgs.js функцию. Код этой функции выполняется каждый раз, когда код, импортировавший её, её вызывает.

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


Использование импортированной функции в app.js

Тут мы пользуемся тем, что, после экспорта, попадает в переменную appMsgs, как функцией. В результате оказывается, что каждый раз, когда мы вызываем импортированную функцию, её код выполняется.

Вот результат запуска этого кода:


Выполнение app.js

Мы рассмотрели два подхода к использованию module.exports. Ещё одним способом применения module.exports является экспорт функций-конструкторов, используемых, с ключевым словом new, для создания объектов. Рассмотрим пример:


Экспорт функции-конструктора из appMsgs.js

А вот обновлённый код app.js, в котором используется импортированная функция-конструктор:

Использование в app.js функции-конструктора, импортированной из appMsgs.js

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

Вот что получится, если выполнить новый вариант app.js:


Выполнение app.js

Я добавил в проект файл userRepo.js и внёс в него следующий код:


Файл userRepo.js

Вот файл app.js, в котором используется то, что экспортировано из userRepo.js:


Использование в app.js того, что экспортировано из userRepo.js

Запустим app.js:


Выполнение app.js

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

Импорт директорий


Давайте ненадолго вернёмся к тому, о чём мы уже говорили. Вспомним о том, как require используется для импорта зависимостей:

var appMsgs = require("./appMsgs")

Node.js, выполняя эту команду, будет искать файл appMsgs.js, но систему будет интересовать и директория appMsgs. То, что она найдёт первым, она и импортирует.

Теперь давайте посмотрим на код, в котором используется эта возможность.

Я создал папку logger, а в ней файл index.js. В этот файл я поместил следующий код:


Код файла index.js из папки logger

А вот файл app.js, в котором команда require используется для импорта этого модуля:


Файл app.js, в котором require передаётся не имя файла, а имя папки

В данном случае можно было бы воспользоваться такой командой:

var logger = require("./logger/index.js")

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

var logger = require("./logger")

Так как система не может обнаружить файл logger.js, она ищет соответствующую папку. По умолчанию импортируется файл index.js, являющейся точкой входа в модуль. Именно поэтому я и дал .js-файлу, находящемуся в папке, имя index.js.

Попробуем теперь выполнить код app.js:


Выполнение app.js

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

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

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

Npm


Мы кратко поговорим и о ещё одном аспекте работы с зависимостями в Node.js. Это npm (Node Package Manager, менеджер пакетов Node.js). Вы, вероятно, уже знакомы с npm. Если кратко описать его суть, то окажется, что он даёт разработчикам простой механизм для включения в их проекты необходимого им функционала, оформленного в виде npm-пакетов.

Установить нужную зависимость с помощью npm (в данном случае библиотеку underscore) можно так:

npm install underscore

Потом эту библиотеку можно подключить в коде с помощью require:


Импорт библиотеки, установленной с помощью npm

На предыдущем рисунке показан процесс работы с тем, что оказалось в нашем распоряжении после импорта пакета underscore. Обратите внимание на то, что при импорте этого модуля путь к файлу не указывают. Используют лишь имя модуля. Node.js загружает его из папки node_modules, находящейся в директории приложения.


Пример использования underscore в app.js

Выполним этот код.


Выполнение app.js

Итоги


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

Применяете ли вы модульный подход при работе над своими Node.js-проектами?


Подробнее..

Перевод Ptpython улучшенный REPL для Python

05.06.2021 14:06:41 | Автор: admin
Возникало ли у вас когда-нибудь желание быстро испытать какую-нибудь свежую идею, прибегнув к интерфейсу командной строки Python, к REPL? Вероятно, если речь идёт об эксперименте буквально с несколькими строками кода, вам просто не захочется создавать для этого новый блокнот Jupyter.

Но в подобной ситуации, возможно, вас не особенно порадует и перспектива использования классической консоли Python, так как она, в отличие от Jupyter Notebook, не поддерживает автодополнение ввода и не умеет работать с документационными строками. В REPL, кроме того, нельзя, после нажатия на Enter, исправлять ошибки в коде.

Что если можно было бы превратить довольно-таки скучную командную строку Python в многофункциональный инструмент, вроде того, запись работы с которым показана ниже?


Продвинутая командная строка Python

Собственно, именно на тех, у кого возникает подобное желание, и ориентирован проект ptpython.

Что такое ptpython?


Ptpython можно назвать улучшенным интерфейсом командной строки Python. Установить его можно так:

pip install ptpython

А для того чтобы его запустить достаточно воспользоваться следующей командой:

ptpython

Возможности по вводу данных


Проверка вводимых данных


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


Ошибка, допущенная в обычной командной строке Python

Но ptpython позволяет проверить то, что введено с клавиатуры, ещё до нажатия на Enter. На следующем анимированном изображении показано, что пропущенная закрывающая скобка вызывает появление сообщения об ошибке. Эту ошибку можно тут же исправить.


Исправление ошибки при работе в ptpython

Автодополнение ввода, основанное на исторических данных


Если вы когда-нибудь подумывали о том, что при работе с командной строкой Python не помешали бы возможности по автодополнению ввода, основанному на исторических данных, то знайте, что ptpython это поддерживает.


Автодополнение ввода, основанное на исторических данных

Но эта возможность ptpython, по умолчанию, не включена. Правда, для того чтобы включить её, достаточно, воспользовавшись клавишей F2, вызвать меню, в котором, пользуясь клавишами-стрелками, надо найти опцию Auto suggestion и перевести её в состояние on. Для закрытия меню надо нажать на Enter.


Включение автодополнения ввода

После включения опции Auto suggestion у вас должно заработать автодополнение ввода, основанное на истории. Для того чтобы воспользоваться тем, что предлагает ptpython, достаточно нажать клавишу-стрелку Вправо.

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


Если при работе с объектом ввести точку будет выведен список его свойств и методов.


Подсказки, выводимые после ввода точки

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

Вставка данных из истории команд


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

После того, как выбор нужного участка кода завершён, достаточно нажать на Enter и соответствующий код будет вставлен в рабочую область.


Копирование кода из панели истории

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

Режим вставки


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


Работа в обычной командной строке Python

А ptpython позволяет редактировать вставленный код, доводя его до нужного состояния.


Редактирование вставленного кода в ptpython

Для того чтобы включить режим вставки достаточно нажать на клавишу F6. Когда этот режим активирован код, при нажатии на Enter, выполняться не будет. А после того, как код будет готов к выполнению нужно снова нажать F6 для выключения режима вставки, а потом дважды нажать на Enter.

Возможности по выводу данных


Просмотр сигнатур функций и документационных строк


Ptpython позволяет просматривать сведения о параметрах функций и конструкторов.


Просмотр сведений о конструкторе DataFrame

Ещё можно смотреть документационные строки классов и функций. Для включения этой возможности нужно открыть меню (F2), а потом включить опцию Show docstring.


Включение вывода документационных строк

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


Вывод документации

Выделение парных скобок


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


Выделение парных скобок

Добавление пустой строки после введённых или выведенных данных


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


Улучшение читабельности кода за счёт пустых строк

Для того чтобы включить эту возможность нужно, вызвав меню клавишей F2, включить опции Blank line after input и Blank line after output.


Включение опций Blank line after input и Blank line after output

Выделение синтаксических конструкций


Ptpython, кроме прочего, поддерживает подсветку синтаксиса.


Подсветка синтаксиса

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

В системе имеется 39 тем. Если, например, вам хочется выбрать такую же цветовую схему, которая используется в Sublime Text знайте, что она имеет код monokai. Этот код нужно ввести в опции меню Code, которую можно найти в разделе Colors.


Настройка темы в меню

Магические команды IPython


Ptpython поддерживает магические команды IPython. Для того чтобы получить доступ к возможностям IPython, нужно воспользоваться командой ptipython.


Возможности IPython


Возможности IPython

Настройка ptpython


Те изменения, которые вносят в настройки ptpython во время работы, исчезают после окончания сеанса работы с программой.

Настройки, которые используются в каждом сеансе, должны быть описаны в файле $XDG_CONFIG_HOME/ptpython/config.py. В Linux путь к нему выглядит как ~/.config/ptpython/config.py.

Вот файл, который содержит все те полезные настройки программы, о которых мы говорили выше.

Итоги


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

Планируете ли вы пользоваться ptpython?


Подробнее..

Перевод Тест пропускной способности ASP.NET Core 5.0 в Kestrel, IIS, Nginx и Caddy

05.06.2021 18:10:05 | Автор: admin
Начиная с версии 2.2. ASP.NET Core поддерживает режим внутрипроцессного размещения приложения (InProcess) в IIS, направленный на улучшение производительности кода. Рик Страл написал статью, в которой подробно исследовал эту тему. С тех пор прошло три года, теперь платформа ASP.NET Core добралась до версии 5.0. Как это повлияло на производительность ASP.NET Core-проектов на различных серверах?



Результаты исследования Рика Страла


Рик Страл в своей статье занимался тестирование ASP.NET Core-кода на Windows в Kestrel и в IIS (в режимах InProcess и OutOfProcess). Его интересовало количество запросов в секунду, обрабатываемых системой. В результате он пришёл к выводу о том, что первое место по производительности получает использование IIS в режиме InProcess, второе Kestrel, третье IIS в режиме OutOfProcess.

Обзор эксперимента


Рик не провёл испытания, позволяющие выявить различия в выполнении ASP.NET Core-кода на Windows- и на Linux-серверах. А вопрос о том, что в 2021 году лучше выбрать для проектов, основанных на ASP.NET Core 5.0, интересует многих из тех, кого я знаю. Поэтому я решил, используя подход к тестированию, похожий на тот, которым пользовался Рик, узнать о том, сколько запросов в секунду может обработать ASP.NET Core 5.0-приложение на Windows и на Linux.

Тестовое окружение


Так как Windows 10, Ubuntu Desktop и другие настольные операционные системы не в полной мере отражают особенности сопоставимых с ними серверных дистрибутивов, я решил выбрать для тестов серверные версии соответствующих ОС. Это были свежеустановленные копии систем с последними патчами, перед выполнением тестов они были один раз перезагружены.

Windows-сервер:


  • Провайдер: Microsoft Azure East Asia Region.
  • ОС: Windows Server 2019 Data Center.
  • Характеристики системы: B2S / 2 vCPU, 4GB RAM, Premium SSD.
  • Окружение: IIS с поддержкой статического и динамического сжатия контента, отсутствие интеграции с ASP.NET 3.5 или 4.x, на сервере установлена платформа ASP.NET Core 5.0.2 Runtime.


Сведения о Windows-сервере

Linux-сервер


  • Провайдер: Microsoft Azure East Asia Region.
  • ОС: Ubuntu Server 20.04 LTS.
  • Характеристики системы: B2S / 2 vCPU, 4GB RAM, Premium SSD.
  • Окружение: включено использование BBR, установлены Nginx, Caddy и ASP.NET Core 5.0.2 Runtime.


Сведения о Linux-сервере

Инструменты для проведения тестов


Рик пользовался West Wind Web Surge, но этот инструмент доступен только на платформе Windows, а это нас не устроит. Я решил воспользоваться опенсорсным кросс-платформенным инструментом bombardier, о котором однажды писали в официальном .NET-блоге Microsoft.

Тестовое приложение


Я создал новый проект ASP.NET Core 5.0 Web API, в котором имеется лишь один метод:

[ApiController][Route("[controller]")]public class TestController : ControllerBase{[HttpGet]public string Get(){return $"Test {DateTime.UtcNow}";}}

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

Проект скомпилирован с применением конфигурации Release и опубликован с использованием FDD. Настройки логирования оставлены в стандартном состоянии:

"LogLevel": {"Default": "Information","Microsoft": "Warning","Microsoft.Hosting.Lifetime": "Information"}

Методика тестирования


Я запускал проект, используя следующие конфигурации:

  • Kestrel.
  • IIS в режиме InProcess.
  • IIS в режиме OutOfProcess.
  • Обратный прокси Nginx.
  • Обратный прокси Caddy.

Затем я применял bombardier. В течение 10 секунд, по 2 соединениям, велась работа с конечной точкой, доступной на localhost. После прогревочного раунда испытаний я, друг за другом, проводил ещё 3 раунда и на основе полученных данных вычислял показатель количества запросов, обработанных исследуемой системой за секунду (Request per Second, RPS).

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

Результаты тестов


Windows + Kestrel


Средний RPS: 18808


Windows + Kestrel

Windows + IIS в режиме InProcess


Средний RPS: 10089


Windows + IIS в режиме InProcess

Windows + IIS в режиме OutOfProcess


Средний RPS: 2820


Windows + IIS в режиме OutOfProcess

Linux + Kestrel


Средний RPS: 10667


Linux + Kestrel

Linux + Nginx


Средний RPS: 3509


Linux + Nginx

Linux + Caddy


Средний RPS: 3485


Linux + Caddy

Итоги


Вот как выглядят результаты тестирования (от самой быстрой комбинации ОС и серверного ПО до самой медленной):

  • Windows + Kestrel (18808)
  • Linux + Kestrel (10667)
  • Windows + IIS в режиме InProcess (10089)
  • Linux + Nginx (3509)
  • Linux + Caddy (3485)
  • Windows + IIS в режиме OutOfProcess (2820)

Мои результаты отличаются от тех, что получил Рик, тестируя ASP.NET Core 2.2-проект. В его тестах производительность IIS в режиме InProcess оказывалась выше, чем производительность Kestrel. Но сейчас Kestrel оказывается быстрее IIS в режиме InProcess, и это кажется вполне логичным и ожидаемым.

А вот неожиданностью для меня стало то, что производительность Windows-серверов c Kestrel оказалась выше производительности аналогичных Linux-серверов. Это меня удивило.

В режиме обратного прокси-сервера Nginx и Caddy, в общем-то, показывают одинаковую производительность. И та и другая конфигурации обходят IIS в режиме OutOfProcess.

Конечно, мой простой тест, в ходе которого сервер возвращает обычную строку, не позволяет проверить все нюансы производительности ASP.NET Core 5.0 и исследовать возможности каждого сервера. В реальных проектах присутствует множество факторов, которые воздействуют на производительность. План моего эксперимента не перекрывает все возможные сценарии использования ASP.NET Core 5.0, в нём, наверное, имеются какие-то недочёты. Если вы что-то такое заметите дайте мне знать.

Дополнение


Мне сообщили, что в .NET 5 у DateTime.UtcNow могут быть проблемы, касающиеся производительности. Я провёл повторные испытания, заменив эту конструкцию на Activity.Current?.Id ?? HttpContext.TraceIdentifier. Получившиеся у меня результаты были выражены немного более высокими показателями, но общая картина осталась почти такой же.

А если увеличить число подключений с 2 до 125 сервер Kestrel, и на Windows, и на Linux, способен дать гораздо более высокую пропускную способность.

Каким серверным ПО вы пользуетесь в своих проектах?


Подробнее..

Перевод Разработка REST-серверов на Go. Часть 2 применение маршрутизатора gorillamux

06.06.2021 14:21:06 | Автор: admin
Перед вами второй материал из серии статей, посвящённой разработке REST-серверов на Go. В первом материале этой серии мы создали простой сервер, пользуясь стандартными средствами Go, а после этого отрефакторили код формирования JSON-данных, вынеся его во вспомогательную функцию. Это позволило нам выйти на достаточно компактный код обработчиков маршрутов.

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



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

Улучшенная система маршрутизации


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

Перед переходом к практическому примеру вспомним о том, как устроен API нашего сервера:

POST  /task/       : создаёт задачу и возвращает её IDGET  /task/<taskid>   : возвращает одну задачу по её IDGET  /task/       : возвращает все задачиDELETE /task/<taskid>   : удаляет задачу по IDGET  /tag/<tagname>   : возвращает список задач с заданным тегомGET  /due/<yy>/<mm>/<dd> : возвращает список задач, запланированных на указанную дату

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

  1. Можно создать механизм, позволяющий задавать отдельные обработчики для разных методов одного и того же маршрута. Например запрос POST /task/ должен обрабатываться одним обработчиком, а запрос GET /task/ другим.
  2. Можно сделать так, чтобы обработчик маршрута выбирался бы на основе более глубокого, чем сейчас, анализа запросов. То есть, например, у нас при таком подходе должна быть возможность указать, что один обработчик обрабатывает запрос к /task/, а другой обработчик обрабатывает запрос к /task/<taskid> с числовым ID.
  3. При этом система обработки маршрутов должна просто извлекать числовой ID из /task/<taskid> и передавать его обработчику каким-нибудь удобным для нас способом.

Написание собственного маршрутизатора на Go это очень просто. Это так из-за того, что организовывать работу с HTTP-обработчиками можно, используя компоновку. Но тут я не стану потакать своему желанию написать всё самому. Вместо этого предлагаю поговорить о том, как организовать систему маршрутизации с использованием одного из самых популярных маршрутизаторов, который называется gorilla/mux.

Сервер приложения для управления задачами, использующий gorilla/mux


Пакет gorilla/mux представляет собой один из самых старых и самых популярных HTTP-маршрутизаторов для Go. Слово mux, в соответствии с документацией к пакету, расшифровывается как HTTP request multiplexer (Мультиплексор HTTP-запросов) (такое же значение mux имеет и в стандартной библиотеке).

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

router := mux.NewRouter()router.StrictSlash(true)server := NewTaskServer()router.HandleFunc("/task/", server.createTaskHandler).Methods("POST")router.HandleFunc("/task/", server.getAllTasksHandler).Methods("GET")router.HandleFunc("/task/", server.deleteAllTasksHandler).Methods("DELETE")router.HandleFunc("/task/{id:[0-9]+}/", server.getTaskHandler).Methods("GET")router.HandleFunc("/task/{id:[0-9]+}/", server.deleteTaskHandler).Methods("DELETE")router.HandleFunc("/tag/{tag}/", server.tagHandler).Methods("GET")router.HandleFunc("/due/{year:[0-9]+}/{month:[0-9]+}/{day:[0-9]+}/", server.dueHandler).Methods("GET")

Обратите внимание на то, что одни только эти определения тут же закрывают первые два пункта вышеприведённого списка задач, которые надо решить для повышения удобства работы с маршрутами. Благодаря тому, что в описании маршрутов используются вызовы Methods, мы можем с лёгкостью назначать в одном маршруте разные методы для разных обработчиков. Поиск совпадений с шаблонами (с использованием регулярных выражений) в путях позволяет нам легко различать /task/ и /task/<taskid> на самом верхнем уровне описания маршрутов.

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

func (ts *taskServer) getTaskHandler(w http.ResponseWriter, req *http.Request) {log.Printf("handling get task at %s\n", req.URL.Path)// Тут и в других местах мы не проверяем ошибку Atoi, так как маршрутизатор// принимает лишь данные, проверенные регулярным выражением [0-9]+.id, _ := strconv.Atoi(mux.Vars(req)["id"])ts.Lock()task, err := ts.store.GetTask(id)ts.Unlock()if err != nil {http.Error(w, err.Error(), http.StatusNotFound)return}renderJSON(w, task)}

В определении маршрутов маршрут /task/{id:[0-9]+}/ описывает регулярное выражение, используемое для разбора пути и назначает идентификатор переменной id. К этой переменной можно обратиться, вызвав функцию mux.Vars с передачей ей req (эту переменную gorilla/mux хранит в контексте каждого запроса, а mux.Vars представляет собой удобную вспомогательную функцию для работы с ней).

Сравнение различных подходов к организации маршрутизации


Вот как выглядит последовательность чтения кода, применяемая в исходном варианте сервера тем, кто хочет разобраться в том, как обрабатывается маршрут GET /task/<taskid>.


А вот что нужно прочитать тому, кто хочет понять код, в котором применяется gorilla/mux:


При использовании gorilla/mux придётся не только меньше прыгать по тексту программы. Тут, кроме того, читать придётся гораздо меньший объём кода. По моему скромному мнению это очень хорошо с точки зрения улучшения читабельности кода. Описание путей при использовании gorilla/mux это простая задача, при решении которой нужно написать лишь небольшой объём кода. И тому, кто читает этот код, сразу понятно то, как этот код работает. Ещё одно преимущество такого подхода заключается в том, что все маршруты можно увидеть буквально раз взглянув на код, расположенный в одном месте. И, на самом деле, код настройки маршрутов выглядит теперь очень похожим на описание нашего REST API, выполненное в произвольной форме.

Мне нравится пользоваться такими пакетами, как gorilla/mux, из-за того, что подобные пакеты представляют собой узкоспециализированные инструменты. Они решают одну единственную задачу и решают её хорошо. Они не забираются в каждый уголок программного кода проекта, а значит, их, при необходимости можно легко убрать или заменить чем-то другим. Если вы посмотрите полный код того варианта сервера, о котором мы говорим в этой статье, то сможете увидеть, что область использования механизмов gorilla/mux ограничена несколькими строками кода. Если, по мере развития проекта, в пакете gorilla/mux будет обнаружено какое-то ограничение, несовместимое с особенностями этого проекта, задача замены gorilla/mux на другой маршрутизатор стороннего разработчика (или на собственный маршрутизатор) должна решаться достаточно быстро и просто.

Какой маршрутизатор вы использовали бы при разработке REST-сервера на Go?


Подробнее..

Плавка металлов за 9 минут в микроволновке и другие интересные штуки обзор ТОП7 самоделок еще одна

07.06.2021 14:15:24 | Автор: admin

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

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

Автор статьи также является многолетним владельцем микроволновой печи, впрочем, как и достаточно большое число людей в России (рискнем сделать такое смелое предположение).

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

На написание такой статьи автора подтолкнуло то, что его микроволновая печь стала подавать явственные признаки, что конец её близок. В нашем случае, это заключается не в выходе из строя электронной части, а скорее в физическом износе самой камеры нагрева: износилось лакокрасочное покрытие, ввиду чего, есть риск получить пищу, с кусочками краски в её составе (Ммм вкуснотишша! Всё, как мы любим! Sarcasm mode: off).

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

Итак начнем!


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

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

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

Судя по анализу этого блогера, показанное в рассматриваемом ролике не совсем соответствует реальности :-).

Но автор статьи решил пойти дальше, так как не планировал поджаривать соседей микроволновой пушкой.

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

Кстати если интересно, можно ознакомиться с устройством типичного трансформатора микроволновки:

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

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

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

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


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

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


Если кто заинтересовался этой темой, то по следующему адресу можно найти достаточно подробное описание по созданию катушек Тесла, с длиной получаемых разрядов до полутора метров!

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

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

Работа печей для плавления базируется на 2 различающихся способах:

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

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

Это занятие является достаточно увлекательным, и занимаются им достаточно широкие слои, преимущественно женского, населения и их можно понять!

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

image

image

image

Для получения настоящего культурного удовольствия и изучения того, что в мире делается по этому направлению, рекомендуется поиск по сайту www.pinterest.com, по ключевым словам: microwave melting glass, microwave fusing glass, microwave fusing.

Если вы всерьез заинтересовались этим занятием, то на известном сайте имеются наборы начинающего.

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

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

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

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

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

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

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

Рассмотренный в микроволновом способе плавки тигель у автора выдерживал 50 плавок без каких-либо признаков разрушения.

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

Если например, рассмотренная выше технология по плавлению в микроволновке занимает по времени в среднем (от закладки до расплава) около 8-9 минут, то способ плавления металлов с использованием муфельной плавильной печи только для разогрева печи требует не менее 30-40 минут, с соответствующими энергозатратами. И это мы ещё не учитываем тот момент, что печь должна быть доставлена с Aliexpress, и она в своём комплекте содержит тигель с достаточно малым сроком наработки на отказ.

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

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

Или же в этих видео:


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

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

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

Однако сама вероятность создания металлических изделий с использованием 3D принтера и имеющейся в наличии микроволновки, является весьма примечательной и достойной внимательного рассмотрения!

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

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

Примечание. Температура плавления силикатного стекла составляет в райне 425 600C. Выше температуры плавления стекло становится жидкостью. Температура плавления металла, например, бронзы составляет в районе 950C.
Таким образом, зная температуру плавления металла, который вы используете и снимая показания температуры с помощью термопары (например), возможно плавить только стекло и не доводить до плавления металл. И стекло заполнит все нужные места в металле, а сам металл не повредится!


Бонус


Завершая рассказ нельзя не упомянуть одну достаточно забавную поделку, которая была в своё время изготовлена упомянутым ранее блогером Allen Pan-ом. Для её создания он использовал трансформатор от микроволновки, который был переделан в электромагнит.

Кроме того, в её составе были использованы следующие компоненты: плата Arduino Pro Mini, аккумулятор на 12 вольт, твердотельное реле, емкостной датчик, подключенный к рукоятке и сканер отпечатка пальца. Всё это было помещено в компактный корпус в форме молота (Мьёльнир-а), принадлежащего Богу грома Тору (согласно Вселенной Марвел).

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

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

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

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

Также, в настоящее время возможно упростить конструкцию молота, если взять вместо платы Arduino pro mini плату esp32: она содержит сенсорные пины, к которым можно подключить металлические площадки на рукоятке молота (предусмотрительно размещенные ранее). И вести обработку события отпустить молот исключительно логическим путём (если площадка 1 удерживается и по площадке 2 в этот момент два раза постучали пальцем то отпустить молот и т.д.). В таком случае, самоделка будет еще привлекательней, так как пропадет существенный демаскирующий признак сканер отпечатка пальца.
Как можно видеть из всего этого длинного рассказа, микроволновка, это не только средство для приготовления и разогрева пищи, но и неисчерпаемый кладезь компонентов, которые позволят вам создать свои экспериментальные и даже вполне полезные вещи.

Для некоторых из этих неординарных применений, даже не требуется каких-либо её переделок!

Что же касается самого автора рассказа, то в списке его предпочтений, так сказать, личного хит-парада, первое место прочно занимает методика плавки металла в микроволновке.

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

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

В любом случае, надеемся, что этот рассказ был для вас полезным и интересным, дав каждому читателю пищу для размышлений!


Подробнее..

Перевод CSS, JavaScript и блокировка парсинга веб-страниц

12.06.2021 14:07:17 | Автор: admin
Недавно мне попался материал, посвящённый проблеме загрузки CSS-файлов, которая замедляет обработку материалов страниц. Я читал ту статью, стремясь научиться чему-то новому, но мне показалось, что то, о чём там говорилось, не вполне соответствует истине. Поэтому я провёл собственное исследование этой темы и поэкспериментировал с загрузкой CSS и JavaScript.



Может ли загрузка CSS-ресурсов блокировать парсинг страницы?


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

Для начала предлагаю поэкспериментировать. Для этого нам понадобится соответствующим образом настроить браузер. CSS-файл мы будем загружать с CDN, поэтому ограничим скорость работы с сетью в браузере Google Chrome. Для этого, на вкладке инструментов разработчика Performance, поменяем значение параметра Network на Slow 3G. Исследовать будем следующую страницу:

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><link href="http://personeltest.ru/aways/cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet"><script>document.addEventListener('DOMContentLoaded', () => {console.log('DOMContentLoaded');})</script><script>console.log('script');Promise.resolve(1).then(res => {console.log('then');});</script></head><body><h1>hello</h1></body></html>

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


Вывод данных в JS-консоль

Может ли загрузка и выполнение JS-кода блокировать парсинг страницы?


Загрузка и обработка JS-файлов, безусловно, блокирует парсинг страницы. Но, чтобы исправить эту проблему, при подключении скриптов к странице можно пользоваться атрибутами defer и async тега <script>. Сейчас мы изучим их воздействие на загрузку страницы.

Обычные загрузка и выполнение скрипта


Если в теге <script> не используются атрибуты async или defer процесс загрузки и обработки материалов страницы происходит так, как показано на следующей схеме. Загрузка JS-файлов и выполнение содержащегося в них кода блокирует парсинг HTML-кода.


Использование тега <script> без атрибутов async и defer

Здесь и далее мы будем пользоваться следующими цветовыми обозначениями.


HTML parsing Парсинг HTML; HTML parsing paused Парсинг HTML приостановлен; Script download Загрузка скрипта; Script execution Выполнение скрипта

Использование тега <script> с атрибутом async


Когда браузер обрабатывает тег <script> с атрибутом async, загрузка JavaScript-кода осуществляется в асинхронном режиме. Код скрипта выполняется сразу после загрузки. При этом выполнение JS-кода блокирует парсинг HTML.


Использование тега <script> с атрибутом async

Использование тега <script> с атрибутом defer


Если в теге <script> имеется атрибут defer код скрипта загружается асинхронно. При этом код, после завершения его загрузки, выполняется только тогда, когда будет завершён парсинг HTML-кода.


Использование тега <script> с атрибутом defer

Эксперименты


Давайте поэкспериментируем с атрибутами async и defer. Начнём со следующей страницы:

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>DomContentLoaded</title></head><body><script src="http://personeltest.ru/away/code.jquery.com/jquery-1.4.4.min.js"></script><script src="./index.js"/> // 0<script src="./index2.js"/> // 2<script >console.log('inline');Promise.resolve().then(res=>{console.log('then');})</script><div id="hello">hello world</div><script>document.addEventListener('DOMContentLoaded', () => {console.log('DOMContentLoaded');})</script></body></html>

Эта страница, помимо загрузки скрипта jquery-1.4.4.min.js с CDN, загружает пару собственных скриптов index.js и index2.js. Ниже приведён их код.

Файл index.js:

Promise.resolve().then((res) => {console.log('index1');return res;});

Файл index2.js:

Promise.resolve().then((res) => {console.log('index2');return res;});

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


Вывод данных в JS-консоль

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

Теперь посмотрим на то, как ведут себя скрипты, в тегах <script> которых используется атрибут <async>:

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>DomContentLoaded</title></head><body><script async src="http://personeltest.ru/away/code.jquery.com/jquery-1.4.4.min.js"></script><script src="./index.js"></script><script src="./index2.js"/></script><script>console.log('inline');Promise.resolve().then(res=>{console.log('then');})</script><div id="hello">hello world</div><script>document.addEventListener('DOMContentLoaded', () => {console.log('DOMContentLoaded');})</script></body></html>

Изучим то, что попадёт в консоль.


Вывод данных в JS-консоль

Скрипт библиотеки jQuery загружается асинхронно. То, что попадает в консоль, выводится там до его загрузки. Если скрипт библиотеки загружается слишком медленно это не помешает парсингу HTML-кода. Сообщение DOMContentLoaded может быть выведено и до, и после завершения загрузки и выполнения async-скрипта. А при применении атрибута defer скрипт будет загружен асинхронно, дождётся завершения обработки материалов документа, а потом, но до события DOMContentLoaded, будет выполнен.

Если вам интересна тема оптимизации веб-страниц с учётом возможной блокировки обработки их кода при загрузке внешних стилей возможно, вам стоит взглянуть на этот материал.

Сталкивались ли вы с проблемами, связанными с блокировкой обработки материалов веб-страниц?


Подробнее..

Перевод Стриминг видео с помощью Akka Streams

12.06.2021 18:20:44 | Автор: admin
Автор статьи, перевод которой мы сегодня публикуем, говорит, что стриминг видео не должен быть такой задачей, с которой у кого-либо возникают сложности. Всё дело в правильном подборе инструментов, среди которых можно отметить пакет Akka Streams. Использование этого пакета позволяет эффективно разрабатывать приложения для потоковой передачи видео.



Правда, не следует думать, что то, о чём мы будем тут говорить, подобно простому примеру, вроде println(Hello world), в котором используется система акторов Akka. Сегодня вы узнаете о том, как создать свой первый сервис для потоковой передачи видео (прошу прощения, если моё предположение неверно, и у вас это уже не первый такой проект). В частности, тут будут использованы пакеты Akka HTTP и Akka Streams, с помощью которых мы создадим REST API, который обладает способностями стриминга видеофайлов в формате MP4. При этом устроен этот API будет так, чтобы то, что он выдаёт, соответствовало бы ожиданиям HTML5-тега <video>. Кроме того, тут я скажу несколько слов о наборе инструментов Akka в целом, и о некоторых его компонентах, вроде Akka Streams. Это даст вам определённый объём теории, которая пригодится вам в работе. Но, прежде чем мы приступим к делу, хочу задать один вопрос.

Почему я решил рассказать о стриминге видео?


У того, что я посвятил эту статью потоковой передаче видео, есть три основных причины.

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

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

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

А теперь мы можем переходить к нашей основной теме.

Что такое набор инструментов Akka? Удобно ли им пользоваться?


Akka это опенсорсный набор инструментов, который нацелен на упрощение разработки многопоточных и распределённых приложений, и, кроме того, даёт программисту среду выполнения для подобных приложений. Системы, которые базируются на Akka, обычно очень и очень хорошо масштабируются. Проект Akka основан на модели акторов, в нём используется система конкурентного выполнения кода, основанная на акторах. Создатели этого проекта многое почерпнули из Erlang. Инструменты Akka написаны на Scala, но в проекте имеются DSL и для Scala, и для Java.

Если говорить об удобстве работы с Akka, то, полагаю, писать код с использованием этого набора инструментов весьма удобно. Если вы хотите получше его изучить рекомендую начать с этого материала.

Основной объём кода, который мы будем тут рассматривать, будет использовать пакеты HTTP и Streams. Мы практически не будем пользоваться стандартным пакетом Akka Actors.

Теперь, когда мы разобрались с самыми базовыми сведениями об Akka, пришло время вплотную заняться пакетом Akka Streams.

Что такое Akka Streams?


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

Для нас важен тот факт, что в Akka Streams есть встроенный механизм back-pressure (обратное давление). Благодаря этому решается одна из самых сложных проблем мира стриминговой передачи данных. Это настройка правильной реакции поставщика данных на работу в условиях, когда потребитель данных не может справиться с нагрузкой. И эту проблему решает инструмент, которым мы будем пользоваться, а нам остаётся лишь научиться работать с этим инструментом, не вдаваясь в какие-то чрезмерно сложные и запутанные темы.

Пакет Akka Streams, кроме того, даёт в наше распоряжение API, который совместим с интерфейсами, необходимыми для работы с Reactive Streams SPI. И, между прочим, стоит отметить, что сам проект Akka входит в число основателей инициативы Reactive Streams.

Про Akka Streams мы поговорили. Поэтому можем переходить к нашей следующей теоретической теме.

Что такое Akka HTTP?


Akka HTTP это, как и Akka Streams, пакет, входящий в набор инструментов Akka. Этот пакет основан на пакетах Akka Streams и Akka Actors. Он направлен на то, чтобы упростить работу приложений, в которых используются инструменты Akka, с внешним миром по протоколу HTTP. Этот пакет поддерживает и серверные, и клиентские возможности. Поэтому с его помощью можно создавать и REST API, и HTTP-клиентов, которые отправляют запросы к неким внешним сервисам.

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

HTML5-тег <video>


Тег <video> это новый элемент, который появился в HTML5. Он создавался как замена Adobe Video Player. Несложно понять, что главная задача этого HTML-элемента заключается в предоставлении разработчикам возможностей встраивания в HTML-документы медиаплееров, способных воспроизводить видеофайлы. Собственно, этот тег очень похож на <img>.

В теге <video> размещают тег <source>, имеющий два важных атрибута. Первый это src, который используется для хранения ссылки на видео, которое нужно воспроизвести. Второй это type, который содержит сведения о формате видео.

Между открывающим и закрывающим тегом <video> </video> можно ввести какой-то текст, который будет использован в роли текста, выводимого вместо элемента <video> в тех случаях, когда этот элемент не поддерживается браузером. Но в наши дни тег <video> поддерживает даже Internet Explorer, поэтому вероятность возникновения ситуации, в которой может понадобиться подобный текст, стремится к нулю.

Как будет работать стриминг с использованием тега и FileIO?</font></h2>Хотя на первый взгляд кажется, что стриминг с использованием тега <video> и FileIO это нечто архисложное, если в этот вопрос немного вникнуть, окажется, что ничего особенного в нём нет. Дело в том, что то, о чём идёт речь, представлено готовыми блоками, из которых нужно лишь собрать то, что нам нужно.На стороне сервера основная нагрузка ложится на объект <code>FileIO</code>. Он будет генерировать события, содержащие фрагменты файла, потоковую передачу которого мы осуществляем. Конечно, размеры этих фрагментов поддаются настройке. И, более того, настроить можно и позицию, начиная с которой будет осуществляться стриминг файла. То есть видео необязательно смотреть сначала его можно начинать смотреть с любого места, интересующего пользователя. Всё это отлично сочетается с возможностями тега <code><video></code>, выполняющего запрос HTTP GET с заголовком <code>Range</code> для того чтобы включить воспроизведение видео без его предварительной загрузки.Вот пара примеров запросов, выполняемых элементом <code><video></code>:<img src="http://personeltest.ru/aways/lh3.googleusercontent.com/Dfgd_RjgpV9W3jzBF9TVNLT_n8cAf5_VfvhxbfeK5wYH5slwUtXhjQ0FlZ0I79gCWsNgIF1Q3q7nPty5HqpCpgXIYrEtjQINuyMaMHe1nWu228-p8o9Hf3n-cHNZN_5iS0QGktk" align="center"><img src="http://personeltest.ru/aways/lh3.googleusercontent.com/SQPBs8xy8HVsT8cw2_WRoOnQW1dqKRp2WVSAzfFsbCBkSmETbm7NKS8zMLBdwcWdIo1AQDO_tSJJw1_2Tcd_oMoaBzdTwDz62Tq2jtzKX0ujmuCEqo99NKH_YLnlL6FQbZ5UOd0" align="center">Если кому интересно заголовок <code>Range:bytes=x-</code> отвечает за выбор позиции, с которой начинается воспроизведение видео. Первый запрос уходит на сервер в начале воспроизведения видео, а второй может быть отправлен тогда, когда пользователь решит куда-нибудь перемотать видео.Сейчас, после довольно-таки длинного вступления, пришло время заняться кодом.<h2><font color="#3AC1EF">Пишем стриминговый сервис</font></h2>В нескольких следующих разделах я расскажу о реализации серверной части нашего стримингового сервиса. А потом мы создадим простую HTML-страницу, с помощью которой проверим правильность работы этого сервиса.Я люблю делать предположения, поэтому сделаю ещё одно, которое заключается в том, что я ожидают, что тот, кто это читает, владеет основами Scala и SBT.<h3><font color="#3AC1EF">1</font></h3>Добавим в файл <code>build.sbt</code> необходимые зависимости. В этом проекте нам понадобится 3 пакета: <code>akka-http</code>, <code>akka-actor-typed</code> (пакета <code>akka-actor</code>, в теории, достаточно, но никогда нельзя забывать о типобезопасности) и <code>akka-stream</code>.<source lang="scala">libraryDependencies ++= Seq("com.typesafe.akka" %% "akka-actor-typed" % "2.6.14","com.typesafe.akka" %% "akka-stream" % "2.6.14","com.typesafe.akka" %% "akka-http" % "10.2.4")</source><h3><font color="#3AC1EF">2</font></h3>Теперь можно создать главный класс, ответственный за запуск приложения. Я решил расширить класс <code>App</code>. Мне кажется, что это удобнее, чем создавать метод <code>main</code>. На следующем шаге мы поместим сюда код, имеющий отношение к созданию системы акторов и HTTP-сервера.<source lang="scala">object Runner extends App {}</source><h3><font color="#3AC1EF">3</font></h3>После создания главного класса мы можем добавить в него код, о котором говорили выше.<source lang="scala">object Runner extends App {val (host, port) = ("localhost", 8090)implicit val system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, "akka-video-stream")Http().newServerAt(host, port)}</source>Сейчас нас вполне устроит такая конфигурация. На последнем шаге мы добавим в код вызов метода <code>bind</code>, что позволит открыть доступ к нашему REST API. Тут мы создаём объект <code>ActorSystem</code> с именем <code>akka-video-stream</code> и HTTP-сервер, прослушивающий порт <code>8090</code> на локальном компьютере. Не забудьте о ключевом слове <code>implicit</code> в определении системы акторов, так как подобный неявный параметр необходим в сигнатуре метода <code>Http</code>.<h3><font color="#3AC1EF">4</font></h3>А тут мы, наконец, реализуем конечную точку REST API, используемую для обработки запросов от тега <code><video></code>.<source lang="scala">object Streamer {val route: Route =path("api" / "files" / "default") {get {optionalHeaderValueByName("Range") {case None =>complete(StatusCodes.RangeNotSatisfiable)case Some(range) => complete(HttpResponse(StatusCodes.OK))}}}}</source>Как видите, я создал конечную точку с URL <code>api/files/default/</code>. В её коде проверяется, есть ли в запросе заголовок <code>Range</code>. Если он содержит корректные данные сервер возвращает ответ с кодом <code>200</code> (<code>OK</code>). В противном случае возвращается ответ с кодом <code>416</code> (<code>Range Not Satisfiable</code>).<h3><font color="#3AC1EF">5</font></h3>Пятый шаг нашей работы отлично подходит для реализации метода, ради которого, собственно, и была написана эта статья.<source lang="scala">private def stream(rangeHeader: String): HttpResponse = {val path = "path/to/file"val file = new File(path)val fileSize = file.length()val range = rangeHeader.split("=")(1).split("-")val start = range(0).toIntval end = fileSize - 1val headers = List(RawHeader("Content-Range", s"bytes ${start}-${end}/${fileSize}"),RawHeader("Accept-Ranges", s"bytes"))val fileSource: Source[ByteString, Future[IOResult]] = FileIO.fromPath(file.toPath, 1024, start)val responseEntity = HttpEntity(MediaTypes.`video/mp4`, fileSource)HttpResponse(StatusCodes.PartialContent, headers, responseEntity)}</source>Тут я сделал следующее:<ul><li>Загрузил файл, потоковую передачу которого я хочу организовать, а затем, учитывая заголовок из запроса, и данные о файле, нашёл позицию в файле, с которой начнётся стриминг, а так же сформировал заголовок <code>Content Range</code>.</li><li>С помощью <code>FileIO</code> создал поток из ранее загруженного файла. Затем я использовал этот поток в роли данных в <code>HttpEntity</code>.</li><li>Я создал ответ, <code>HttpResponse</code>, с кодом <code>206</code> (<code>Partial Content</code>), с соответствующими заголовками и с телом в виде <code>responseEntity</code>.</li></ul>Ещё мне хочется подробнее поговорить о <code>FileIO</code>, так как это самый удивительный механизм во всей статье. Что, на самом деле, происходит при выполнении строки <code>FileIO.fromPath(file.toPath, 1024, start)</code>?Тут, из содержимого файла, находящегося по заданному пути, создаётся объект <code>Source</code> (Akka-аналог Producer из Reactive Streams). Каждый элемент, выдаваемый этим объектом, имеет размер, в точности равный 1 Мб. Первый элемент будет взят из позиции, указанной в параметре <code>start</code>. Поэтому, если в <code>start </code>будет 0 первый элемент окажется первым мегабайтом файла.<h3><font color="#3AC1EF">6</font></h3>Мы уже реализовали основную логику серверной части приложения. А сейчас нам надо отрефакторить её код для того чтобы нашим сервером можно было бы пользоваться.Начнём с внесения изменений в определение REST API:<source>complete(HttpResponse(StatusCodes.Ok)) => complete(stream(range))</source>Получается, что вместо того, чтобы просто возвращать <code>OK</code>, мы вызываем метод <code>stream</code> с передачей ему параметра <code>range</code> и начинаем стриминг.Нельзя забывать о том, что наш API всё ещё недоступен для внешнего мира. Поэтому нам нужно модифицировать соответствующий фрагмент кода, ответственный за запуск HTTP-сервера:<source>Http().newServerAt(host, port) =>Http().newServerAt(host, port).bind(Streamer.route)</source>Готово! Теперь у нас есть рабочий бэкенд, а наш REST API ждёт подключений от любых программ, которым он нужен. Осталось лишь всё протестировать.<h2><font color="#3AC1EF">Тестирование стримингового сервиса</font></h2>Мы, чтобы протестировать приложение, создадим простую HTTP-страницу, единственным достойным внимания элементом которой будет тег <code><video></code>. Причём, обо всём, что надо знать для понимания работы этой страницы, мы уже говорили. Поэтому я просто приведу ниже полный код соответствующего HTML-документа.То, что вы в итоге увидите в окне браузера, открыв эту страницу, должно, более или менее, напоминать то, что я покажу ниже. Конечно, ваш стриминговый сервис вполне может передавать не тот видеофайл, который использовал я.<source lang="html"><!DOCTYPE html><html lang="en"><head><title>Akka streaming example</title></head><body style="background: black"><div style="display: flex; justify-content: center"><video width="1280" height="960" muted autoplay controls><source src="http://personeltest.ru/away/localhost:8090/api/files/default" type="video/mp4">Update your browser to one from XXI century




Тут мне хотелось бы обратить ваше внимание на 5 важных вещей:

  1. Я использовал возможности тега <source> вместо использования соответствующих атрибутов тега <video>.
  2. В атрибуте src тега <source> я указал путь, при обращении к которому бэкенд начнёт потоковую передачу видеофайла.
  3. В атрибуте type тега <source> я указал тип файла.
  4. Я добавил к тегу <video> атрибуты autoplay и muted для того чтобы видео начинало бы воспроизводиться автоматически.
  5. К тегу <video> я добавил и атрибут controls, благодаря чему будут выводиться элементы управления видеоплеера, встроенного в страницу.

На элемент <div> можете особого внимания не обращать. Он тут нужен лишь для стилизации плеера.

Для проверки правильности работы системы достаточно запустить бэкенд и открыть вышеописанный HTML-документ в любом современном браузере.


Правильная работа стримингового сервиса

Обратите внимание на то, что автоматическое воспроизведение видео не начнётся до тех пор, пока к тегу <video> не будут добавлены атрибуты muted и autoplay. Если не оснастить тег <video> этими атрибутами воспроизведение придётся включать вручную, нажимая на соответствующую кнопку.

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

Что можно улучшить?


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

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

Итоги


Сегодня я постарался доказать то, что реализация простого стримингового сервиса это, при условии использования правильных инструментов, проще, чем кажется. Использование инструментов Akka и подходящего HTML-тега способно значительно сократить объём работы. Правда, не забывайте о том, что тут показан очень простой пример. Для реализации реального стримингового сервиса этого может быть недостаточно.

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

Вот GitHub-репозиторий с кодом проекта.

Какие инструменты вы выбрали бы для создания собственного стримингового сервиса?


Подробнее..

Перевод Разработка REST-серверов на Go. Часть 3 использование веб-фреймворка Gin

17.06.2021 16:13:58 | Автор: admin
Сегодня, в третьей части серии материалов, посвящённых разработке серверов на Go, мы займёмся реализацией нашего REST-сервера с использованием Gin одного из самых популярных веб-фреймворков для Go. Вот код, который мы будем тут обсуждать.



Выбор веб-фреймворка


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

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

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

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

Маршрутизация и Gin


Наша функция main настраивает новый маршрутизатор Gin и регистрирует маршруты:

router := gin.Default()server := NewTaskServer()router.POST("/task/", server.createTaskHandler)router.GET("/task/", server.getAllTasksHandler)router.DELETE("/task/", server.deleteAllTasksHandler)router.GET("/task/:id", server.getTaskHandler)router.DELETE("/task/:id", server.deleteTaskHandler)router.GET("/tag/:tag", server.tagHandler)router.GET("/due/:year/:month/:day", server.dueHandler)

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

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

  1. Вместо указания HTTP-метода в виде дополнительного (Go) вызова метода в маршруте, метод закодирован в имени функции, используемой для регистрации маршрута. Например тут используется конструкция вида router.POST, а не что-то вроде router.HandleFunc(...).Methods(POST).
  2. Gorilla поддерживает обработку запросов с использованием регулярных выражений. А Gin нет. К этому ограничению мы ещё вернёмся.

Обработчики запросов


Посмотрим на код обработчиков запросов, используемых при применении Gin. Начнём с самых простых, в частности с getAllTasksHandler:

func (ts *taskServer) getAllTasksHandler(c *gin.Context) {allTasks := ts.store.GetAllTasks()c.JSON(http.StatusOK, allTasks)}

Тут стоит обратить внимание на несколько интересных моментов:

  1. У обработчиков, используемых в Gin, нет стандартных сигнатур HTTP-обработчиков Go. Они просто принимают объект gin.Context, который может быть использован для анализа запроса и для формирования ответа. Но в Gin есть механизмы для взаимодействия со стандартными обработчиками вспомогательные функции gin.WrapF и gin.WrapH.
  2. В отличие от ранней версии нашего сервера, тут нет нужды вручную писать в журнал сведения о запросах, так как стандартный механизм логирования Gin, представленный ПО промежуточного уровня, сам решает эту задачу (и делается это с использованием всяческих полезных мелочей, вроде оформления вывода разными цветами и включения в журнал сведений о времени обработки запросов).
  3. Нам, кроме того, больше не нужно самостоятельно реализовывать вспомогательную функцию renderJSON, так как в Gin есть собственный механизм Context.JSON, который позволяет формировать JSON-ответы.

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

func (ts *taskServer) getTaskHandler(c *gin.Context) {id, err := strconv.Atoi(c.Params.ByName("id"))if err != nil {c.String(http.StatusBadRequest, err.Error())return}task, err := ts.store.GetTask(id)if err != nil {c.String(http.StatusNotFound, err.Error())return}c.JSON(http.StatusOK, task)}

Тут особенно интересно выглядит обработка параметров. Gin позволяет обращаться к параметрам маршрута (к тому, что начинается с двоеточия, вроде :id) через Context.Params.

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

Привязка данных запросов


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

func (ts *taskServer) createTaskHandler(c *gin.Context) {type RequestTask struct {Text string  `json:"text"`Tags []string `json:"tags"`Due time.Time `json:"due"`}var rt RequestTaskif err := c.ShouldBindJSON(&rt); err != nil {c.String(http.StatusBadRequest, err.Error())}id := ts.store.CreateTask(rt.Text, rt.Tags, rt.Due)c.JSON(http.StatusOK, gin.H{"Id": id})}

В Gin имеется серьёзная инфраструктура для организации привязки запросов к структурам данных Go, содержащих данные из запросов. Тут под привязкой понимается обработка содержимого запросов (которое может быть представлено данными в различных форматах, например JSON и YAML), проверка полученных данных и запись соответствующих значений в структуры Go. Здесь мы пользуемся весьма примитивной формой привязки данных для RequestTask, где проверка данных не используется. Но, полагаю, нам стоит знать не только о базовых, но и о более продвинутых возможностях Gin.

Можно заметить, что Gin-версия createTaskHandler существенно короче более ранних версий аналогичного обработчика, так как за разбор JSON-данных запроса отвечает ShouldBindJSON.

Ещё внимание обратить стоит на то, что теперь нам не нужно пользоваться одноразовой структурой для ID ответа. Вместо этого мы используем gin.H псевдоним для map[string]interface{}; это очень просто, но, всё же, позволяет весьма эффективно конструировать ответы, используя совсем небольшие объёмы кода.

Дополнительные возможности Gin


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

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

Ограничения фреймворков


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

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

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

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

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

Каким фреймворком вы воспользовались бы при разработке сервера на Go?


Подробнее..

Перевод Как попасть в состояние потока?

18.06.2021 20:15:41 | Автор: admin
Для меня попадание в состояние потока является единственным способом продуктивной работы над сложными программными проектами. И я полагаю, что разработчик может так организовать свою жизнь, чтобы как можно сильнее удлинить время, которое он каждый день может проводить в этом состоянии. Тут я хочу рассказать о том, что лично я пытаюсь делать для того, чтобы чаще попадать в состояние потока.



Сон


image

Сон это самый важный фактор среди тех, что влияют на мою продуктивность. Если бы мне пришлось выбирать между хорошим ночным отдыхом и другими пунктами моего списка я выбрал бы сон. У меня есть жёсткое правило не пить кофе после 16:00. Если я делаю себе кофе в 15:45, а потом забываю о нём до 16:05, я убираю его в холодильник и оставляю на следующий день. Незначительный рост эффективности вечерней работы не стоит серьёзного ухудшения моей продуктивности на следующий день.

В те дни, когда я перевозбуждён, мне обычно помогает успокоиться приём 0,3-0,9 мг мелатонина. Я, кроме того, считаю, что спать лучше в прохладной комнате. Мне кажется, что оптимальным вариантом является температура в 16-18C.

Кофе


image

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

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

Качество воздуха


image

Можно очень сильно улучшить продуктивность, понизив уровень CO2 в воздухе рабочей комнаты. Под очень сильным улучшением я понимаю что-то в районе 20% или больше. Было одно исследование, где изучали воздействие уровня CO2 на когнитивную деятельность людей. Одна группа работала в помещении с уровнем CO2 в 1000 ppm, вторая с уровнем CO2 в 600 ppm. Для справки на открытом воздухе уровень CO2 составляет 400 ppm. Люди, работавшие в комнате с более высокой концентрацией CO2, набрали в когнитивных тестах примерно на 20% меньше баллов, чем люди, которые дышали воздухом с более низким содержанием CO2.

Я, пока не обзавёлся монитором качества воздуха, считал, что уровень CO2 в моей рабочей комнате достаточно низок, так как я часто проветривал эту комнату. А оказалось, что уровень CO2 в моей комнате составляет примерно 1200 ppm, что даже выше, чем в неблагополучной комнате вышеописанного исследования! Для того чтобы это исправить, я почти всегда, когда работаю, держу окно приоткрытым. Это позволяет поддерживать уровень CO2 в моей комнате примерно на отметке в 450 ppm.

Физические упражнения


image

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

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

Работа в одиночестве


image

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

Удаление игр


image

Игра Valorant это мой наркотик. Она практически идеально мне подходит. По опыту я знаю, что могу провести изрядную часть дня, стреляя по врагам. Поэтому я сам для себя построил забор Шеллинга и напрочь удалил соответствующий раздел диска. Кто-то другой, вероятно, сможет лучше себя контролировать, не прибегая к столь радикальным мерам. А вот у меня однажды вышло так, что я очень надолго заигрался, потеряв массу ценнейшего времени. Поэтому лично я предпочитаю всеми силами избегать компьютерных игр.

Блокировка сайтов


image

У меня есть дурная привычка, которая заключается в том, что я, столкнувшись со сложной задачей, начинаю бесцельно прокручивать ленту Твиттера. Это похоже на то, как если бы я, пытаясь заехать на велосипеде на крутой склон, заблокировал бы колёса этого велосипеда. В такой ситуации я теряю скорость, мне, когда я всё же возвращаюсь к работе, приходится восстанавливать в голове детали того, чем я занимался. Для того чтобы не попадать в эту ловушку, я использую Freedom и блокирую все сайты, которые меня отвлекают.

Составление плана работ на день


image

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

Музыка


image

Я полагаю, что идеальный музыкальный жанр, который позволяет мне длительное время не терять сосредоточения, это Deep House. Вот один из моих любимых миксов (живое выступление на воздушном шаре!). Ещё я пользуюсь наушниками с шумоподавлением. Без них я просто не могу сосредоточиться. Даже если я работаю в тихой комнате, мне очень нравится невероятная тишина, которую обеспечивают такие наушники.

Питание


image

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

Выбор правильной задачи


image

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

Как вы относитесь к состоянию потока?


Подробнее..

Перевод Практический взгляд на Raspberry Pi Pico с точки зрения STM32

19.06.2021 14:07:57 | Автор: admin
Сравнительно недавно Raspberry Pi Foundation выпустила плату Raspberry Pi Pico, основанную на микроконтроллере (Micro Controller Unit, MCU) RP2040. Эта плата привлекла большое внимание членов сообщества разработчиков различных электронных устройств. Появилось довольно много проектов, в которых используются программируемые модули ввода-вывода (Programmable I/O, PIO) Raspberry Pi Pico. Например, это проект PicoDVI, в котором конечные автоматы PIO используются для вывода DVI-сигнала.



Но с появлением Raspberry Pi Pico связано не только радостное возбуждение разработчиков электроники. Это событие заставило сообщество задаться важным вопросом о том, окажет ли появление платы какое-то ощутимое влияние на тех, кто пользуется STM32, SAM и другими микроконтроллерами, основанными на Cortex-M. Станет ли микроконтроллер RP2040 жизнеспособным выбором для некоторых из проектов, в которых используются похожие MCU? Учитывая то, что в состав RP2040 входит двухъядерный процессор ARM Cortex-M0+, кажется справедливой идея использования этого микроконтроллера там же, где применяются 32-битные MCU от ведущих производителей компонентов такого рода, в частности, от STMicroelectronics.

Сможет ли небольшой проект Raspberry Pi Foundation показать инженерам STM как надо делать микроконтроллеры, или создателям платы на RP2040 стоит пересмотреть некоторые из своих гипотез? Сложно ли будет портировать на RP2040 низкоуровневый код, рассчитанный на STM32?

Сложно ли перенести STM32-проект на RP2040?


Короче говоря, когда я обратила внимание на RP2040, мне подумалось, что будет интересно попытаться портировать на новый микроконтроллер мой C++-фреймворк для STM32. Правда, эта идея меня заинтересовала не из-за двухъядерного ARM Cortex-M0+. У меня есть двухъядерные микроконтроллеры STM32H7 (M4 и M7), которые, за счёт более совершенных характеристик, легко обойдут RP2040. Сильнее всего меня заинтриговали программируемые модули ввода-вывода RP2040, возникало такое ощущение, что они достойны того, чтобы познакомиться с ними поближе.


Плата Raspberry Pi Pico, основанная на RP2040 подключена к одноплатному компьютеру Raspberry Pi, играющему роль SWD-адаптера (оригинал)

Основываясь на опыте работы с STM32 я поняла, что смогу быстро портировать некоторые файлы, создав в репозитории проекта ветку RP, рассчитанную на другую архитектуру, и принявшись за дело. Ведь и в основном проекте, и в новой ветке код будет рассчитан на Cortex-M. Обычно работа с новым ARM-микроконтроллером заключается в том, чтобы найти даташит, справочное руководство и CMSIS-файлы для соответствующего устройства. А потом существующий низкоуровневый код можно легко адаптировать под новый подход к именованию периферийных устройств и под новую схему регистров, учитывая то, что фундаментальные компоненты нового и старого микроконтроллеров (SysTick, NVIC и так далее) ничем не отличаются.

Может, я поступила слишком опрометчиво, но я заказала плату Raspberry Pi Pico, даже не поинтересовавшись тем, есть ли для неё CMSIS-файлы, и даже не взглянув в справочное руководство по ней. Позже я, к своему удивлению, выяснила, что пока нет даже и речи о наличии CMSIS-файлов для Raspberry Pi Pico, или хотя бы о возможности взаимодействия RP2040 с другими устройствами из экосистемы Cortex-M. Но при этом SVD-файл для MCU RP2040 имеется в Pico SDK, а на основе этого файла можно создать заголовочный файл для устройства. Благодаря проекту cmsis-pi-pico в моём распоряжении, в итоге, оказалось рабочее решение.

Решение моей задачи можно было бы и упростить


Для того чтобы низкоуровневый программный проект завёлся бы на целевом MCU, при работе с STM32 нужно решить несколько задач. В них входит подготовка загрузочного кода, который выполняет некоторые простые настройки рабочей среды и таблицы векторов прерываний, содержащей адреса функций обработчиков прерываний.


Последовательность загрузки RP2040 (даташит RP2040, рисунок 15) (оригинал)

Есть ещё и скрипт компоновщика, который позволяет обеспечить нахождение в памяти всех необходимых битов по правильным смещениям. Всё это представляет собой достаточно скромный набор задач при работе с MCU, в котором выполняется загрузка образа прошивки, находящейся во Flash ROM по адресу, используемому по умолчанию.

Первая сложность, которую нужно было преодолеть для того чтобы научиться работать с RP2040, заключалась в понимании особенностей цепочечного процесса загрузки микроконтроллера. Тут всё очень похоже на то, как в прошлом, на обычных компьютерах, была организована загрузка с дискет, или то, как устроена загрузка с HDD/SSD. А именно внешняя QSPI Flash ROM рассматривается MCU лишь как устройство, которое, возможно, содержит загрузочные данные. Загрузчик первой фазы загрузки интегрирован в MCU и располагается в ROM по адресу 0x0000 0000. Он обращается к интерфейсу QSPI и пытается загрузить из него 256 байт данных. Потом будет проверен CRC32-хеш этих данных. Если проверка пройдёт успешно, они будут признаны загрузчиком второй фазы загрузки.

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

Говорят, что хорошие художники копируют


У меня ушло достаточно много времени на то, чтобы понять, как подход к управлению тактированием периферийных устройств, принятый в STM32, соотносится с системной архитектурой RP2040. Я внимательно читала даташит RP2040 и всех вокруг спрашивала об этом. Как оказалось, RP2040-версия системы управления тактированием периферии называется RESETS. Эта система является полной противоположностью той, что применяется в STM32. А именно, нужно установить условие сброса периферийного блока в 0 для того чтобы включить его тактирование. Так, чтобы включить тактирование GPIO, нужно переключить бит 8 в RESETS_RESET (PADS_BANK0).


Функциональная схема GPIO-пина RP2040 (оригинал)

Когда я это поняла, я посмотрела раздел документации по GPIO-периферии (раздел 2.19). И кое-что тут же бросилось мне в глаза. А именно, то, что я там увидела, совершенно не похоже на то, как устроена практически вся GPIO-периферия, с которой я когда-либо сталкивалась. В частности, речь идёт о периферии STM32, AVR и SAM.

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

А теперь, когда я через всё это прошла, полагаю, можно будет просто переписать мой код, после чего он заработает на RP2040?

Причуды загрузки


Как уже было сказано, загрузчик второй фазы загрузки должен быть расположен в начале образа прошивки. Я считала, что это должен быть какой-то достаточно стандартный код, поэтому просто взяла готовый ASM-код, который выдал официальный Pico SDK, и использовала его при сборке примера Blinky. Добавив этот код к RP2040-порту моего проекта Nodate, я смогла без проблем собрать Blinky.

Запись результирующего ELF-бинарника в RP2040 стала ещё одним приключением. Дело в том, что на плате Raspberry Pi Pico нет встроенного SWD-адаптера, чего-то в духе ST-Link. А микроконтроллеру на двухъядерном Cortex-M нужен могоканальный SWD-адаптер. Единственным подобным устройством, которое было у меня под рукой, оказался адаптер, интегрированный в плату Nucleo-STM32H7. Поэтому я решила использовать кастомный форк OpenOCD, созданный Raspberry Pi Foundation. Его я запустила на Raspberry Pi.

После столь основательной подготовки мне удалось успешно прошить RP2040, но ничего не заработало. Беглая проверка вызвала у меня такое ощущение, что в ходе загрузки мне не удалось выйти за пределы исходного загрузчика и добраться до прошивки, находящейся в SPI ROM. Сейчас мне сложно дать ответ о причинах происходящего. Это могла быть проблема с ASM-кодом второй фазы загрузки, это могла быть ошибка в экспериментальных CMSIS-файлах RP2040, которые создавала не я. Это могло быть и что-то совершенно иное.

Продолжение следует?



Raspberry Pi Pico (оригинал)

После того, как я потратила много часов на то, чтобы завести RP2040 с использованием CMSIS-файлов и файлов загрузчика второй фазы загрузки, мне кажется, что можно немного отстраниться от ситуации и переоценить происходящее. А именно, с того момента, когда начинала формироваться моя точка зрения на Raspberry Pi Pico, в запросе по поводу CMSIS-файлов появились сведения о том, что официальные CMSIS-файлы, возможно, появятся в Pico SDK 1.2.0. Это довольно-таки приятно.

Полагаю, любому, кто хочет поближе познакомиться с RP2040, пользуясь инструментами, ставшими индустриальным стандартом, имеет смысл дождаться этого релиза Pico SDK. А после того, как в моём распоряжении окажутся официальные CMSIS-файлы, я, вероятно, начну с переделывания примера Nodate Blinky, а потом попробую поработать с PIO. Перспектива создавать собственные интерфейсы кажется мне весьма привлекательной. И хотя возможности Raspberry Pi Pico не так мощны, как возможности CPLD или FPGA, они, всё равно, способны лечь в основу интереснейших проектов.

Возникает такое ощущение, что авторы даташита для RP2040 (он, скорее, похож на смесь справочного руководства и даташита) иногда забывают о том, что в нём должно быть описание микроконтроллера, а не чего-то другого. В эти моменты он превращается в учебное руководство по Pico SDK. Хотя материалы этого даташита и способны принести пользу тем, кто стремится освоить Pico SDK, тем, кто хочет написать что-то своё, пользы от него, однозначно, меньше, чем от более привычного даташита.

Полагаю, тех, кто захочет написать для Raspberry Pi Pico что-то своё, не особенно порадуют такие особенности платы, как запутанная работа с GPIO-периферией, сложный процесс загрузки, необходимость в загрузчике второй фазы загрузки, непрозрачность внешней ROM. В общем тому, кому интересна плата Raspberry Pi Pico, пока приходится ориентироваться на официальный SDK.

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

Пользовались ли вы Raspberry Pi Pico?


Подробнее..

Перевод Почему я всё ещё люблю C, но при этом терпеть не могу C?

19.06.2021 18:15:23 | Автор: admin
Мне на удивление часто приходится говорить о том, почему мне всё ещё нравится язык C, и о том, почему я плохо отношусь к C++. Поэтому я решил, что мне стоит об этом написать, а не снова и снова повторять одно и то же.



Как это обычно бывает у C-программистов, язык C не был ни моим первым языком, ни языком, после которого я уже не изучал ничего другого. Но мне всё ещё нравится этот язык, и когда мне нужно писать программы я выбираю именно его. Правда, в то же время, я стараюсь быть в курсе того, что происходит в мире современных (и не очень) языков программирования. Я слежу за тенденциями в этой сфере и пишу собственный хобби-проект, связанный с мультимедийными технологиями, на Rust. Почему же я до сих пор не поменял C на что-то более современное? И при чём тут C++?

Почему C это не самый лучший язык программирования?


Сразу скажу то, что, пожалуй, и так все знают: нет такого понятия, как самый лучший язык программирования. С каждым языком связан набор задач, для решения которых он подходит лучше всего. Например, хотя и можно заниматься трассировкой лучей в Excel, применяя VBA, лучше будет делать это с использованием более подходящего языка. Поэтому полезно знать об ограничениях языков программирования чтобы не жаловаться на то, что веб-серверы не пишут на Fortran, и на то, что почти нигде Perl или C++ не используется в роли внутренних скриптовых языков. C может считаться не очень хорошим языком по причинам, которые я перечислю ниже (это помимо того, что язык этот просто очень старый и того, что его нельзя назвать активно развивающимся языком, но это дело вкуса).

В синтаксисе C имеются неоднозначные конструкции (например, символ * может играть роль обычного оператора умножения, может применяться в виде унарного оператора разыменования, или может использоваться при объявлении указателей; а радости работы с typedef достойны отдельной статьи).

Этот язык не является безопасным. Например в C-программах довольно часто встречаются ошибки, связанные с обращением к элементам массивов, которые находятся за пределами границ массивов. В языке нет проверок на ошибки такого рода, производимых во время выполнения программы. А вот, например, в Borland Pascal, не говоря уже о более современных языках, такие проверки есть (это плюс, даже если подобные возможности можно, пользуясь параметрами компиляции, отключить ради повышения производительности). А использование в языке указателей ещё сильнее усложняет задачу программиста по поддержанию кода в хорошем состоянии. И, кроме того, C имеет и некоторые другие неприятные особенности, вроде возможности вызова функции без объявления прототипа, в результате чего функции легко можно передать аргумент неправильного типа.

У C довольно-таки скромная стандартная библиотека. В некоторых других языках программирования могут даже иметься стандартные веб-серверы (или, как минимум, всё, что нужно для создания таких серверов), а в стандартной библиотеке C нет даже описаний контейнеров.

Почему я, несмотря на все недостатки C, пользуясь именно этим языком?


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

Например, если нужно получить значение элемента массива, имея два смещения, одно из которых может быть отрицательным числом, то при программировании на C можно воспользоваться такой конструкцией: arr[off1 + off2]. А при использовании Rust это уже будет arr[((off1 as isize) + off2) as usize]. C-циклы часто короче, чем идиоматичные механизмы Rust, использование которых предусматривает комбинирование итераторов (конечно, обычными циклами можно пользоваться и в Rust, но это не приветствуется линтером, который всегда рекомендует заменять их итераторами). И, аналогично, мощными инструментами являются memset() и memmove().

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

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

При чём тут C++?


Если говорить о C++, то хочу сразу сказать, что я не отношусь к тем, кто ненавидит этот язык. Если вы этим языком пользуетесь и он вам нравится я ничего против этого не имею. Я не могу отрицать того, что C++, в сравнении с C, даёт нам два следующих преимущества. Во-первых это улучшение структуры программ (это поддержка пространств имён и классов; в Simula, в конце концов, есть хоть что-то хорошее). Во-вторых это концепция RAII (если описать это в двух словах, то речь идёт о наличии конструкторов для инициализации объектов при их создании и о наличии деструкторов для очистки ресурсов после уничтожения объектов; а если глубже разработать эту идею, то можно прийти к понятию времени жизни объекта из Rust). Но, в то же время, у C++ есть несколько особенностей, из-за которых мне этот язык очень и очень не нравится.

Это, прежде всего склонность языка постоянно вбирать в себя что-то новое. Если в каком-то другом языке появится какая-нибудь возможность, которая станет популярной, она окажется и в C++. В результате стандарт C++ пересматривают каждые пару лет, и при этом всякий раз в него добавляют новые возможности. Это привело к тому, что C++ представляет собой монструозный язык, которого никто не знает на 100%, язык, в котором одни возможности часто дублируют другие. И, кроме того, тут нет стандартного способа указания того, возможности из какой редакции C++ планируется использовать в коде. В Rust это поддерживается на уровне единицы компиляции. В IIRC C++ для той же цели предлагалась концепция эпох, но эта затея не удалась. И вот одно интересное наблюдение. Время от времени мне попадаются новости о том, что кто-то в одиночку (и за приемлемое время) написал рабочий компилятор C. Но я ни разу не встречал похожих новостей о компиляторе C++.

Вторая особенность C++, из-за которой мне не нравится этот язык, заключается в том, что это, на самом деле, не просто смесь нескольких языков. Это, кроме того, мета-язык, иначе говоря язык, в котором используются шаблоны. Я понимаю для чего это создано, согласен с тем, что это лучше, чем препроцессор C для типонезависимого кода. Но, на самом деле, это приводит к появлению некрасивого громоздкого кода. Это означает переход от идеи заголовочный файл содержит объявления, а компилируемый код функционал к идее заголовочный файл содержит весь код, используемый в проекте, в состав которого входит этот файл. Мне не нравится, когда код долго компилируется, а этот подход ведёт к увеличению времени компиляции проектов.

И, наконец, я мог бы вообще не обращать внимания на C++, если бы этот язык не был бы связан с C и не оказывал бы на C плохое влияние. Я не говорю о ситуации, когда, говоря о C и C++, их объединяют, упоминая как C/C++, и считая, что тот, кто знает C, знает и C++. Я имею в виду связь между языками, которая оказывает влияние и на стандарты, и на компиляторы. С одной стороны C++ основан на C, что придало C++, так сказать, хорошее начальное ускорение. А с другой стороны сейчас C++, вероятно, лучше смотрелся бы без большей части своего C-наследия. Конечно, от него пытаются избавиться, называя соответствующие конструкции устаревшими, но C-фундамент C++ никуда пока не делся. Да и будет ли популярным, скажем, некий С++24, вышедший в виде самостоятельного языка, основанного на C++21 и лишённого большей части устаревших механизмов? Не думаю.

Воздействие компиляторов C++ на C


Вышеописанная связь C и C++ приводит к тому, что к C относятся как к C++, лишённому некоторых возможностей. Печально известным примером такого восприятия C является C-компилятор Microsoft, разработчики которого не позаботились о поддержке возможностей C99 до выхода версии компилятора 2015 года (и даже тогда разработчики придерживались стратегии bug-for-bug compatibility, когда в новой реализации чего-либо воспроизводят старые известные ошибки; делалось это для того, чтобы пользователи компилятора не были бы шокированы, внезапно обнаружив, что вариадические макросы вдруг там заработали). Но тот же подход можно видеть и в стандартах, и в других компиляторах. Эти проблемы связаны друг с другом.

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

Я, конечно, говорю, о неопределённом поведении, и о том, как оно рассматривается компиляторами. Эта тема стала популярным пугалом (код полагается на способ представления чисел с дополнением до двух; в результате в нём имеется неопределённое поведение и компилятор может выбросить оптимизировать целый блок кода!).

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

  • Поведение, определяемое архитектурой системы (то есть то, что зависит от архитектуры процессора). Сюда, в основном, входят особенности выполнения арифметических операций. Например, если я знаю, что целевая машина использует для представления чисел дополнение до двух (нет, это не CDC 6600), то почему компилятор (который, как представляется, тоже знает об особенностях целевой архитектуры) должен считать, что система будет вести себя иначе? Ведь тогда он сможет лучше выполнять некоторые теоретически возможные оптимизации. То же применимо к операциям побитового сдвига. Если я знаю о том, что в архитектуре x86 старшие биты, выходящие за пределы числа, отбрасываются, а на ARM отрицательный сдвиг влево это сдвиг вправо, почему я не могу воспользоваться этими знаниями, разрабатывая программу для конкретной архитектуры? В конце концов, то, что на разных платформах целые числа имеют разные размеры в байтах, считается вполне приемлемым. Компилятор, обнаружив нечто, рассчитанное на конкретную платформу, может просто выдать предупреждение о том, что код нельзя будет перенести на другую платформу, и позволить мне писать код так, как я его писал.
  • Неочевидные приёмы работы с указателями и каламбуры типизации. Такое ощущение, что плохое отношение к подобным вещам возникло лишь ради потенциальной возможности оптимизации кода компиляторами. Я согласен с тем, что использование memcpy() на перекрывающихся областях памяти может, в зависимости от реализации (в современных x86-реализациях копирование начинается с конца области) и от относительного расположения адресов, работать неправильно. Разумно будет воздержаться от подобного. А вот другие ограничения того же плана кажутся мне менее оправданными. Например запрещение одновременной работы с одной и той же областью памяти с использованием двух указателей разных типов. Я не могу представить себе проблему, возникновение которой может предотвратить наличие подобного ограничения (это не может быть проблема, связанная с выравниванием). Возможно, сделано это для того чтобы не мешать компилятору оптимизировать код. Кульминацией всего этого является невозможность преобразования, например, целых чисел в числа с плавающей запятой с использованием объединений. Об этом уже рассуждал Линус Торвальдс, поэтому я повторяться не буду. С моей точки зрения это делается либо для улучшения возможностей компиляторов по оптимизации кода, либо из-за того, что этого требует C++ для обеспечения работы системы отслеживания типов данных (чтобы нельзя было поместить экземпляр некоего класса в объединение, а потом извлечь его как экземпляр совсем другого класса; это тоже может как-то повлиять на оптимизации).
  • Поведение кода, определяемое реализацией (то есть поведение, которое не в точности отражает то, что предписано стандартом). Мой любимый пример подобной ситуации это вызов функции: в зависимости от соглашения о вызове функций и от реализации компилятора аргументы функций могут вычисляться в совершенно произвольном порядке. Поэтому результат вызова foo(*ptr++, *ptr++, *ptr++) неопределён и на подобную конструкцию не стоит полагаться даже в том случае, если программисту известна целевая архитектура, на которой будет выполняться код. Если аргументы передаются в регистрах (как в архитектуре AMD64) компилятор может вычислить значение для любого регистра, который покажется ему подходящим.
  • Полностью неопределённое поведение кода. Это тоже такой случай, когда сложно спорить со стандартом. Пожалуй, самый заметный пример такого поведения кода связан с нарушением правила, в соответствии с которым значение переменной в одном выражении можно менять лишь один раз. Вот как это выглядит в одном знаменитом примере: i++ + i++. А вот пример, который выглядит ещё страшнее: *ptr++ = *ptr++ + *ptr++е.

C++ это язык более высокого уровня, чем C. В то время, как в C++ имеется большинство возможностей C, использовать эти возможности не рекомендуется. Например, вместо прямого преобразования типов надо применять reinterpret_cast<>, а вместо указателей надо применять ссылки. От С++-программистов не ждут того, что они будут понимать низкоуровневый код так же хорошо, как C-программисты (это, конечно, лишь средняя температура по больнице, в реальности всё зависит от конкретного программиста). И всё же, из-за того, что существует очень много C++-программистов, и из-за того, что C и C++ часто воспринимают как C/C++, C-компиляторы часто расширяют в расчёте на поддержку ими C++ и переписывают на C++ для того чтобы упростить реализацию сложных конструкций. Это произошло с GCC, и того, кто начнёт отлаживать с помощью GDB С++-код, находящийся в .c-файлах, ждёт много интересного. Грустно то, что для того чтобы скомпилировать код C-компилятора нужен C++-компилятор, но, чему нельзя не радоваться, всё ещё существуют чистые C-компиляторы, вроде LCC, PCC и TCC.

Итоги


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

Но, по крайней мере, нельзя заменить C90 на какой-нибудь C90 Special Edition и сделать вид, что C90 никогда не существовало.

Как вы относитесь к языку C?


Подробнее..

Перевод Оптимизация веб-графики в 2021 году

20.06.2021 18:15:44 | Автор: admin
Изображения, используемые на веб-страницах, привлекают пользователей, пользователи довольно-таки охотно щёлкают по ним мышью. Изображения делают веб-страницы лучше во всём кроме скорости работы страниц. Изображения это огромные куски байтов, которые обычно являются теми частями сайтов, которые загружаются медленнее всего. В этом материале я собрал всё, что нужно знать в 2021 году об улучшении скорости работы веб-страниц через оптимизацию работы с изображениями.



Изображения обычно имеют большие размеры. Даже очень большие. В большинстве случаев CSS- и JavaScript-ресурсы, необходимые для обеспечения работоспособности страниц это мелочь в сравнении с тем объёмом данных, который нужно передать по сети для загрузки изображений, используемых на страницах. Медленные изображения могут повредить показателям Core Web Vitals сайта, могут оказать воздействие на SEO и потребовать дополнительных затрат на трафик. Изображения это обычно тот самый ресурс сайта, который оказывает решающее воздействие на показатель Largest Contentful Paint (LCP) и на задержки загрузки сайта. Они способны увеличить показатель Cumulative Layout Shift (CLS). Если вы не знакомы с этими показателями производительности сайтов почитайте о них в Definitive Guide to Measuring Web Performance.

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

1. Формат изображений


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

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


Изображения ленивца

Слева мы можем видеть фото нашего товарища-ленивца Сэма. Эта картинка в формате JPG занимает всего лишь 32,7 Кб. А если то же самое изображение преобразовать в формат PNG размер графического файла увеличится более чем вдвое до 90,6 Кб!

Справа находится рисунок со всё тем же Сэмом. Этот рисунок лучше всего хранить в формате PNG. Так он занимает всего 5,5 Кб. А если преобразовать его в JPG, то его размер подскочит до 11,3 Кб.

Обратите внимание: всё то, что фотографиями не является, обычно занимает меньше места, чем фотографии. Не забывайте об этом, проектируя свои веб-страницы.

Существует, конечно, ещё много графических форматов! Если у вас имеется некое векторное изображение (состоящее из всяческих линий и геометрических фигур), то вам лучше всего подойдёт формат SVG. Более новые браузеры поддерживают и более современные графические форматы вроде AVIF и WebP. Их использование для хранения подходящих изображений позволяет добиться ещё более серьёзного уменьшения размеров графических файлов.

2. Отзывчивые изображения и их пиксельные размеры


Не все посетители сайта будут просматривать его в одних и тех же условиях. У кого-то имеется огромный монитор шириной в 1600 пикселей. А кто-то смотрит сайт на планшете с шириной экрана в 900 пикселей, или на телефоне с экраном шириной в 600 пикселей. Если на сайте применяется изображение шириной в 1200 пикселей это будет означать, что при просмотре такого сайта на устройствах с небольшими экранами сетевые и другие ресурсы будут тратиться впустую, так как размер таких изображений при выводе на экран, всё равно, будет уменьшен.


Просмотр сайта на устройствах с разными экранами

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

<img src="picture-1200.jpg"srcset="picture-600.jpg  600w,picture-900.jpg  900w,picture-1200.jpg 1200w"sizes="(max-width: 900px) 100vw, 1200px"alt="my awesome picture" height="900" width="1200" />

В данном случае ширина базового изображения составляет 1200 пикселей. Оно, кроме того, является изображением, записанным в атрибут src тега и используемым по умолчанию. В srcset описаны 3 варианта изображения шириной в 600, 900 и 1200 пикселей. В sizes используются медиа-запросы CSS, позволяющие дать браузеру подсказку, касающуюся видимой области, доступной для вывода изображения. Если ширина окна меньше 900 пикселей место, где будет выведено изображение, займёт всю его ширину 100vw. В противном случае место для вывода изображения никогда не окажется шире 1200 пикселей.

Большинство инструментов для работы с изображениями, вроде Photoshop, Gimp и Paint.NET, умеют экспортировать изображения в различных размерах. Стандартные системные графические программы тоже, в определённых пределах, способны решать подобные задачи. А если надо автоматизировать обработку очень большого количества изображений возможно, есть смысл взглянуть на соответствующие инструменты командной строки вроде ImageMagick.

Скрытие изображений при просмотре сайта на мобильных устройствах


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

<img src="picture-1200.jpg"srcset="picture-600.jpg  600w,picture-900.jpg  900w,picture-1200.jpg 1200w"sizes="(max-width: 600px) 0, 600px"alt="my awesome picture" height="900" width="1200" />

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

3. Качество изображений


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


Исходное PNG-изображение с прозрачными участками имеет размер 57 Кб. Такое же изображение, но сжатое, имеет размер 15 Кб.

Уменьшение размера изображения достигается путём удаления из него неиспользуемых цветов, или путём комбинирования цветов и пикселей, которые очень похожи друг на друга. Но, на самом деле, в тонкости всего этого можно и не вдаваться. Подбор приемлемого уровня сжатия изображений можно доверить специализированным инструментам. Например самые разные задачи по сжатию изображений хорошо решают TinyPNG и ImageMin.

4. Встраивание изображений в веб-страницы


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

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


Изображение, встроенное в страницу

Может, выглядит это и странновато, но тут перед нами так называемый Data URL. Такие URL пользуются полной поддержкой всех браузеров. В атрибуте src сказано, что соответствующие данные надо воспринимать как PNG-изображение в кодировке base64. После описания изображения идёт набор символов, представляющих содержимое этого изображения. В данном случае это маленькая красная точка.

Google часто использует встроенные в страницу изображения при выводе результатов поиска. Это одна из причин того, что поиск Google кажется таким быстрым.

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

А вот удобный веб-инструмент для преобразования изображений в формат base64.

5. Ленивая загрузка изображений


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

Вместо того, чтобы заставлять браузер сразу загружать все изображения, можно посоветовать ему немного полениться. Ленивая загрузка изображений это такой подход к работе с изображениями, когда браузеру предлагают загружать изображения только тогда, когда они могут понадобиться пользователю. Применение ленивой загрузки изображений способно оказать огромное положительное влияние на показатель Largest Contentful Paint (LCP), так как благодаря этому браузер, при загрузке страницы, может уделить основное внимание только самым важным изображениям.

В спецификации HTML есть атрибут loading, которым можно воспользоваться для организации ленивой загрузки изображений, но он пока имеет статус экспериментального. А Safari ещё даже его не поддерживает. В результате о его широком использовании говорить пока нельзя. Но, к счастью, ленивую загрузку изображений можно организовать средствами JavaScript.

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

var lazyEls = [].slice.call(document.querySelectorAll("[data-src]"));var lazyObserver = new IntersectionObserver(function(entries) {entries.forEach(function(entry) {if (entry.isIntersecting) {var el = entry.target;var src = el.getAttribute("data-src");if (src) { el.setAttribute("src", src); }lazyObserver.unobserve(el);}});});lazyEls.forEach(function(el) {lazyObserver.observe(el);});

Тут, для определения того момента, когда надо загружать изображение, используется объект IntersectionObserver. Когда наступает нужный момент содержимое атрибута data-src копируется в атрибут src и изображение загружается. Тот же подход можно применить к атрибуту srcset и воспользоваться им при работе с любым количеством изображений.

Пользуются этим, переименовывая атрибут src в data-src.

<img data-src="picture-1200.jpg"loading="lazy" class="lazy" />

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

Настройка размеров области, которую займёт изображение


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

Избежать сдвига макета страницы можно, указав атрибуты height и width тега <img>.

<img data-src="picture-1200.jpg"loading="lazy" class="lazy"width="1200" height="900" />

Обратите на то, что значения атрибутов height и width это не 1200px и 900px. Это просто 1200 и 900. И работают они немного не так, как можно было бы ожидать. Размер соответствующего изображения не обязательно будет составлять 1200x900 пикселей. Этот размер будет зависеть от CSS и от размеров макета страницы. Но браузер, благодаря этим атрибутам, получит сведения о соотношении сторон изображения. В результате, узнав ширину изображения, браузер сможет правильно настроить его высоту.

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

Итоги


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

Как вы оптимизируете изображения, используемые в ваших веб-проектах?


Подробнее..

Недоумение про ещё один корпоративный чат или как сделать приятно всем

21.06.2021 12:17:59 | Автор: admin

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

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

Чтоб было безопасно. Т.е. чтоб не просто data scientist модный в шортиках из одной американской/китайской/российской компании не мог в рамках своих задач узнать что-то полезное, а даже рядовой админ банка не смог увидеть или унести переписку одного уважаемого человека с другим. Даже в качестве картинки. Даже на свой админский супер-защищённый комп. Следовательно, оно должно быть self-hosted разворачиваемо отдельно и полностью контролируется исключительно теми, кому положено следить и зарплату за это платят. Ещё нужно подключиться к системам, отвечающим за безопасность передаваемого контента. Ещё нужно иметь в руках команды сопровождения все возможные рычаги, чтобы нерадивого пользователя можно было ограничить в желании другому пользователю передавать то, что не положено.

Чтобы было удобно. Сейчас на дворе 2021 год. Но даже закачать справочник пользователей или синтегрить с корпоративной телефонией банка что-то это уже подвиг на грани фантастики. И удивлению моему не было предела тот же slack обладает пользовательским интерфейсом, который физически невозможно объяснить курьеру из доставки подавай ему пользовательский интерфейс ala telegram. И желательно с видео конференциями встроенными. И прям очень нужен голосовой виртуальный ассистент, голосом удобнее. Ещё невозможно объяснить человеку, у которого есть одновременно два телефона, планшет и два компьютера почему ему нужно выбрать, где же можно работать с этим мессенджером, а где остаться без мессенджера. Ну и зачем каждый раз свой номер телефона светить не ясно.

Чтобы было удобно для внутренних коммуникаций. Тут приходят умные люди из разных отделов, департаментов и цельных предприятий и говорят нам возможность узконаправленных рассылок нужна. Таргетированных, как это модно называть. По полу, по городу, по региону, по подразделению, по должности и т.д. И в этот момент все open-source решения для чатов (а их только на github больше 2100 штук) куда-то деваются. Остаются те, кто реально зарабатывает. Но первый пункт не выполняется.

Чтобы развитие продукта помогало бизнесу, а не мешало всем подряд. Удивительно, но с этой точки зрения почти никто не смотрит. Сколько времени сотрудник тратит на поиск телефона в адресной книге где-то там, потом нужно найти телефон, чтобы позвонить и на этом телефоне набрать 11 заветных цифр. И выяснить, например, что номер с ошибкой. Гораздо удобнее нашёл ФИО, посмотрел фото и сразу набрал. Нужно ещё двоих подключить аналогично набрал и добавил. И никакой музыки от абонента, которому кто-то в это время позвонил, портящей всем 114 остальным участникам совещания не только настроение. 2021 на дворе. И чтоб если нужно любой модуль за месяц прикрутить можно было. Ну хорошо, иногда за два

Отсутствие зависимости от вендора и его капризов. Если ты маленькая организация из 50 человек (а по статистике таких ох как много), тебе нужно решение готовое. Даже когда 3000 человек нужно обслужить вопрос даже не стоит идёшь и выбираешь решение. Можно даже покапризничать и тендер объявить. А если у тебя 400 000 сотрудников? А если миллион планируется? Тут и вендоров вечных с хорошим SLA мало, и возможности их контролировать тоже не велики. Или вендор маленький и может случайно помереть при очередном кризисе или принятии закона/уехать ему понадобится всей командой, или вендор большой, но его мало интересуют проблемы конкретного клиента у него самого может быть 40 000 сотрудников и 1000 таких же клиентов по миру.

Казалось бы каждое из этих пяти требований по отдельности легко покрывается тысячами продуктов. Если взять и выкинуть одно тоже есть десятки. А вот всё вместе и сразу можно только самим мучительно делать. Чем и занимаемся. И вас зовём.

Ну и на всякий случай сошлюсь на бородатые требования одного habrовода (http://personeltest.ru/aways/habr.com/ru/post/405887/ - их мы тоже учли и удовлетворили): кроссплатформенность. Чтоб я наконец-то мог сидя на обеде, или в транспорте, или в отпуске кому-то что-то написать с телефона, да и узнать, что мне кто-то написал. И чтоб мой коллега, у которого Линукс, не делал каждый раз печальное лицо при слове чат. заточенный под общение в компаниях. Чтоб у меня был чат, где есть все мои коллеги и только мои коллеги живой активный проект. Чтобы баги, как застывшие в янтаре насекомые, не висели в продукте до конца времён передача файлов. Ну зачем мне заливать эту картинку в общую папку, если я просто могу кинуть её через чат! нормальная синхронизация уведомлений / непрочитанного. Чтобы не как в Скайпе словил сообщение, и потом в течение 24 часов находишь уведомление о нём на каждом своём девайсе.

Продолжение следует.

Подробнее..

Есть будущее у Fullstack-разработчиков?

05.06.2021 16:05:21 | Автор: admin

"Неужели компании хотят так сильно экономить, что готовы терять в качестве и времени?"

Решил поделиться своим опытом, который достаточно тесно связан с Fullstack-разработчиками, в одном стартап (хотя бьются на рынке с 2016 года).

Сразу прокомментирую, что в этой статье нет цели оклеветать или высказать своё негативное мнение о компаниях. Нет! Я опираюсь только на их опыт и на полученные результаты.

Я занимаюсь подбором IT-специалистов и для того, чтобы не быть "тем самым HR, который сливает , а не помогает, своими оценочными тестами и вопросами вне понимания специфики работы", мне приходится изучать гигабайты информации на habr и других ресурсах. И в первых рядах моего несогласия, есть такая профессия - Fullstack-разработчик.

Немного для общего понимания поясню, почему я выбрал подход собирать информацию об IT-специалистах.

С 2018 года я начал вникать в эту сферу более плотно. Освоил основы HTML/CSS, познакомился с Python, PHP, Swift. Естественно эти познания подтолкнули меня сменить компанию из обычной на IT. Мне повезло и меня взяли в достаточно перспективный на мой взгляд стартап (разглашать не буду его название из добрых побуждений). Первые месяца три, я работал на должности специалиста по работе с клиентами, но сейчас понимаю, что на самом деле выполнял функционал Project-manager и одновременно Product Owner. Ну и по совместительству еще продавец и support.

Мне реально нравился проект и я горел его улучшить. Хотите верьте, хотите нет, но в этом проекте я стал хуже спать. Я постоянно думал, что можно улучшить. Я стал предлагать идеи по улучшению своему непосредственному, а тот вообще не про IT и очень скоро я понял, что я ему говорю свои идеи, а он просто не правильно их "продает" генеральному. Я не хочу сказать, что этот руководитель плохой. Наоборот, он достаточно внимательно относился ко всем сотрудникам и реально хотел донести мои идеи генеральному, но просто не владея техническим восприятием, он не мог объяснить так, как это говорил ему я.

Меня же это совсем не устраивало. Я хотел менять этот стартап. Я начал настаивать, чтобы он пригласил генерального на совещание и позвали меня тоже. Так я смог объяснить самостоятельно, что в проекте работает на мой взгляд не корректно и что и самое главное, КАК, нужно улучшить.

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

И вот мне все же пришлось упереться в стену, которую непроизвольно построили разработчики. Сейчас вы поймете о чем я. Проработав почти год в проекте и коммуницируя с Team Lead, я не знал, сколько в команде разработчиков. Мне пояснили, что проект новый на рынке (хотя я знал точно, что они не уникальны) и что не могут себе позволить разработчиков со стороны. Что есть команда, (как позже я все же выяснил, что она из 5 человек), в которой есть back&front и парочка как раз Fullstack-разработчиков. На них и держался весь проект.

Естественно они сильные специалисты и я и сейчас в шоке, как они все это стойко держали. Да, Scrum (Agile), Jira. Пользовались теми же инструментами, которые хвалил рынок. Но "бэклог" еженедельно рос. Спрос на продукт стремительно набирал обороты и я начал "кричать", пытаясь быть услышанным, что нам срочно нужно расширять команду разработчиков и причем принципиально , среди них не должны быть больше Fullstack. К тому моменту, когда негодование users начинало зашкаливать, из-за постоянных срывов обещанных дедлайнов, я явно видел, что для быстрого роста и своевременного исправления скопившихся багов, нужно брать людей на отдельные блоки. И вот тут-то мы и выяснили, что Fullstack-разработчики писали код так, что его нельзя пока что разделить на блоки. Что если пустить со стороны человека, то он будет видеть весь код. Увы, но такова была реальность. Приняли решение максимально срочно декомпозировать, но вы наверно знаете, что на практике это совсем не просто.

"Страсти" в этом проекте накалялись. Разработчики сутками пытались исправлять баги, про которые ежедневно менеджерам по работе с клиентами звонили клиенты. Все мы принимали еженедельно новые алгоритмы, которые полностью меняли структуру работы и максимально выводили из процесса разработчиков (я их нарисовал 5 в период 7 месяцев). Пытались максимально разгрузить разработчиков, сняли все, что можно получить временно внешними ресурсами (сайт на Битрикс, CRM и так далее). Но в итоге, это не дало нужных результатов. Спустя 2 года, я покинул проект.

И вот сейчас, находясь в поисках IT-специалистов для заказчиков, я все чаще вижу вакансию Fullstack-разработчик. Неужели компании хотят так сильно экономить, что готовы терять в качестве и времени?

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

Надеюсь кому-то будет полезен мой опыт. Я не в коем случае не претендую на истинность и неизменность своего мнения по отношению к Fullstack-разработчикам, но на данный момент, я думаю так как написал.

Успешных вам проектов!

Подробнее..

Чиним проблемы нагрузок в Go с помощью настройки пула HTTP-соединений

09.06.2021 20:16:20 | Автор: admin

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

Проблема скрывалась внутри нашего API Gateway. Это сервис, который реализует паттерн Фасад и предоставляет единственное торчащее наружу окно к микросервисам.

В упрощенном виде его работу можно представить так:

  1. Проверить аутентификацию и авторизацию с помощью HTTP-запроса в сервис аутентификации

  2. Спроксировать запрос в нужный сервис, который определяем по пути и методу запроса пользователя

Иллюстрация работы API GatewayИллюстрация работы API Gateway

Конец декабря время роста нагрузок и числа ошибок

Настал конец декабря. Вместе с ним к нам в поддержку стали приходить вот такие обращения:

При работе в ЛК возникают постоянные ошибки системные по 10-20 раз на дню и больше. Просьба исправить и наладить работу площадки.

Ошибки возникали на стороне API Gateway. Мы полезли в логи за подробностями и увидели ошибки, похожие на таймауты обращения к сервису аутентификацию:

{err_type: context.deadlineExceededError, err: context deadline exceeded}{err_type: *errors.errorString, err: context canceled}

Трейсы в Jaeger показали ровно такую же картину мы не дожидались ответа от сервиса аутентификации за 2 секунды. Поэтому между нами и разработчиками сервиса аутентификации произошёл примерно такой диалог:

- Ребята, кажется, вы таймаутите. Вот трейс, на котором видно, что мы не дождались от вас ответа за 2 секунды.

- Ничего подобного, у нас все норм мы за 200 миллисекунд отвечаем в 99% запросов. А вот вы по какой-то причине часто преждевременно обрываете соединение.

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

Скриншот с множеством ошибок Cancelled by clientСкриншот с множеством ошибок Cancelled by client

Итого, мы имеем:

  1. Используемый нами сервис аутентификации стабильно отрабатывает за 200 миллисекунд.

  2. Многие наши обращения к этому сервису таймаутят за 2 секунды.

Причина проблемы: дефолтные настройки в Go

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

$ ss -natp state time-wait | awk '{print $4}' | sort -nr | uniq -c | sort -nr | head1053 10.20.49.117:801030 10.20.49.92:801016 10.20.49.91:801014 10.20.54.129:801013 10.20.53.213:801008 10.20.53.173:80969 10.20.53.172:80

Эта команда показывает количество TCP-сокетов в состоянии TIME_WAIT до разных удалённых портов. Если коротко, то состояние TIME_WAIT это де-факто закрытое клиентом соединение. Linux по возможности предотвращает повторное использование этих пар на протяжении 60 секунд, чтобы защититься от того, что старые пакеты помешают вновь установленному TCP-соединению.

Но для нас важно другое. Само существование TCP-соединения означает, что соединение установилось и закрылось. Если такая ситуация происходит массово, то мы имеем дело с накладными расходами на DNS-резолвинг и установку соединения. В результате этого время HTTP-запроса может увеличиваться. Избежать эту проблему помогают пулы соединении. В Go для этои цели используется абстракция http.Transport.

Здесь мы вплотную приближаемся к истокам проблемы. Мы для всех клиентских запросов использовали http.DefaultTransport. Он обладает следующими параметрами:

var DefaultTransport RoundTripper = &Transport{    Proxy: ProxyFromEnvironment,    DialContext: (&net.Dialer{        Timeout:   30 * time.Second,        KeepAlive: 30 * time.Second,    }).DialContext,    ForceAttemptHTTP2:     true,    MaxIdleConns:          100,    IdleConnTimeout:       90 * time.Second,    TLSHandshakeTimeout:   10 * time.Second,    ExpectContinueTimeout: 1 * time.Second,}

Среди перечисленных выше параметров к настройке пула соединений имеют отношения два:

  • MaxIdleConns число соединений, которое разрешается иметь в состоянии Idle (т.е. открытых TCP-соединений, которые в данный момент не используются);

  • IdleConnTimeout время, через которое закрываются такие неактивные соединения.

Однако в DefaultTransport совершенно не указан другой ключевой параметр MaxIdleConnsPerHost. Он отвечает за то, сколько неактивных TCP-соединений допускается устанавливать на один хост.

При этом если MaxIdleConnsPerHost не указан, тогда используется значение по умолчанию:

const DefaultMaxIdleConnsPerHost = 2

Поскольку мы использовали именно http.DefaultTransport для всех запросов, мы получили следующую проблему.

Представим, что нам одновременно понадобилось установить 10 соединений до сервиса аутентификации. Тогда хотя бы для 8 из них будут открыты и вскоре тут же закрыты TCP-соединения, из-за ограничения MaxIdleConnsPerHost. Если такая ситуация будет повторяться часто, у нас будет больше накладных расходов на один HTTP-запрос, поскольку для него понадобится новое соединение. Из-за этого вероятность таймаутов возрастает.

Решение: отдельный транспорт с особенными настройками

Чтобы решить проблему, мы сделали следующее:

  • Выделили отдельный транспорт под сервисы аутентификации и авторизации, чтобы транспорт под сервис аутентификации не вытеснял остальные сервисы.

  • Утилизировали выделенный пул на полную сделали так, чтобы значение MaxIdleConnsPerHost соответствовало значению MaxIdleConns:

func createOneHostTransport() *http.Transport {    result := http.DefaultTransport.(*http.Transport).Clone()    result.MaxIdleConnsPerHost = result.MaxIdleConns    return result}
График response time обращения к сервису аутентификацииГрафик response time обращения к сервису аутентификации

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

Но почему в Go такие настройки по умолчанию?

Возможно, у вас сейчас возник вопрос: зачем делать такие настройки, которые потом приходится исправлять? Неужели разработчики языка Go и библиотек к нему не подумали о том, как это будет использоваться на практике?

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

Если бы приложения могли говорить...

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

Приложение: http.Transport, привет! Нам тут сотня пользователей пришла одновременно и им всем вдруг срочно понадобилось посмотреть инфу о своём профиле. Установи, пожалуйста, сотню соединений к user-service

http.Transport: Конечно, вот получи сотню установленных соединений под запросы! Но я их не буду ещё полторы минуты закрывать, вдруг пригодятся .

Приложение: Всё сработало, спасибо! Только вот теперь им всем вдруг захотелось посмотреть информацию о товаре. Пожалуйста, установи соединение с product-service.

http.Transport: Да, не вопрос держи. Но только я их у себя в пуле соединений хранить не буду, поскольку он уже полностью забит соединениями до user-service, которые тебе больше не нужны.

Приложение: (_)

Разбираемся с непонятными таймаутами в Go: чеклист

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

  1. Проверьте метрики по скорости выполнения запросов к сторонним сервисам по HTTP. Если таких метрик нет, заведите пригодятся.

  2. Если видите расхождения в таймаутах клиента и сервера, проверьте количество соединений TIME_WAIT.

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

  4. Для хоста с неоправданно большим числом запросов к нему рассмотрите вариант заведения отдельного транспорта.

Что ещё почитать по теме

Подробнее..

Перевод Культура разработки ПО слишком позитивна, это может нам вредить

14.06.2021 12:13:27 | Автор: admin
image

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

Выгорание


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

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

Но как насчёт людей, которые не любят программирование, но всё же хорошо справляются со своей работой? Попробуйте сказать своим коллегами или в Twitter, что вы один из них, и вас сразу же заклеймят как плохого разработчика. Однако на самом ли деле это так? Люди, которые делают свою работу, а после работы занимаются чем-то другим, тоже могут быть потрясающими программистами. Не каждый обязан приходить домой и трудиться над хобби-проектами, писать посты в блог, записывать видео в YouTube о кодинге и читать книги о программировании.

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


Новые блестящие игрушки


Ещё один аспект, в отношении которого негатив воспринимается в штыки это отношение к новым трендам, создаваемым компаниями уровня FAANG. Попробуйте сказать что-нибудь против SOA или Docker. Попробуйте предложить более взрослый и зрелый язык или SSR. Это аналогично ситуации со страстью к работе. Люди мгновенно заявят, что ты плохой разработчик, потому что ты препятствуешь прогрессу.

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

Эта недавняя статья показывает реальность многих организаций: I Almost Got Fired for Choosing React in Our Enterprise App

Best Practices


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

Какие из этих универсальных best practices на самом деле не универсальны? DRY (Dont Repeat Yourself) часто упоминается в одном предложении с KISS (Keep It Simple Stupid), хотя часто они являются взаимоисключающими практиками. Simple означает отсутствие необязательных абстракций, однако если начинать сразу с DRY-кода, то он и ведёт к преждевременным абстракциям.

Лично я пользуюсь правилом 3X:

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



Многократно используемые компоненты создавать в три раза сложнее, чем одноразовые

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

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

Подведём итог


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

От нас, разработчиков, ожидают, что мы должны любить программирование. Но почему? Истина проста: это хорошо для работодателей. Сколько ужасных историй мы слышали из геймдева? Тем не менее, множество молодых людей мечтает о работе в игровой индустрии, хотя спустя несколько лет их заменят на новую поросль наивной молодёжи.

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

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

Я хочу пробовать что-то другое, и это совершенно нормально.



На правах рекламы


Наша компания предлагает аренду VPS для совершенно любых проектов. Создайте собственный тарифный план в пару кликов, максимальная конфигурация позволит разместить практически любой проект 128 ядер CPU, 512 ГБ RAM, 4000 ГБ NVMe!

Подписывайтесь на наш чат в Telegram.

Подробнее..

Открылся набор в Indie Games Accelerator и Indie Games Festival от Google Play

14.06.2021 12:13:27 | Автор: admin

Indie Games Accelerator и Indie Games Festival две программы для независимых (инди) разработчиков мобильных игр, организованных командой Google Play. Программы направлены на то, чтобы помочь небольшим игровым студиям и разработчикам стать популярнее в Google Play независимо от того, на какой стадии находятся их проекты.

В этом году обе программы пройдут в онлайн-формате, заявки принимаются до 1 июля подробности под катом.

Для нас важно поддерживать не только крупные международные компании, но и небольшие инди-команды благодаря своей креативности и увлеченности играми, они создают уникальные и интересные проекты. Если вы работаете над уникальным проектом и хотите, чтобы о нем узнал мир, предлагаем вам принять участие в одной (или обоих сразу) из наших программ Indie Games Accelerator и Indie Games Festival.

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

Indie Games Accelerator: обучение и менторская поддержка

Эта программа разработана для начинающих инди-разработчиков, которые работают над прототипами будущих игр или делают тестовые релизы.

Проекты, которые пройдут отбор и станут участниками акселератора, смогут присоединиться к 12-недельной образовательной программе, а также получат возможность поработать над своими проектами вместе с экспертами из Google, крупных игровых студий и венчурных фондов. Rovio, Game Insight, Zynga, Play Ventures, Unity Technologies, Belka Games с полным списком менторов и условиями участия можно ознакомиться здесь.

В этом году в акселерационной программе участвуют более 70 стран, заявки на Indie Games Accelerator из России, Украины и Беларуси будут приниматься впервые!

Indie Games Festival: промо-кампании для финалистов

Эта программа разработана для небольших команд, которые недавно выпустили игру и хотят дальше работать над проектом без помощи паблишера или инвесторов.

Основные критерии отбора: инновационность, увлекательность и дизайн. Среди призов: фичеринг на Google Play и промо-кампании для 3 игр-победителей стоимостью 100 000 евро.

Условия участия: в программе участвуют 29 стран Европы, включая Россию, Украину и Беларусь; максимальное количество человек в команде 50, игра должна быть выпущена на Google Play не ранее 3 марта 2020 г. Подробнее с правилами участия и критериями отбора можно ознакомиться здесь.

В прошлом году в финал конкурса прошло три проекта из России: My Diggy Dog 2 от King Bird Games, Color Spots от UX Apps и Tricky Castle от Team Tricky подать заявку можно до 1 июля.

Подробнее..

Дайджест интересных материалов для мобильного разработчика 396 (31 мая 6 июня)

06.06.2021 16:09:40 | Автор: admin
Сегодня в нашем дайджесте архитектурные паттерны и победители Swift Student Challenge, инициализация цепочек и цветов Fuchsia, инди-акселератор и инди-фестиваль от Google, Android 12 для разработчиков, $643 млрд из App Store и многое другое!



Этот дайджест доступен в виде еженедельной рассылки. А ежедневно новости мы рассылаем в Telegram-канале.

iOS

Архитектурные паттерны в iOS: привет от дядюшки Боба, или Clean Architecture
Тернистый путь внедрения Swift Package Manager. Доклад Яндекса
Swift и CoreData. Или как построить Swift ORM на основе Objective-C ORM
Как сделать экран подтверждения СМС-кода на iOS
Мои приложения для разработчиков вышли в топ iOS и Mac App Store: сколько это принесло?
WWDC21: Школьники и студенты из России победители Swift Student Challenge
Объявлены номинанты Apple Design Awards 2021
Добавляем поддержку Siri в iOS-приложение за считанные минуты
Как сериализовать и десериализовать объекты в iOS
Как улучшить время компиляции и выполнения Xcode
Удаление фона с помощью Core ML и SwiftUI
Как извлечь функциональность из устаревшего iOS-кода
Приложение для чата без пароля для iOS с Auth0
Как добавить Swift-код в качестве кастомной LLDB команды
Design to Code: превращая дизайн в код
SPIndicator: индикатор в стиле Apple

Android

Проекты в Gradle 7: как не зависеть от зависимостей
Всё о PendingIntents
Инициализация Rx цепочки
Proto DataStore + AndroidX Preferences на Kotlin
Подробный обзор Android 12 для разработчиков
Введение в систему Снапшотов Compose
Недоверенные события касания
Понимаем юнит-тесты для Android в 2021
Polestar предлагает эмулятор для разработчиков, создающих приложения для Android Automotive
QA-инженеры, функциональное и UI-тестирование в Azimo
10 лучших библиотек для разработчиков Android в 2021 году
Сохранение данных на Android с помощью Room Database и Data Store Руководство для начинающих
CheckboxQuestions: вопросы и чекбоксы
Compose Space Invaders: игра для декстопа на Jetpack Compose
Carousel Recyclerview: красивая карусель

Разработка

Как художнику найти работу мечты в геймдеве. А также советы по оформлению портфолио
4 технических решения, которые делают API сервис успешным
C# vs Kotlin
Как и зачем Mail.ru Group провела редизайн мобильной версии главной страницы портала
Mobile People Talks: какого же цвета Fuchsia?
Podlodka #218: схемотехника
HarmonyOS заработала на смартфонах
Новый SDK от Loomдобавляет видео-сообщения в любые веб-приложения
Facebook открывает Messenger API в Instagram для всех
Задачи с собеседований: зарплата
Дизайн приложений: примеры для вдохновения #44
Stack Overflow продан за $1.8 млрд
Что не так с Flutter?
Исследование продакт-менеджеров 2021 от Product Plan
Как оставаться в физической и ментальной форме, продолжая программировать
О создании гибкого пользовательского интерфейса на примере Instagram Threads
Представляем новый язык дизайна Material You от Google
Сеты бесплатных иконок для разработчиков и дизайнеров
Как привлечь первых 100 клиентов в SaaS: 5 простых шагов
Следующим стартапом на триллион станет образовательная компания
5 задач для автоматизации с помощью Python
Я не мог быстро тратить деньги, и это чуть не убило мой стартап
Flutter 2.2: создаем первую Universal Windows Program (UWP)
Мой код плохо пахнет, но все в порядке
Как создать свою первую Облачную функцию Firebase
5 вещей, которые я узнал после двух лет работы инженером-программистом в Microsoft
Test-driven Development для создания пользовательских интерфейсов
Мой опыт интервью в Twitter
Flutter: создание красивых приложений для Windows удобная структура дизайна и навигация
Вселенная no-code/low-code стартапов и ее игроки
Пример дизайна: Safe Space wellness-приложение для Android
База данных с вопросам из интервью в Apple

Аналитика, маркетинг и монетизация

В Android также ограничивают действие рекламного идентификатора
make sense: О запуске агротех-стартапа
Voodoo открывает летний конкурс гиперказуальных игр
Google запускает Indie Games Accelerator и Indie Games Festival
Продажи в App Store в 2020 выросли на 24% до $643 млрд
Создатели читов для PUBG Mobile заработали $77 млн
3 лучшие техники геймификации
Greg: приложение для любителей растений
Маркетплейс для разработчиков Malt получил 80 млн
Социальная сеть Poparazzi стала 1 App Store: секреты роста
Проектирование продуктов, формирующих привычки
Ошибки при расчете юнит-экономики
9 способов встроить виральность в ваш продукт
Как создать отличные скриншоты для страницы приложения в App Store

AI, Устройства, IoT

Учиться, учиться, и ещё раз учиться?
Теория игр как механизм для анализа крупномасштабных данных

Предыдущий дайджест. Если у вас есть другие интересные материалы или вы нашли ошибку пришлите, пожалуйста, в почту.
Подробнее..

Лайфхаки для роботизации 1С

03.06.2021 20:19:56 | Автор: admin

Несмотря на широкую распространенность платформы 1С в России и большое количество разработчиков автоматизация различных процессов, связанных с этими решениями, может потребовать значительного времени и длиться месяцами.

Основная проблема связана с тем, что все доработки, реализуемые 1С-программистами устанавливаются в лучшем случае поверх чистой конфигурации от самого вендора, а в худшем на целый пласт других доработок от других разработчиков.

Часто такие надстройки слетают при обновлении версии базовой конфигурации 1С, что требует как минимум их повторного включения, а чаще повторной разработки с учетом изменения внутренней кухни конфигурации от вендора.

Рассказываем о том, как при помощи UI Framework избежать подобных проблем роботизации в 1С. Благодаря этому фреймворку UiPath можно роботизировать процессы, в которых участвует несколько приложений, систем и сайтов, гораздо быстрее, чем при помощи программирования в 1С. Подробности ищите в посте.

Статья написана при поддержке архитектора, технического специалиста UiPath: Валентина Драздова.

Попытки обойти проблемы 1С снаружи

Некоторые разработчики не встраивают свои решения непосредственно в конфигурацию 1С, а создают отдельно стоящие приложения, которые обращаются непосредственно к 1С с использованием интерфейсов COM-объектов или взаимодействуют с сервером 1С через web-сервисы. Как правило, такой подход действительно устраняет проблему при обновлении конфигурации, так как обновление не влияет на внешнее ПО, а интерфейсы COM-объектов и веб-сервисов меняются крайне редко. Недостатком данного подхода является то, что от разработчиков требуется больше компетенций, чем просто программирование в 1С, а следовательно больше затрат на тестирование совместимости, что влечет за собой увеличение общей стоимости проекта.

Роботизация как ключ к быстром изменениям

С тех пор, как в Россию пришли решения на базе RPA разработчики начали применять данную технологию для роботизации. Важным преимуществом RPA является то, что такие решения не требуют глубоких знаний в профессиональной разработке ПО, а также использования COM-объектов и web-сервисов, RPA-разработчику необходимо лишь показать, как эту задачу делает реальный человек. Таким образом роботизацией могут заниматься те, у кого есть экспертиза не в программировании, а в своем деле, которое требует автоматизации бизнес-процессов.

Вызовы для RPA со стороны 1C

Однако, несмотря на все предоставляемые преимущества, RPA-платформы часто не дают полностью роботизировать любой процесс в 1С. Мы решили разобраться в теме и выявили причины.

Практически все RPA-решения взаимодействуют с интерфейсами одними и теми же способами:

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

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

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

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

Рис. 1 Пример проблемы выбора элемента меню в 1С 8.3 робот видит блок меню, но не может увидеть конкретные кнопки менюРис. 1 Пример проблемы выбора элемента меню в 1С 8.3 робот видит блок меню, но не может увидеть конкретные кнопки меню

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

UI Framework универсальная таблетка от UiPath

В отличие от множества других RPA-решений платформа UiPath предлагает использовать собственную разработку, которая называется UI Framework. Особенность фреймворка заключается в максимальной абстракции методов взаимодействия, что позволяет применять разные режимы поиска элементов интерфейса, в том числе и комбинации их вариантов для достижения наилучшего результата.

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

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

AA режим использования средств Microsoft Active Accessibility. В данном режиме UiPath использует интерфейс специальных возможностей Windows, который позволяет людям с плохим зрением взаимодействовать с программами. В основном данный метод наиболее применим для старых приложений, реализованных на чистом WinAPI без применения современных технологий.

UIA режим использования средств Microsoft UI Automation. В данном режиме UiPath использует методы доступа, предоставляемые для современных приложений Windows. Как правило, это приложения, использующие технологии WPF и универсальные приложения Windows (известные так же, как приложения магазина Windows 8/Windows 10).

Рис. 2 Пример успешного выбора элемента меню в 1С 8.3 благодаря выбору UI Framework UIA робот видит кнопки меню, а не блокРис. 2 Пример успешного выбора элемента меню в 1С 8.3 благодаря выбору UI Framework UIA робот видит кнопки меню, а не блок

Методики поиска Ui Framework надежнее, чем способы с координатами или изображением. Однако, только одного корректного выбора UI Framework недостаточно. Например, на формах ввода документов 1С можно встретить следующую проблему существует несколько полей, и ни одно поле не помечено специальными идентификаторами. Как результат после выбора одного поля робот видит его в нескольких местах сразу:

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

Использование разных режимов работы с интерфейсами позволяет достичь более высоких результатов при определении тех или иных компонентов управления в 1С, однако, только их недостаточно, и именно здесь приходят на помощь дополнительные средства:

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

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

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

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

Рис. 4 Пример решения проблемы выбора элемента за счет использования якоря с обозначением текста слева от поля. Как результат робот видит только нужное поле, что гарантирует корректный результат роботизации.Рис. 4 Пример решения проблемы выбора элемента за счет использования якоря с обозначением текста слева от поля. Как результат робот видит только нужное поле, что гарантирует корректный результат роботизации.

Повторное использование действий

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

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

Рис. 5 - Пример использования готовой настроки выбора кнопки "Печать" из репозитория объектовРис. 5 - Пример использования готовой настроки выбора кнопки "Печать" из репозитория объектов

Репозиторий объектов располагается на компьютере разработчика, что дает возможность применять добавленные селекторы во всех проектах, которыми он занимается. Если роботизацией занимается команда разработчиков - имеется возможность создания репозитория в специальной UI-библиотеке. UI-библиотеки можно распространять через оркестратор UiPath, благодаря чему все разработчики и роботы всегда будут иметь доступ к последней версии селекторов.

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

Рис. 6 Пример использования собственной библиотеки действий, созданной в UiPathРис. 6 Пример использования собственной библиотеки действий, созданной в UiPath

Адаптация к изменениям

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

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

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

Computer Vision дополнительная таблетка для сложных случаев

Несмотря на большие возможности поиска элементов управления в UI Framework в платформе 1С существует гораздо больше проблем для RPA-разработчиков, чем изначально можно предположить. Например, одной из сложностей при роботизации 1С являются таблицы. Если с веб-версией таблиц роботы от UiPath справляются очень легко, то вот волшебные Windows-таблицы, которые умеют превращаться в деревья, иногда ставят UI Framework в тупик. Однако, там где нельзя получить доступ к данным напрямую техническим способом имеется возможность использовать компьютерное зрение. Мы уже рассказывали о преимуществах компьютерного зрения при работе с удаленными рабочими столами. Однако, хотим лишь указать на то, что проблема с таблицами и прочими сложностями платформы 1С, отлично решается именно с использованием Computer Vision.

Кейс: как с помощью RPA заносить данные в 1С

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

Еще несколько плюсов роботизации 1С с помощью UiPath

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

Отдельно стоит отметить движение UiPath в сторону citizen developers, а именно предоставление бизнес-пользователям, далеким от программирования, возможности самостоятельно делать простых роботов, в том числе и для 1С. Таким образом, когда вашим сотрудникам понадобится роботизация они смогут сделать ее самостоятельно в короткие сроки не ожидая долгих согласований от отдела разработки (или же привлечения сторонних организаций и фрилансеров).

Подробнее..

Мы решили внедрить Agile-Lean принципы в процесс разработки на ходу и вот что из этого получилось

19.06.2021 12:05:20 | Автор: admin

Термин бережливого производства (Lean) в настоящее время на слуху. Мы все знаем результаты применения данной идеи в компании Toyota, которые позволили выпускать малые партии комплектующих точно в срок (Just-In-Time, JIT).

В книге Microsoft Secrets (1995 года) авторы (Кузумано и Ричард Селби) описали подходы контроля качества схожие с Lean применяемым в Toyota.

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

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

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

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

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

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

Отправная точка

Изначально в команде применялась несколько упрощенная методология Scrum. Ниже приведу ее описание.

Набор артефактов:

  1. Project backlog журнал требований, реализуемых в рамках проекта, обнаруженные в процессе эксплуатации инциденты. Обычно требования оформляются в виде User Story. В качестве инструмента для верхнеуровневого планирования использовали Excel. Там же, для удобства, чтобы все было в одном месте, на отдельной странице сделали диаграмму Ганта и диаграмму сгорания.

  2. Sprint backlog журнал требований и инцидентов реализуемых за спринт.

  3. Scrum-доска. В качестве инструмента использовали доску Trello с расширением Plus For Trello для контроля трудоемкости.

Роли в команде:

  1. Руководитель проекта сотрудник, который планирует процесс реализации требований, координирует действия всех участников, вплоть до завершения проекта.

  2. Scrum-мастер обычно тимлид группы разработки, занимается управлением процесса разработки, несет ответственность за конечный результат работы разработчиков.

  3. Команда разработчиков состоит из опытных разработчиков и новичков, которые повышают свои компетенции в процессе участия на проекте.

  4. Команда консультантов сотрудники погруженные в бизнес-процессы заказчика. Занимаются подготовкой требований, взаимодействием по требованиям и бизнес-процессам с конечными пользователями, консультируют конечных пользователей, тестируют разработанную функциональность.

Совещания:

  1. Ежедневный митинг команды.

  2. Ретроспектива в конце спринта.

  3. Ежеквартальные ретроспективы.

Параметры спринта:

  1. Продолжительность: 1 месяц.

  2. Ежедневные обновления продуктивной системы по итогам завершения работ по блокам задач.

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

Во время локдауна подход дал сбой. Удаленная работа наложила свой отпечаток на эффективность работы команды, столкнулись со сложностями, которые необходимо было решить.

Хьюстон, у нас проблемы

Явные проблемы заметили на втором месяце этапа ОПЭ (опытно-промышленной эксплуатации), когда новые требования по дефектам начали закрывать время затраченное на запланированные дефекты и пожелания. Фактически мы стояли на месте. Вероятность успеха сделать все вовремя стремилась к 0.

Для исправления ситуации было решено провести экстренную ретроспективу и собрать все существующие проблемы.

Удалось выявить следующие точки улучшения:

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

  2. Недостаточное качество итогового кода, требуется повысить контроль качества.

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

  4. Разные направления работ выполняют разные группы разработчиков, недостаточно взаимодействия по реализованной функциональности. Необходимо отказаться от разделения.

  5. Много времени уходит на разработку и доработку, консультанты простаивают.

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

А что думает заказчик? Заказчик недоволен динамикой реализации требований. Но готов рассмотреть вариант с четким и прогнозируемым планом.

Именно в этот момент появилась идея использовать подход JIT для улучшения текущей ситуации.

Какие преимущества Agile-Lean мы попробуем использовать в нашем проекте

Основываясь на описании бережливого производства, можно выделить не только сильные, но и слабые стороны для бережливой разработки.

Сильные стороны:

  1. Получение результата в ограниченное время.

  2. Устранение ненужных действий, которые могут снизить стоимость.

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

  4. Гибкость проекта, возможность его корректировки под требования заказчика.

Слабые стороны:

  1. Большие требования к вовлеченности команды в процесс.

  2. Строгая документация, что несколько противоречит принципам Agile, когда продукт важнее документации.

  3. Необходимость детального планирования перед каждым спринтом.

  4. Необходимо выделять сотрудников, которые будут следить за процессом, при этом не будут участвовать в производстве, что ослабит команду.

  5. Продолжительность процесса разработки может вырасти, необходимо быть к этому готовым.

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

Адаптируем 7 принципов Lean

Согласно методологии Lean для разработки программных продуктов, выделяется 7 основных принципов:

  • Исключение потерь. Потерями считается все, что не добавляет ценности для потребителя: излишняя функциональность; ожидание (паузы) в процессе разработки; нечеткие требования; бюрократизация; медленное внутреннее сообщение.

  • Акцент на обучении. Короткие циклы разработки, раннее тестирование, частая обратная связь с заказчиком.

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

  • Предельно быстрая доставка заказчику. Короткие итерации.

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

  • Интегрирование. Передать целостную информацию заказчику. Стремиться к целостной архитектуре. Рефакторинг.

  • Целостное видение. Стандартизация, установление отношений между разработчиками. Разделение разработчиками принципов бережливости. Мыслить широко, делать быстро, ошибаться мало; учиться стремительно.

Проанализировав принципы, сформировали 7 своих, которые подошли к нашей текущей ситуации и должны были помочь закрыть существующие проблемы:

1. Убрать ненужное

Под ненужным будем понимать следующее:

  1. Все, что не приносит пользы конечным пользователям. Сюда относятся непонятные и несрочные требования, редко проявляющиеся дефекты. Мы их откладываем или отказываемся вовсе после согласования с заказчиком.

  2. Ненужный код, дублирование кода.

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

  4. Программные дефекты. Любые дефекты появляются, когда код не проходит достаточную проверку качества.

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

Что мы сделали, чтобы решить задачу:

  1. Детальная проработка требований и согласование описания требований с тимлидом разработки, дополнительная расшифровка требований тимлидом на понятный для разработчиков язык.

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

  3. Повышение взаимодействия разработчиков, консультантов и тимлида. Регулярные встречи 1:1, еженедельные встречи для мини-обучения и небольших вебинаров, еженедельная упрощенная ретроспектива для обсуждения и решения на месте текущих сложностей.

  4. Формирование задач из набора требований в рамках одного реализуемого процесса. Расчет: разработчик загружен на одну задачу не менее 8 часов.

2. Создавать знания и обмениваться ими

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

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

3. Повышение качества кода

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

Для повышения качества были приняты следующие предложения:

  1. Парное программирование. Непосредственное взаимодействие тимлида с разработчиками, совместный анализ требований, проектирование, решение сложных задач.

  2. Степень готовности (Definition of Done, DoD). Задача считается завершенной только в том случае, когда разработчик обсудил реализацию с тимлидом и провел демонстрацию разработанной функциональности консультанту, который закреплен за данной задачей.

  3. Максимальное количество задач в работе (Work In Progress, WIP) каждого разработчика. У разработчика в работе и в тестировании суммарно не может быть больше 3-ех задач. Если разработчик отправил на тестирование все 3 задачи, то он обязан довести эти задачи до публикации в продуктивную систему, для этого активно взаимодействует с консультантами, отвечает на возникающие вопросы, помогает в процессе тестирования.

4. Сокращение спринтов

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

Поэтому решили сделать ряд ограничений на спринт:

  1. Спринт длится 1 рабочую неделю.

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

  3. Все реализованные доработки тестируются на специальной копии продуктивной системы с продуктивными данными (PreProd), и только после успешной проверки публикуются на продуктивную среду (Prod).

  4. Публикация на продуктивный стенд выполняется только один раз в последний день спринта.

  5. После каждого спринта собирается сокращенная ретроспектива на 30 минут для сбора фидбека с команды.

5. Расширение полномочий команды

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

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

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

6. Не торопиться с принятием решений

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

Решение принятое под воздействием эмоций может породить к большому числу проблем.

7. Регулярная оптимизация процесса

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

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

Для реализации данного принципа с тимлида команды разработки были сняты все задачи по разработке и переданы команде, объем задач на спринт был сокращен, т.к. команда разработки фактически ослабла.

Тимлид команды теперь выступает в качестве наставника:

  1. Организует периодическое обучение, разбор сложных ситуаций.

  2. Инициирует передачу опыта между разработчиками.

  3. Помогает консультантам в формировании требований, а разработчикам в реализации этих требований.

  4. Занимается развитием разработчиков и расширением их компетенций.

  5. Занимается подбором и развитием инструментария, повышающего эффективность процесса разработки.

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

Основная проблема бережливого производства отодвигание сроков

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

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

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

Итоги

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

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

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

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

  1. Обеспечить единую общую среду общения и обмена знаниями.

  2. Организовывать совместные видеоконференции, желательно с камерой, чтобы видеть эмоции участников.

  3. Не пренебрегать неформальным общением.

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

По итогам внедрения Lean получили следующие количественные изменения:

  1. Скорость разработки стала прогнозируемой и составила примерно 4 крупные задачи (до 6 часов на задачу в среднем) на сотрудника в неделю, ранее мощность команды в среднем составляла до 2-3 завершенных задач в неделю на сотрудника. Да, задачи крупные и это не совсем по Agile, но это помогло в нашей ситуации.

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

  3. Уменьшилось вдвое количество задач, возвращаемых на доработку.

  4. Еженедельно закрывалось по 3 крупные задачи из техдолга.

  5. Втрое уменьшилось количество дефектов, фиксируемых конечными пользователями.

Данный опыт планируется транслировать на другие проекты компании.

Спасибо за внимание, коллеги! Хотелось бы увидеть в комментариях ваш опыт использования Agile-Lean (или их адаптации) на ваших проектах.

Подробнее..

Категории

Последние комментарии

© 2006-2021, personeltest.ru