Чтобы выбрать один из двух подходов, нужно определить профиль нагрузки на сервер.
Предположим, что каждый запрос требует c миллиекунд CPU и w миллисекунд реального времени для обработки. Время CPU расходуется на активные вычисления, а реальное время включает все запросы к внешним ресурсам. Например, запрос требует 5 мс времени c от CPU и 95 мс на ожидание ответа от базы данных. В итоге получается 100мс. Давайте также предположим, что потоковая версия сервера может поддерживать до t потоков, прежде чем начнутся проблемы с планированием и переключением контекста.
Если каждый запрос требует только время CPU для обработки, сервер в состоянии ответить на самое большее 1000/c запросов в секунду. Например, если каждый запрос занимает 2 миллисекунды времени CPU, тогда получится 1000/2=500 запросов в секунду.
В общем случае многопоточный сервер в состоянии обработать t*1000/w запросов в секунду.
Пропускная способность потокового сервера состовляет минимум от этих выражений (1000/c и t*1000/w). Событийний сервер ограничен лишь производительностью CPU (1000/c), поскольку использует всего один поток. Все описанное выше можно выразить следующим образом:
def max_request_rate(t, c, w):
cpu_bound = 1000. / c
thread_bound = t * 1000. / w
print 'threaded: %d\nevented: %d' % (min(cpu_bound, thread_bound), cpu_bound)
Теперь рассмотрим различные типы серверов и посмотрим, как они себя покажут в различной реализации.
Для примеров я буду использовать t = 100.
Начнем с классического примера: HTTP прокси сервер. Этот тип серверов почти не требует времени CPU, поэтому предположим, что c = 0.1 мс. Предположим, что стоящие следом сервера получают задержку, скажем w = 50 мс. Тогда мы получим:
>>> max_request_rate(100, 0.1, 50)
threaded: 2000
evented: 10000
Наши расчеты показывают, что потоковый сервер будет в состоянии обработать 2 000 запросов в секунду, а событийный 10 000. Большая производительность событийного сервера говорит нам, что для потокового сервера узким местом стало количество потоков.
Другой пример — сервер веб-приложений. Сперва рассмотрим случай приложения, которое не требует никаких внешних ресурсов, но производит определенные вычисления. Время обработки будет, допустим 5 мс. Т.к. никакие блокирующие вызовы не совершаются, время w составит также 5 мс. Тогда:
>>> max_request_rate(100, 5, 5)
threaded: 200
evented: 200
В данном случае узким местом стала производительность CPU.
А теперь представим, что приложению нужно запросить данные из внешнего ресурса и время CPU составит 0.5 мс, а общее время w = 100 мс.
>>> max_request_rate(100, 0.5, 100)
threaded: 1000
evented: 2000
Как видно из простых расчетов, да и вообще из математики, синхронная потоковая реализация не будет иметь большую производительность, чем асинхронная событийная.
Однако стоит учитывать, что конечного пользователя интересует в первую очередь реальное время, которое он тратит на ожидание ответа. Проблемы начинаются, когда приложению приходится делать что-то долгое. Например, совершить большой и сложный запрос в базу данных, провести сложный расчет, обработать графику, произвести запрос на чужой сервер. Примеров можно привести много.
Как только однопоточной программе, обслуживающей в цикле десять тысяч клиентов, приходится ради одного из них задержаться, например, на секунду, эту секунду ждут все остальные. Если таких запросов много — ни один клиент не получит ответ раньше, чем общее время обработки запросов. Кооперативная многозадачность в действии. В случае потоковых решений, действует вытесняющая многозадачность. Система не позволит «тяжелому» запросу тратить время всех.
С одной стороны мы имеем прозводительные асинхронные решения, узким местом которых является сложность написания программ, поскольку большинство существующих библиотек написаны в блокирующем стиле. Программист берет на себя отвестственность за то, что ошибка в одном запросе не скажется на остальных.
С другой стороны мы имеем блокирующие решения, для которых мы привыкли писать программы. Проблему количества поддерживаемых потоков можно решить с помощью специализированных средств, например гринлетов или Erlang-процессов. Если потоковые сервера достингут планки, при которой узким местом перестанет быть количество потоков, они будут выглядеть более привлекательно за счет времени отклика и надежности.
Источник: Хабрахабр - Веб-разработка
Оригинальная страница: Потоки или события
company website view it visit this site right here bags replica ysl visit here visit this site right here
ОтветитьУдалить