Асинхронные серверы

Добрый день, сегодня немного упомяну об асинхронной серверной архитектуре. Это вводная статья, не привязанная к конкретным языкам, серверам и фраемворкам. Далее я буду рассказывать об проектрировании и написании приложений на Python Tornado. А пока, определимся, а что-же такое асинхронный Web сервер?
--
В литературе асинхронные серверы также называются неблокирующими. Но чтобы понять чтоже это такое, вспомним историю:
Изначально были и есть многопоточные серверы, в которых создаётся пул (некое заранее выделенное, созданное, количество) процессов или потоков и каждый процесс или поток отводился на обработку одного соединения.

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

Но какие проблемы в производительности: Во-первых нагрузка на планировщик - много потоков или , теб паче, процессов (хотя это самый маленький и ничтожный недостаток можно про него забыть). Во-вторых, если у нас пул из 100 процессов, то при 100 подключений 101 будет отброшено - это не всегда хорошо, тем более если мы используем технологию long polling. А крупные игроки в сети столкнулись с проблемой 10k. Бот неты подбросили DDOS.

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

Так как сейчас существуют 12-ти ядерные процессоры, то не всегда актуально держать один процесс для работы с основной петлёй, поэтому существуют расширения архитектуры на несколько процессов. Обычно общее количество процессов не должно превышать количество ядер процессора.
Более подробно об асинхроннной архитектуре можно почитать по ссылке:
http://habrahabr.ru/post/150788/

А я тем врменем перейду к основному:
Достоинства и недостатки асинхронной архитектуры по сравнению с многопоточной. Рекомендации к выбору архитектуры.
-- Достоинства:

  • 1) Решение проблемы 10k.
  • 2) Более высокая отзывчивость легковестных приложений.
  • 3) Более эффективное использование процессорного времени.
  • 4) Усложнение реализации DOS и DDOS атак, теперь просто открыть большое количество долгих соединений не достаточно.
  • 5) Возможно гибкое управление нагрузкой, Пока "Большие запросы" выполняются асинхронно и тупят, "Маленькие" свободно выполняются.

-- Недостатки:

  • 1) Если уж система начала тупить, то она тупит для всех одинакого.
  • 2) Высокая нагрузка на разработчика, теперь надо в ручную поддерживать многопоточность для обработки "Больших" запросов и использовать либо асинхронные библиотеки для блокирующих запросов, либо вручную реализовывать асинхронную обработку. Для маленьких и простых проектов данные неудобства малозаметны, но для специфичных задач это может породить массу проблем, к примеру не так легко разобраться с "асинхронными сокетами".
  • 3) Более высокий порог вхождения. Нет права ошибиться в архитектуре web приложения. Нужен опыт в построении высокопроизводительных систем иначе придётся не раз всё переделывать.

--Рекомендации к использованию: (опираясь на данную литературу: ссылка)

  • 1) Для тех кто любит "модно. молодёжно, современно"
  • 2) Актуально при разработки высоконагруженных веб-серверов.
  • 3) При использовании большого количества блокирующих вызовов.
  • 4) Если используете Long polling
  • 5) Если используете Web sockets
  • 6) Если используете Comet
  • 7) Актуально. если необходимо увеличить устойчивость системы при DOS атаке.
  • 8) Если в вашей системе огромное количество маленьких "лёгких" запросов от огромного количества клиентов - то рекомендую асинхронную архитектуру.
vedro-compota's picture

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

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

_____________
матфак вгу и остальная классика =)

humanmashine's picture

Для понимания я советую прочитать тут: http://habrahabr.ru/post/150788/
Я эту ссылку не случайно дал.
Но чтобы было понятнее приведу пример того как обрабатывается запрос.
----- Классическая многопоточная архитектура:
1) У нас пул из 100 процессов. Клиент присылает запрос, установив соединение. Клиент занимает один процесс и у нас остаются 99 свободных, к которым будут обращаться другие клиенты.
2) Во время обработки запроса наш серверный процесс обращается к базе данных. На время обращения он блокируется и засыпает и не обрабатывает других клиентов и не закрывает соединение с клиентом. Поэтому для обработки остальных запросов у нас по прежнему 99 потоков.
3) Когда БД возвращает ответ процесс серверный заканчивает обработку запроса, отправляет данные клиенту и закрывает соединение, вуаля, он снова готов принимать запросы.
Подытожим. Наш сервер блокируется при запросе к БД. По сути сервер многопоточная архитектура - это много экземпляров нашего сервера, чтобы в то время как заблокируется один его работу выполнили другие экземпляры.
------
- Асинхронная архитектура.
1) У нас главная петля - очередь, и один главный процесс.
2) Клиент отправляет запрос. Запрос становится в очередь.
3) Главный процесс смотрит, если этот запрос можно почти моментально выполнить - выполняем и берём следующий из очереди. (Пока мы смотрели могло ещё несколько запросов придти)
4) Если запрос выполнить сразу не получается (скажем опять запрос к БД), то главный процесс отправляет этот запрос на выполнение асинхронно (в другом потоке, или ещё как нибудь - не важно, главное чтобы параллельно и не блокироваться). И приступает к обработке следующего запроса (Тут вся фишка..), а запрос к БД выполняется где-то на фоне.
5) Запрос к БД выполнился и асинхронный обработчик поместил его в конец очереди (петли)
6) Главный процесс добрался до выполненного запроса, передал данные клиенту и закрыл соединение.
Вот отличия грубо. По настоящему всё может быть немного иначе в асинхронной архитектуре - это один из вариантов. Можно по разному её реализовать.