Нашли ошибку или опечатку? Выделите текст и нажмите

Поменять цветовую

гамму сайта?

Поменять
Обновления сайта
и новые разделы

Рекомендовать в Google +1

Увеличение производительности веб-сервера

57

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

Кеширование часто используемых объектов

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

  1. Если данные уже присутствуют в кеше, используются эти данные.

  2. Иначе извлечь данные, сохранить в кеше и использовать их.

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

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

// Сохранить значение в коллекции
Application["listOfCountries"] = countries;

// Получить это значение
countries = (IEnumerable<string>)Application["listOfCountries"];

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

  • Определять предельное время хранения объектов в кеше, указывая либо значение типа TimeSpan (продолжительность), либо значение типа DateTime (конкретные дата и время). По истечении времени хранения объекты будут удаляться из кеша автоматически.

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

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

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

Добавление элементов в кеш выполняется так же, как добавление элементов в словарь:

Cache["listOfCountries"] = countries;

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

Cache.Insert("listOfCountries", countries, null,
     System.Web.Caching.Cache.NoAbsoluteExpiration, 
     TimeSpan.FromMinutes(60));

Парадигма доступа к кешу с использованием класса Cache реализуется следующим образом:

object retrievedObject = null;

retrievedObject = Cache["theKey"];
if (retrievedObject == null)
{
    // Отыскать данные (в базе данных, веб-службе и т.д.)
    object originalData = null;

    // ...

    // Сохранить вновь извлеченные данные в кеше
    Cache["theKey"] = originalData;
    retrievedObject = originalData;
}

// использовать извлеченный объект
// (либо из кеша,либо только что добавленный в кеш)
// ...

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

Использование асинхронных страниц, модулей и контроллеров

Когда среда выполнения ASP.NET получает запрос от IIS, она передает его пулу потоков выполнения, после чего одному из рабочих потоков поручается выполнить задачу по обработке запроса, какой бы она ни была - простой HTTP-обработчик запроса, страница в приложении ASP.NET WebForm или контроллер в приложении ASP.NET MVC.

Поскольку количество рабочих потоков ограничено (определяется значением maxWorkerThreads в разделе processModel в файле Web.config), среда ASP.NET также имеет ограниченное количество потоков для одновременной обработки запросов.

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

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

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

Ограничения на объем пула потоков выполнения и очереди запросов в приложениях ASP.NET определяются в разделе processModel, в файле web.config и отчасти управляются атрибутом processModelAutoConfig.

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

В веб-приложениях, где обработка многих запросов связана с получением данных от веб-служб или из баз данных, часто можно наблюдать низкое использование процессора, даже при обслуживании большого количества пользователей. Выяснить использование процессора в своем веб-приложении можно с помощью счетчиков производительности, например, с помощью комбинации счетчиков Processor\% CPU Utilization, ASP.NET Applications\Requests/Sec (Приложения ASP.NET\ Запросов/сек) и ASP.NET\Requests Queued (ASP.NET\ Запросов в очереди).

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

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

Создание асинхронной страницы

Подробное описание создания асинхронных страниц находится в статье "Асинхронные веб-формы".

Создание асинхронного контроллера

Контроллеры в ASP.NET MVC также могут создаваться как асинхронные, если выполняют продолжительные операции ввода/вывод. Чтобы создать асинхронный контроллер требуется выполнить следующие шаги:

  1. Создать класс контроллера, наследующий тип AsyncController.

  2. Реализовать методы ...Async и ...Completed для каждой синхронной операции, где ... - это имя операции.

  3. В методе ...Async вызвать метод AsyncManager.OutstandingOperations.Increment(), передав ему количество выполняемых асинхронных операций.

  4. В коде, выполняемом по завершении каждой асинхронной операции, вызвать метод AsyncManager.OutstandingOperations.Decrement(), чтобы сообщить об окончании операции.

Например, ниже представлен контроллер, выполняющий асинхронную операцию Index, которая вызывает службу, возвращающую данные для отображения в представлении:

// Абстрактный код - не компилируется 
// (создан в целях демонстрации)
public class MyController : AsyncController
{
    public void IndexAsync()
    {
        // Известить AsyncManager, что выполняется единственная операция
        AsyncManager.OutstandingOperations.Increment();
        MyService serviceProxy = new MyService();

        // Зарегистрировать событие завершения
        serviceProxy.GetDataCompleted += (sender, e) =>
        {
            AsyncManager.Parameters["result"] = e.Value;
            AsyncManager.OutstandingOperations.Decrement();
        };

        serviceProxy.GetHeadlinesAsync();
    }
    public ActionResult IndexCompleted(MyData result)
    {
        return View("Index", new MyViewModel { TheData = result });
    }
}

Примеры использования асинхронного кода без применения асинхронных контроллеров в ASP.NET MVC, вы можете найти в статье "ASP.NET MVC 5 -Улучшение производительности с помощью контроллеров".

Пройди тесты