четверг, 31 мая 2012 г.

Высокая нагрузка

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

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

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

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

Однопоточность дает несколько плюсов. Во первых нам не нужно ничего синхронизировать между потоками. Во вторых нам не нужны никакие блокировки. Есть и другие.

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

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

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

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

Первый тестовый модуль, который я выложил на bitbucket, тестирует http.
К серверу nginx удалось одновременно подключить 25000 тестовых http клиентов. Если установить nginx на отдельной машине, наверное можно и больше. http клиенты упираются только в сеть.

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