Асинхронное программирование (АП) — это форма параллельного программирования, которая позволяет структурной единице системы работать отдельно от основного потока приложения. Когда работа завершена, она уведомляет основной поток о том, была ли работа завершена или нет. Такое программирование выгодно, так как оно увеличивает поддерживаемую пропускную способность, что делает ее привлекательной, учитывая растущую потребность интернета в системах с высокой масштабируемостью.
Модели асинхронного программирования
Для того чтобы обработать непрерывность результата неблокирующих операций после их завершения, были созданы различные модели АП. Преимущества их оцениваются с точки зрения того, насколько они позволяют приблизиться к схеме, наиболее близкой к последовательной.
Типы моделей АП:
- Continuations step model — преемственная ступенчатая модель. Это наиболее используемая асинхронность в Node JS. Каждая функция получает информацию о том, как она должна обрабатывать в операции результат успеха или ошибки.
- Event model — модель события, использует управляемую событиями архитектуру, которая позволяет неблокирующим операциям сообщать об их завершении по признакам успеха или неудачи, требует корреляция для синхронизации.
- Promise model — модель обещания, объясняется возвращаемыми значениями неблокирующих операций независимо от момента времени, в который были получены указанные значения успеха или неудачи.
- Generator model — модель генератора. Генераторы используются для временного возврата управления вызывающей программе и последующего возврата к подпрограмме, путем восстановления состояния в точке, в которой ее выполнение было прекращено.
Архитектурные принципы Node
Несмотря на то что в последнее время Node JS получает резкую критику в отношении использования им вычислительных циклов из-за однопоточной среды, его философия, основанная на трех сильных архитектурных принципах, остается востребованной.
JavaScript асинхронен по своей природе, как и Node. Платформа для запуска серверного JavaScript, Node.js была представлена в 2009 году с использованием асинхронной модели ввода-вывода, управляемой событиями, что делает ее эффективной и масштабируемой.
Чат является наиболее типичным примером многопользовательского приложения Node.js в реальном времени. Начиная с IRC для многих проприетарных и открытых протоколов на нестандартных портах, появилась возможность реализовывать все в современных Noje.js с WebSockets, работающими по умолчанию на том же порте 80, который прослушивает новые сообщения, отправленные их клиентами. На стороне клиента есть HTML-страница с несколькими настроенными обработчиками, одна для кнопки «Отправить», выбирающая сообщение и отправляющая его в WebSocket, и другая, которая прослушивает сообщения, поступающие на клиент. Очевидно, что это простая и базовая модель, но основанная на другой дисперсии сложности.
Неактивная модель, которую Node JS использует в API для поддержки асинхронного программирования, является шагом к продолжению. Каждая неблокирующая операция получает функцию в качестве последнего параметра, которая включает логику продолжения. Она будет вызвана после окончания операции, как для обработки результатов в случае успеха, так и для устранения ошибок. Функция продолжения позволяет указать операции блокировки, как она должна продолжаться после завершения операции.
Управление последовательным потоком
Для того чтобы в рамках этой модели продолжить установление последовательных потоков выполнения, необходимо объединить каждую последующую функцию в цепочку, как продолжение предыдущей, где результаты будут обрабатываться в случае успеха или неудачи. Это приводит к диагонализации кода, который был назван пирамидой ада (ад обратного вызова), из-за отсутствия практической управляемости, если только число последовательных цепочек растет минимально.
Распараллеливание — асинхронное выполнение неблокирующих операций происходит немедленно, так как его простой вызов выполняется в фоновом режиме по определению. Чтобы превратить блокирующие операции в неблокирующие, требуется небольшой процесс инкапсуляции, который запускает операцию в фоновом режиме.
Синхронизация функций продолжения
Она требует цепочки в конце каждой параллельной последовательности функции завершения, которая применяет определенную логику только после того, как будет подтверждено, что все параллельные ветви завершены. Для реализации этой проверки используются диаграммы на основе счетчиков.
Пример с помощью продолжателей:
- Параллельность, цикл позволяет выполнять все неблокирующие пары считывания-счета неблокирующим способом.
- Последовательность, каждая пара считывания считывается через шаг функций продолжения.
- Синхронизация, каждая параллельная ветвь получает последнее продолжение, которое выполняет логику завершения, как только будет обеспечено завершение всех ветвей.
Библиотеки продолжателей
Существует множество библиотек, которые могут помочь упростить жизнь разработчикам, работающих с моделью АП. Некоторые из них связаны не только с АП, но и с функциональной парадигмой, которая обусловлена тем фактом, что механизмы внедрения соединений, по сути, являются функциональными преимуществами.
Виды библиотек:
- Async, пожалуй, самая известная и широко используемая библиотека для асинхронного программирования на основе продолжателей. Она предлагает различные методы управления потоком для неблокирующих функций.
- Join — является реализацией метода синхронизации, который можно найти в других языках, таких как C и работающего с потоками. Это может также использоваться в обещаниях, хотя в данном случае его использование менее актуально.
- Fn.js — отличная библиотека, которая реализует различные функциональные методы управления. Его практическая применимость в этом контексте связана с возможностями, которые он предоставляет для генерации неблокирующих функций и применения проверки.
Преимущества и недостатки
Асинхронное программирование на основе продолжателей хороший вариант для ситуаций с простой логикой управления потоком. Обычно это относится к программам в Node JS, которые позволяют определять неблокирующий ответ на входящие запросы.
Преимущества модели:
- Простые схемы запроса и ответа.
- Согласованность со схемами функционального программирования.
- Легко понять как концептуальный механизм.
Недостатки:
- Когда логика управления разработана недостаточно, процесс усложняется, что приводит к созданию кода с распределенной функциональной логикой, который трудно читать, понимать и поддерживать.
- Сложность в определении логики управления потоками.
- Сложно настраиваемые механизмы синхронизации.
- Логика управления распределяется между каждой неблокирующей ветвью.
Модель, управляемая событиями
Событие является сигналом в бизнес-экосистеме. Анатомически они обычно состоят из типа, временной метки и набора данных, характеризующих контекст, в котором произошло событие. Архитектура событий (EDA) обеспечивает механизм связи между клиентами и поставщиками в отношениях 1: N и с номинальной развязкой. Одно из многих его применений — устранение проблем асинхронной обработки данных.
В централизованных архитектурах, управляемых событиями, существует центральная коммуникационная шина-посредник, которая отвечает за обеспечение эффективности процесса регистрации прослушивающих клиентов и запуска уведомлений по требованию поставщиков к ним. Этот механизм допускает мощность N:N, а схема называется шаблоном PUB/SUB.
В распределенных управляемых событиями архитектурах каждый провайдер отвечает за управление подпиской своих клиентов и за отправку уведомлений, при возникновении события. Механизм связи также номинально отделен, но обычно количество провайдеров и клиентов составляет 1:N. Эта схема соответствует наблюдаемой схеме или источникам событий в Node JS jargon.
Вычислительная абстракция: Promise
Обещание — это вычислительная абстракция, которая представляет собой обязательство со стороны неблокирующей операции, вызванной для доставки ответа вызывающей программе, когда результат получен после завершения. Обещание — это объект, который предоставляет два метода для включения логики обработки в случае успеха или неудачи.
Они отвечают простому жизненному циклу, который необходимо знать, чтобы с ними можно было работать. Существенная ценность обещания заключается в двух принципах. Во-первых, логика процесса в случае успеха или неудачи применяется только один раз. И, во-вторых, гарантируется выполнение логики успеха или неудачи, даже если обещание разрешено до того, как будут введены его драйверы. Если потребуется, обещание ждет своих обработчиков, асинхронного программирования JavaScript.
Есть несколько способов получить обещания, которые можно идентифицировать как шаблоны построения, которые появляются периодически при использовании этой модели. Определение ES6 включает в себя обещания и Node JS версии 0.12 имеет поддержку этой спецификации. Кроме того, есть несколько библиотек, которые реализуют модель обещаний. Для того чтобы иметь сравнительную систему отсчета, был определен стандарт Promises A +, который управляет всеми реализациями с доступными на тот момент объектами.
Синхронное и асинхронное программирование
Рекомендуется использовать АП JavaScript, загрузку для всех его тегов и кода поставщика из него. Это обеспечивает наилучшую совместимость с наибольшим количеством поставщиков. Такое размещение предоставляет всем тегам поставщиков наибольшую возможность завершить отслеживание, прежде чем посетитель перейдет на следующую страницу.
Синхронная загрузка происходит, когда браузер должен остановить рендеринг страницы, чтобы завершить выполнение кода JavaScript. Если он обнаруживает синхронный тег JS, то блокирует отображение страницы до завершения выполнения кода. Это аналогично тихоходному грузовику на дороге с одной полосой движения, который замедляет движение за ним. Современные веб-сайты отошли от этого метода, потому что он представляет прямой риск задержки времени загрузки страницы.
Недостатком этого метода является то, что весь сайт блокируется от начала до полной загрузки тега. И хотя поставщики тегов заключают соглашения об уровне обслуживания в течение срока их доставки, на производительность могут влиять несколько факторов. К ним относятся медленное время отклика, связанное с поставщиками, ведение ненужных серверов приложений в гибридную модель клиент-сервер и медленный интернет-трафик. Если пользователь загружает теги синхронно, рекомендуется убедиться, что у поставщика время отклика составляет 100 миллисекунд (мс) или быстрее.
Программирование синхронной и асинхронной обработки данных через API — это интерфейсы прикладного программирования, которые возвращают данные для запросов либо сразу, либо, соответственно, позже. Синхронные и асинхронные API предоставляют способ делать немедленные или запланированные запросы ресурсов, данных или услуг, когда они доступны. Современный метод, принятый для сайтов, заключается в асинхронной загрузке тегов.
В этом методе код JavaScript обрабатывается параллельно с остальным содержимым страницы. Это означает, что даже если тег поставщика медленно реагирует или загружается, он не будет замедлять работу остальной части страницы. Используя этот подход, можно не только разделять теги JavaScript, загружаемые независимо друг от друга, асинхронный метод минимизирует влияние загрузки внешних файлов JS на процесс рендеринга страницы.
Понимание и профилирование C #
Microsoft и сообщество .NET очень упростили АП благодаря реализации асинхронного ожидания в C #. Последние версии ASP.NET активно используют его для повышения производительности. Многие инструменты для мониторинга производительности и профилирования пытаются поддерживать и визуализировать производительность асинхронного программирования 1С. Продукты Stackify Prefix & Retrace имеют отличную поддержку приложений, использующих C # async await. Для начала нужно понять, как на самом деле работает код, использующий async awai» и HttpClient в качестве примера.
Используя ILSpy, можно увидеть, как компилятор преобразует этот код в AsyncState Machine. Конечный автомат выполняет весь сложный код под прикрытием, что позволяет разработчикам, писать асинхронный код.
Профилирование асинхронного программирования в C 5 0 сложным, потому что он пересекает потоки. Традиционно, метод и все вызовы его дочерних методов происходят в одном потоке. Это облегчает понимание отношений между родительскими и дочерними методами. С асинхронным кодом это совсем другая история. Родительский метод запускается в одном потоке. Когда начинается операция ввода-вывода, код в этом потоке заканчивается. Когда операция ввода-вывода завершается, код продолжает выполняться в новом потоке. Связать код между этими потоками, как часть более крупной транзакции, довольно сложно.
AIOHTTP: сервер-клиент для asyncio
Aiohttp — позволяет пользователям создавать асинхронные серверы и клиенты. Пакет асинхронного программирования aiohttp работает для клиентских и серверных веб-сокетов. Документация из этого примера aiohttp используется для захвата HTML-страницы.
В этом примере показано, как загрузить один или несколько файлов, и также можно скачивать файлы через программу. В нем указано несколько новых элементов, таких как asynctimeout. Это позволяет создать менеджер контекста времени ожидания. Внизу код создает асинхронный цикл синхронизации и использует его в качестве основной функции.
Создают объект Client Session в главной функции асинхронного программирования и coroutine, и функцию сопрограммы, которая собирает URL всего, что нужно загрузить. В download coroutine он создает менеджер контекста, который работает около X секунд. По истечении этого количества секунд X менеджер контекста заканчивается. Далее используют функцию get () сеанса, которая находит объект ответа.
Когда разработчик создает атрибут содержимого объекта ответа, он возвращает aiohttp. StreamReader, который позволяет пользователю загружать файл в любом размере. Как только будет прочитан файл, он будет записан на локальный диск. После чего используют функцию response (), чтобы завершить обработку ответа. Согласно документации он неявно вызывает release (). Тем не менее, асинхронное программирование Python явно лучше. Лучше оставить эту функцию, чтобы предотвратить дальнейшие проблемы. Здесь есть один раздел, который блокирует раздел кода, записываемый на диск, при этом код остается заблокированным. Использование aiohttp — реальный способ улучшить рабочий процесс, где пользователям не нужно тратить время на создание сервера, загрузку ссылок и написание асинхронных файлов, что сокращает время создания проекта.
Асинхронное программирование позволяет достичь большей эффективности в программном обеспечении, поскольку не блокируется поток выполнения для длительных процессов или взаимодействия с пользователем, как для разработки приложений для Node, так и для браузеров.
Иван Фролов
24 февраля, 2019