Полезные советы по оптимизации ASP-приложений. Кэшируйте часто используемые данные в объектах Application или Session

Sponsored By

A few years back Phil Haack wrote a great article on the dangers of recurring background tasks in ASP.NET . In it he points out a few gotchas that are SO common when folks try to do work in the background. Read it, but here"s a summary from his post.

  • An unhandled exception in a thread not associated with a request will take down the process.
  • If you run your site in a Web Farm, you could end up with multiple instances of your app that all attempt to run the same task at the same time.
  • The AppDomain your site runs in can go down for a number of reasons and take down your background task with it.

If you think you can just write a background task yourself, it"s likely you"ll get it wrong. I"m not impugning your skills, I"m just saying it"s subtle . Plus, why should you have to?

There"s LOT of great ways for you to do things in the background and a lot of libraries and choices available.

Some ASP.NET apps will be hosted in IIS in your data center and others will be hosted in the Azure cloud. The spectrum of usage is roughly this, in my opinion:

  • General: Hangfire (or similar similar open source libraries)
    • used for writing background tasks in your ASP.NET website
  • Cloud:
    • A formal Azure feature used for offloading running of background tasks outside of your Website and scale the workload
  • Advanced: Azure Worker Role in a Cloud Service
    • scale the background processing workload independently of your Website and you need control over the machine

There"s lots of great articles and videos on how to use , and lots of documentation on how Worker Roles in scalable Azure Cloud Services work, but not a lot about how your hosted ASP.NET application and easily have a background service. Here"s a few.

WebBackgrounder

As it says "WebBackgrounder is a proof-of-concept of a web-farm friendly background task manager meant to just work with a vanilla ASP.NET web application." Its code hasn"t been touched in years, BUT the WebBackgrounder NuGet package has been downloaded almost a half-million times.

The goal of this project is to handle one task only, manage a recurring task on an interval in the background for a web app.

If your ASP.NET application just needs one background task to runs an a basic scheduled interval, than perhaps you just need the basics of WebBackgrounder.

Using System;
using System.Threading;
using System.Threading.Tasks;

namespace WebBackgrounder.DemoWeb
{
public class SampleJob: Job
{
public SampleJob(TimeSpan interval, TimeSpan timeout)
: base("Sample Job", interval, timeout)
{
}

Public override Task Execute()
{
return new Task(() => Thread.Sleep(3000));
}
}
}

Built in: QueueBackgroundWorkItem - Added in .NET 4.5.2

Somewhat in response to the need for WebBackgrounder, .NET 4.5.2 added QueueBackgroundWorkItem as a new API . It"s not just a "Task.Run," it tries to be more:

QBWI schedules a task which can run in the background, independent of any request. This differs from a normal ThreadPool work item in that ASP.NET automatically keeps track of how many work items registered through this API are currently running, and the ASP.NET runtime will try to delay AppDomain shutdown until these work items have finished executing.

It can try to delay an AppDomain for as long as 90 seconds in order to allow your task to complete. If you can"t finish in 90 seconds, then you"ll need a different (and more robust, meaning, out of process) technique.

The API is pretty straightforward, taking Func. Here"s an example that kicks of a background work item from an MVC action:

Public ActionResult SendEmail( User user)
{
if (ModelState.IsValid)
{
HostingEnvironment.QueueBackgroundWorkItem(ct => SendMailAsync(user.Email));
return RedirectToAction("Index", "Home");
}

Return View(user);
}

FluentScheduler

FluentScheduler is a more sophisticated and complex scheduler that features a (you guessed it) fluent interface. You have really explicit control over when your tasks run.

Using FluentScheduler;

public class MyRegistry: Registry
{
public MyRegistry()
{
// Schedule an ITask to run at an interval
Schedule().ToRunNow().AndEvery(2).Seconds();

// Schedule a simple task to run at a specific time
Schedule(() => Console.WriteLine("Timed Task - Will run every day at 9:15pm: " + DateTime.Now)).ToRunEvery(1).Days().At(21, 15);

// Schedule a more complex action to run immediately and on an monthly interval
Schedule(() =>
{
Console.WriteLine("Complex Action Task Starts: " + DateTime.Now);
Thread.Sleep(1000);
Console.WriteLine("Complex Action Task Ends: " + DateTime.Now);
}).ToRunNow().AndEvery(1).Months().OnTheFirst(DayOfWeek.Monday).At(3, 0);
}
}

FluentScheduler also embraces IoC and can easily plug into your favorite Dependency Injection tool of choice by just implementing their ITaskFactory interface.

Quartz.NET

Quartz.NET is a .NET port of the popular Java job scheduling framework of the (almost) same name. It"s very actively developed. Quartz has an IJob interface with just one method, Execute, to implement.

Using Quartz;
using Quartz.Impl;
using System;

namespace ScheduledTaskExample.ScheduledTasks
{
public class JobScheduler
{
public static void Start()
{
IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler();
scheduler.Start();

IJobDetail job = JobBuilder.Create().Build();

ITrigger trigger = TriggerBuilder.Create()
.WithDailyTimeIntervalSchedule
(s =>
s.WithIntervalInHours(24)
.OnEveryDay()
.StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(0, 0))
.Build();

Scheduler.ScheduleJob(job, trigger);
}
}
}

Then, inside your Application_Start, you call JobScheduler.Start(). There"s a great getting started article on Quartz at Mikesdotnetting you should check out.

Hangfire

And last but definitely not least, the most polished (IMHO) of the group,

Об опасностях выполнения фоновых задач в ASP.NET. Он выделил три основных риска, связанных с запуском фонового процесса:

  1. Необработанное исключение в потоке, несвязанном с запросом, может снять процесс.
  2. Если запустить сайт в веб-ферме, то есть вероятность случайно завершить несколько экземпляров приложения, которое могло запустить несколько экземпляров одной и той же задачи одновременно.
  3. Домен приложения, в котором запущен сайт, по разным причинам может выгрузиться и снять фоновую задачу, запущенную в нем.
Конечно, можно написать собственный менеджер для управления фоновыми задачами. Но, вероятнее всего, Вы сделаете это неправильно. Никто не собирается оспаривать Ваши навыки разработчика. Просто создание подобного менеджера - это достаточно тонкая вещь. Да и зачем Вам это надо?

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

Какие-то ASP.NET приложения могут работать на Ваших собственных серверах под IIS, какие-то размещаться в Azure.

Для запуска фоновых задач можно выделить несколько вариантов:

HANGFIRE

И последний в обзоре, но, безусловно, непоследний по функциональности, наиболее продвинутый из всех Hangfire. Это действительно очень продвинутый фреймворк для фоновых задач в ASP.NET. Опционально он может использовать Redis, SQL Server, SQL Azure, MSMQ или RabbitMQ для повышения надежности выполнения задач.

Документация Hangfire действительно превосходна. Хотелось бы, чтобы каждый проект с открытым исходным кодом имел такую документацию.

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

Hangfire позволяет легко определить задачи типа «запустить и забыть», информация о которых будет храниться в базе данных:

BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget"));
Можно отсрочить выполнение задачи:

BackgroundJob.Schedule(() => Console.WriteLine("Delayed"), TimeSpan.FromDays(1));
Или запустить задачу в CRON стиле

RecurringJob.AddOrUpdate(() => Console.Write("Recurring"), Cron.Daily);
Работать с Hangfire очень удобно. Hangfire имеет хорошую документацию и обучающие руководства , основанные на реальных примерах.

Hangfire - это целая экосистема для работы с фоновыми задачами в ASP.NET приложениях.

Библиотеки доступны в виде открытых исходных кодов или Nuget пакетов.

Итоги (лично от себя)

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

Я уже знаю, что мне нужно запускать больше одного процесса, и работать процессы могут долго (ограничение в 90 секунд на завершение в QueueBackgroundWorkItem). FluentScheduler выглядит неплохо, но хотелось большего. Hangfire – отличное решение, но, вроде, сразу требует использования базы данных для хранения очереди задач. Да и не совсем там все бесплатно – есть и платная версия.

В итоге я пока выбрал Quartz.NET: вполне приличная документация, достаточное количество примеров, чтобы быстро начать использовать, а также расширяемый функционал, который можно добавлять по мере возрастания потребностей.

Если вы знаете другие библиотеки для запуска фоновых задач или имеете опыт решения подобных задач – делитесь в комментариях.

Аккомпанировать

АККОМПАНИРОВАТЬ accompagner ,> нем. akkompanieren . 1 . дипл. Приложить к чему-л., спроводить чем-л. (какую-л . посылку, отправление) . Я тот экстракт письмом своим не аккомпанировал. Кантемир Реляц. 2 172.

2. расш. Сопровождать кого-, что-л., быть дополнением к чему-л. Спинат, растение самое здоровое и для всякого желудка годное.. аккомпанирует все нарядные блюда. 1809. Гримо Прихотник 60. Однажды меня с Сазоновым (он мне всегда аккомпанирует в Ватикане) заперли. Гальберг Письма 57. Одни злословят по привычке, другие аккомпанируют. Ростопчин Ох, французы 114. Дежурный по полку ротмистр доказал, что ему хотелось есть и что аппетит у него богатырский, да и мы все аккомпанировали ему недурно, нечего сказать, не отказываясь от лафита и д"икема, которым, гостеприимный хозяин наполнял наши стаканы. В. П. Бурнашев Восп. // РА 1872 1839.

3. 1690. Лексис. муз. Сопровождать аккомпанементом. Акомпанировать своему голосу . Кн. муз. 52. Играть соло или аккомпанировать в сих концертах. Финдейз. 171. <Отец> играл на Виолончеле, и всегда акомпанировал наши Дуо. ММ 3 14. Он же Куликов фортепианами и арфою аккомпанирует. <Шарманщик> аккомпанирует вальс Ланнера свистками и трелями. Григорович Петерб. шарманщик. Он аккомпанировал Вьетану и многим певцам. Римский-Корс. Летопись. // РР 1970 1 60. || Припевать, подпевать . Но не было ли, спрашивает он, в вашем пении монотонии? Было, отвечает целомудренная супруга; но в финале все хорошо акомпанировали. Зритель 1 121. Хватайко поет и все акомпанируют: бери, большой тут нет науки. Капнист Ябеда 85.

4. перен. Сопровождая что-л., создавать определенный фон. БАС-2. Шепот продолжался, и ему аккомпанировал смущенно-счастливый смех. Мам.- Сиб. Черты из жизни Пепко. Собаки, смирно лежавшие у ворот, не выдерживали <визга поросят> и принялись аккомпанировать громким лаем и воем. А. Осипович Мечтатели. // ОЗ 1881 8 1 462. Лишь папа лесничий Дрожжинин шумно ел суточные щи, да мама для этикета аккомпанировала, едва разжимая строгие губы. Аксенов Затоварен. бочкотара. // РР 1970 3 60. - Лекс. Ян.1803: акомпанировать; Соколов 1834: акомпанирова/ть; Даль: акомпанирова/ть; САН 1933: аккомпани/ровать; Сл. 18: аккомпанировать 1734 (ако- 1792).


Исторический словарь галлицизмов русского языка. - М.: Словарное издательство ЭТС http://www.ets.ru/pg/r/dict/gall_dict.htm . Николай Иванович Епишкин [email protected] . 2010 .

Смотреть что такое "аккомпанировать" в других словарях:

    АККОМПАНИРОВАТЬ - (фр. accompagner). Сопровождать пение игрою на каком либо музыкальном инструменте. Словарь иностранных слов, вошедших в состав русского языка. Чудинов А.Н., 1910. АККОМПАНИРОВАТЬ франц. accompagner. Сопровождать пение игрою на каком либо… … Словарь иностранных слов русского языка

    аккомпанировать - См … Словарь синонимов

    аккомпанировать - и устарелое аккомпанировать … Словарь трудностей произношения и ударения в современном русском языке

    АККОМПАНИРОВАТЬ - АККОМПАНИРОВАТЬ, рую, руешь; несовер., кому. Исполнять аккомпанемент. А. на рояле. Толковый словарь Ожегова. С.И. Ожегов, Н.Ю. Шведова. 1949 1992 … Толковый словарь Ожегова

    АККОМПАНИРОВАТЬ - кого или что (сонату) чем, на чем, муз., франц. вторить, сопровождать, подголашивать, подголосить, подыгрывать; держать другой, согласный голос. Аккомпанирование ср., ·длит. аккомпанировка жен., ·об. действие по гл., вторенье, подголоска,… … Толковый словарь Даля

    аккомпанировать - (иноск. шут.) вторить, делать что в подражание другому, совместно с ним (намек на сопровождение пения или одного инструмента игрою одного или многих инструментов) Ср. Я издавна солист и аккомпанемента не ожидаю, один пью... Лесков. На ножах. 1,… … Большой толково-фразеологический словарь Михельсона

    Аккомпанировать - несов. неперех. 1. Сопровождать игрой на музыкальном инструменте или на музыкальных инструментах сольную вокальную или инструментальную партию, основную тему или мелодию музыкального произведения. 2. перен. Создавать определённый фон, сопровождая … Современный толковый словарь русского языка Ефремовой

    аккомпанировать - аккомпанировать, аккомпанирую, аккомпанируем, аккомпанируешь, аккомпанируете, аккомпанирует, аккомпанируют, аккомпанируя, аккомпанировал, аккомпанировала, аккомпанировало, аккомпанировали, аккомпанируй, аккомпанируйте, аккомпанирующий,… … Формы слов

    аккомпанировать - аккомпан ировать, рую, рует … Русский орфографический словарь

    аккомпанировать - (I), аккомпани/рую, руешь, руют … Орфографический словарь русского языка

Книги

  • Учись аккомпанировать на 6-струнной гитаре. От романсов к року и джазу , Манилов В.. В книге вы найдете необходимые сведения об аккордах, их последовательностях и способах усложнения партии ритм-гитары, о стандартной аппликатуре и типовых ритмических моделях различных жанров…

Последнее обновление: 10.03.2016

Одним из ключевых нововведений последних версий фреймворка.NET стала асинхронность. Хотя фреймворк и раньше позволял использовать асинхронные методы, но с появлением библиотеки Task Parallel Library работа с асинхронным кодом была предельно упрощена, а сам формат работы изменился. Были добавлены новые возможности по созданию асинхронных методов с использованием новых ключевых слов, таких как async и await.

При создании нового контроллера мы в настройках уже можем указать, как нам нужен контроллер - синхронный или асинхронный. По умолчанию Visual Studio добавляет в проект стандартные контроллеры, методы которых, как правило, возвращают объект ActionResult. Но если мы при добавлении контроллера в папку Controllers выберем тип MVC 5 Controller with views, using Entity Framework , то в окне настройки нового контроллера специальное поле позволит нам указать, что новый контроллер будет содержать асинхронные методы:

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

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

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

И если обработка запроса блокируется очень долго, то IIS начинает задействовать для обслуживания других входящих запросов новые потоки. Однако есть ограничения на общее количество потоков. Когда количество потоков достигает предела, то вновь входящие запросы помещаются в очередь ожидания. Однако и тут есть ограничение на количество запросов в очереди. И когда это количество превышает предел, то IIS просто отклоняет все остальные запросы с помощью статусного кода 503 (Service Unavailable).

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

Перейдем непосредственно к коду. Для создания асинхронных методов используются модификаторы async и await , которые позволяют выполнять продолжительные операции без блокирования основного потока.

Сравним на примере вызов синхронного и асинхронного метода:

Using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using AsyncContollers.Models; using System.Threading.Tasks; using System.Data.Entity; namespace AsyncContollers.Controllers { public class HomeController: Controller { BookContext db = new BookContext(); public ActionResult Index() { IEnumerable books = db.Books; ViewBag.Books = books; return View(); } // асинхронный метод public async Task BookList() { IEnumerable books = await db.Books.ToListAsync(); ViewBag.Books = books; return View("Index"); } } }

Оба метода выполняют одну и ту же операцию - извлечение данных из БД и получают идентичные результаты. Но если первый синхронный метод Index представляет привычную для нас запись, то асинхронный метод BookList уже выглядит необычно.

Этот метод возвращает не объект ActionResult, а объект Task . Task представляет асинхронную операцию, выполняющуюся продолжительное время.

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

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

Но также следует учитывать, что await используется с методами, возвращающими объект Task . Поэтому для получения данных из БД используется метод await db.Books.ToListAsync() , который также извлекает данные из БД, но уже в асинхронном режиме.

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

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

Публикуемый ниже материал представляет собой первый совет из целой серии советов по оптимизации приложений, использующих технологии ASP и Visual Basic Scripting Edition (VBScript). Большинство из них были многократно обсуждены и c успехом проверены на веб-сайте Microsoft Corporation и других ASP-сайтах. Авторы материала подразумевают, что вы уже знакомы с основами разработки ASP-приложений, включая VBScript и/или JScript, ASP-сессиями и др. важными объектами (Request, Response и Server).

Кэшируйте часто используемые данные на сервере

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

Кэширование данных — это классический компромисс «место или время». Если вы избрали для кэширования правильный материал, вы можете видеть внушительное повышение производительности вашего приложения. Чтобы кэширование было эффективным нужно использовать для временного хранения только те данные, которые многократно используются для вывода и каждый раз трудоемки для повторных вычислений. Кэш, полный устаревшей информации, будет пустой тратой памяти сервера.

Данные, которые не изменяются часто, являются хорошим кандидатом для кэширования, потому что вам не надо будет волноваться относительно их синхронизации через какое-то время с конечной базой данных. Выпадающие списки (сombo-box), таблицы ссылок, пункты меню, и переменные конфигурации сайта (включая имена DSN, адреса IP и URL) — первые кандидаты для хранения в кэше. Заметьте, что вы можете кэшировать представление данных много быстрее, нежели данные сами себя. Если ASP-страница изменяется не так часто и ее временный кэш будет весьма внушительным (например, полный каталог изделий фирмы), попробуйте использовать сгенерированные HTML-страницы, чем каждый раз загружать сервер генерацией ASP-страниц.

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

Объекты Application и Session служат для хранения данных в памяти, значения которых могут быть доступны между несколькими HTTP-запросами (в отличие от обычных переменных, чьи значения доступны только в теле одной ASP-страницы). Данные объекта Session доступны только одному пользователю (в течении его сессии), в то время как данные Application доступны всем пользователям веб-сайта. Поэтому часто перед разработчиком возникает вопрос: в каком из объектов сохранять часто используемые данные. Обычно, для инициализации переменных этих объектов используются процедуры файла Global.asa — Application_OnStart() или Session_OnStart() соответственно. Если в вашем Global.asa еще нет этих процедур, то вы можете добавить их сами или инициализировать переменные, когда это будет необходимо. Примером может быть следующая процедура, использующая Application для хранения значений многократно использующейся переменной EmploymentStatusList. Процедура проверяет существование данных в EmploymentStatusList и при необходимости расчитывает их заново:

<% Function GetEmploymentStatusList Dim d d = Application("EmploymentStatusList") If d = "" Then " Если значения нет - выполним расчет d = FetchEmploymentStatusList() Application("EmploymentStatusList") = d End If GetEmploymentStatusList = d End Function %>

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

Function FetchEmploymentStatusList Dim rs Set rs = CreateObject("ADODB.Recordset") rs.Open "select StatusName, StatusID from EmployeeStatus", _ "dsn=employees;uid=sa;pwd=;" "Получить все строки FetchEmploymentStatusList = rs.GetRows() rs.Close Set rs = Nothing End Function

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

Function FetchEmploymentStatusList Dim rs, fldName, s Set rs = CreateObject("ADODB.Recordset") rs.Open "select StatusName, StatusID from EmployeeStatus", _ "dsn=employees;uid=sa;pwd=;" s = "" & vbCrLf rs.Close Set rs = Nothing FetchEmploymentStatusList = s End Function

Кэшируйте данные на диске веб-сервера

Иногда в памяти вашего веб-сервера может быть слишком большое количество данных. "Слишком много", конечно, является спорным вопросом — это зависит от того, сколько памяти вы хотите использовать, а также число элементов для кэширования и частота, с которой эти элементы будут запрашиваться. В любом случае, если вы имеете слишком большое количество данных для кэширования в памяти, подумайте о переносе кэша в текстовый или XML-файл на жесткий диск веб-сервера. Вы можете одновременно комбинировать кэширование на диске и в памяти, чтобы сформировать оптимальную стратегию для вашего сайта.

Заметьте, что при измерении производительности одиночной ASP-страницы, получение данных с диска может не всегда быть быстрее, чем получение равноценных данных из БД. Но "файловое" кэширование уменьшает загрузку БД и сети, а при высокой загрузке БД до, значительно улучшит общую производительность после. Кэширование может быть очень эффективно при кэшировании результатов сложных запросов (например, соединение таблиц), трудоемких процедур сохранения, больших наборов записей. Чтобы убедится, насколько выгодным будет это решение требуется протестировать различные схемы сохранения.

ASP и COM обеспечивают несколько инструментальных средств для создания схем кэширования на диске. Функции набора записей ADO Save() и Open() сохраняют и загружают recordset c диска. Используя эти методы вы можете переписать код из прошлого совета, заменяя запись в объект Application на метод Save() для записи в файл.

Есть несколько других компонентов, которые работают с файлами:

* Scripting.FileSystemObject позволяет создавать, читать и записывать файл.
* MSXML, MicrosoftR XML parser поддерживает сохранение и загрузку XML-документов.
* Объект LookupTable (например, используемый на MSN.com) — лучший выбор для загрузки простых списков с диска.

Наконец, рассмотрите вопрос принудительного кэширования информации на диске. Сгенерированный HTML-код может быть сохранен на диске как.htm или.asp файл; гиперссылки могут указывать прямо на этот файл. Вы можете автоматизировать процесс генерации HTML, используя коммерческие инструментальные средства типа XBuilder или средства публикации в Интернет, входящие в MicrosoftR SQL ServerT. Кроме того, при помощи директивы #include можно включать отдельные HTML-части в файл ASP или читать HTML-файл с диска используя FileSystemObject. Например, на начальной странице vbCode (http://vbcity.com/vbcode/ru/home.asp) приводятся 10 последних тем обсуждения двух дискуссионных форумов. Отобразить эти списки можно при помощи создания двух наборов записей ADO при каждом обращении к данной странице или, следуя данному совету, сохранить их однажды в виде HTML-файла list.inc, а затем включать в home.asp:

Второй путь работает значительно быстрее.

Избегайте кэшировать медленные компоненты в объектах Application или Session

Несмотря на то, что кэшированиe данных в объектах Application или Session может быть хорошей идеей, кэширование COM-объектов может иметь серьезные ловушки. Занесение наиболее используемых COM-объектов в объекты Application или Session часто соблазняет, но, к сожалению, много COM-объектов, включая все, написанные в Visual Basic 6.0 или ранее, могут вызывать серьезные критические проблемы после сохранения в объектах Application или Session.

В частности, любой компонент, который выполняется медленно, вызовет критические проблемы когда кэшируется в объектах Session или Application. Быстрый (проворный non-agile) компонент — компонент, помеченный ThreadingModel=Both, который объединен Free-threaded marshaler (FTM), или — компонент, помеченный ThreadingModel=Neutral. (Neutral — новая модель в WindowsR 2000 and COM+). Следующие компоненты не проворны:

* Free-threaded components.
* Apartment-threaded components.
* Single-threaded component.
* Configured components (библиотека Microsoft Transaction Server (MTS)/COM+ и серверные приложения) не проворны пока они Neutral-threaded. Apartment-threaded components и другие не проворные компоненты хорошо работают в пределах страницы (т.е. создаются и разрушаются в пределах одной ASP-страницы).

В IIS 4.0 компонент, отмеченный ThreadingModel=Both выполняется быстро. В IIS 5.0 уже не так достаточно. Компонент не должен только быть отмечен как Both, он должен также объединен FTM.

IIS выполняет проверку компонентов, но если вы хотите ее отменить (т.е. хотите позволить непроворным компонентам быть сохраненными в объектах Application или Session), вы можете установить AspTrackThreadingModel в metabase в значение True. Но это (изменение AspTrackThreadingModel) не рекомендуется.

IIS 5.0 выдаст сообщение об ошибке, если Вы пытаетесь сохранить непроворный компонент, созданный с использованием Server.CreateObject, в объекте Application. Вы можете обойти это, используя в Global.asa, но это также не рекомендуется, поскольку это ведет к проблемам (очереди и сериализация), объясняемым ниже.

Что же все-таки неправильно если вы кэшируете непроворные компоненты? Непроворный компонент, кэшируемый в объекте Session блокирует Session от других рабочих потоков (thread) ASP. ASP обслуживает пул (контейнер) рабочих потоков, запрашиваемых другими сервисами. Обычно, новый запрос обрабатывается первым доступным потоком. Если Session блокирована, то запрос должен ждать поток, когда он станет доступным. Проведем аналогию, которая поможет понять эту ситуацию: вы идете в магазин, выбираете несколько булок, и платите за них в кассе #3. Всякий раз, после того как вы выбрали булки в том магазине, вы всегда оплачиваете их в кассе #3, даже в том случае, когда в других кассах короче очередь или даже вообще нет покупателей.

Сохранение непроворных компонентов в объект Application накладывает столь же негативный эффект на производительность. ASP создает специальный поток для выполнения меделенных компонентов в пределах Application. Это имеет два последствия: все запросы выстраиваются в очередь к этому потоку и все запросы сериализуются. Выстраивание в очередь означает, что параметры были сохранены в общедоступной области памяти; запросы переключаются к специальному потоку; метод компонента выполнен; результаты выстраиваются в общедоступную область. Сериализация (преобразование в последовательную форму) означает, что все методы выполняются в одно время. Для двух различных потоков ASP не возможно одновременное выполнение методов общедоступного компонента. Это уничтожает многопотоковость (параллелизм), особенно на мультипроцессорных системах. Хуже всего то, что все непроворные компоненты в пределах Application совместно используют один поток ("Host STA"), так что негативные результаты сериализации налицо.

Смущены? Есть некоторые общие правила. Если Вы пишете объекты в Visual Basic (6.0 или ранее), не храните их в объектах Application или Session. Если вы не знаете потоковую модель объекта, не храните его в кэше. Вместо кэширования где-либо непроворных объектов, вы должны создать и удалить их на каждой странице. Объекты выполнятся непосредственно в рабочем потоке ASP и не будет никакой очереди или сериализации. Производимость будет адекватна, если COM-объекты запущены под IIS и если они не используют много времени, чтобы инициализироваться и уничтожаться. Заметьте, что однопотоковые (single-threaded) объекты не должны использоваться этот путь. Будьте внимательным — VB может создавать однопотоковые объекты! Если вы используете однопотоковые объекты, этот путь (типа таблицы Microsoft Excel) не рассчитывает на высокую производительность.

Наборы записей (recordset) ADO могут безопасно кэшироваться когда ADO отмечен как Free-threaded. Чтобы сделать ADO как Free-threaded используйте файл Makfre15.bat, который обычно зафиксирован в каталоге Program FilesCommon FilesSystemADO.

Предупреждение: ADO не должен быть Free-threaded, если вы используете Microsoft Access в качестве БД. Набор записей ADO должен быть также вообще отсоединен, если вы не можете управлять конфигурацией ADO на вашем веб-сайте.

Не кэшируйте соединение БД в объектах Application или Session

Кэширование соединений ADO — обычно плохая стратегия. Если один объект Connection сохранен в объекте Application и используется на всех страницах, то все страницы будут бороться за использование этого соединения. Если объект Connection сохранен в ASP-объекте Session, то соединение БД будет создано для каждого пользователя. Это создает излишнюю загрузку веб-сервера и БД.

Вместо кэширования соединений БД, создавайте и уничтожайте объекты ADO на каждой ASP странице, которая использует ADO. Это эффективно, потому что IIS имеет встроенное подключение БД. Более точно, IIS автоматически допускает объединение подключений OLEDB и ODBC. Это гарантирует, что создание и уничтожение связей на каждой странице будут эффективны.

Так как соединенные наборы хранят ссылки на подключение БД, это следует, что вы должны не кэшировать соединенные наборы в объектах Application или Session. Однако, вы можете безопасно кэшировать отсоединенные наборы, которые не держат ссылку на подключение. Чтобы отсоединить набор записей, сделайте следующие два шага:

Set rs = Server.CreateObject("ADODB.RecordSet") rs.CursorLocation = adUseClient " шаг 1 " Заполните recordset с данными rs.Open strQuery, strProv " Теперь отсоедините recordset от источника данных rs.ActiveConnection = Nothing " шаг 2

Подробную информацию относительно подключений смотрите в справочниках по ADO и SQL Server.

Разумное использование объекта Session

Теперь, когда в предыдущих советах были раскрыты достоинства кэширования данных в объектах Applications и Sessions, мы собираемся предложить вам избегать использования объекта Session. Сессии имеют несколько ловушек когда используются на загруженных сайтах. Под "загруженными" имеются ввиду сайты с сотнями запрашиваемых страниц в секунду или тысячами пользователей одновременно. Этот совет также важен для сайтов, которые должны масштабироваться горизонтально — т.е. те сайты, которые используют несколько серверов для распределения нагрузки и обеспечения отказоустойчивости при сбоях. Для меньших сайтов, типа intranet-сайтов, преимущества применения Sessions все же перевешивают.

Обобщая, ASP автоматически создает Session для каждого пользователя, который обращается к веб-серверу. Каждая сессия занимает приблизительно 10 Кб памяти (сверх любых данных, сохраненных в Session) и немного замедляет выполнение всех запросов. Сессия остается действующей до окончания таймаута (timeout), обычно 20 мин.

Но самая большая проблема при использовании сессий — это не производительность, а расширяемость. Сессии не охватывают все задействованные веб-сервера; как только Session была создана на одном сервере ее данные остаются там. Это означает, что если вы используете сессии на мультисерверном веб-сайте, вы должны придумать стратегию для обработки запросов каждого пользователя, которые должны быть всегда направлены на сервер, на котором существует сессия этого пользователя. Это называется "застреванием" пользователя на сервере (или "липкой сессией").

Объект Application также не охватывает все сервера: если вам нужно совместно использовать и обновлять данные Application через веб-сервера, вам нужно использовать конечную базу данных. Однако неизменяемые (read-only) данные Application все же полезны на мультисерверных сайтах.

Наиболее критичные к скорости сайты обычно используют не менее двух веб-серверов. Поэтому при проектировании вашего ASP-приложения вы должны либо использовать "липкие сессии", либо просто избегать применения объекта Session, также как любых других методов, сохраняющих информацию пользователя на разных веб-серверах.

Если же вы не используете Session, то убедитесь, что отключили их. Это можно сделать посредством Internet Services Manager (см. документацию по ISM). Но если вам все-таки необходимо использовать сессии, то есть несколько путей уменьшить их удары про производительности.

Вы можете переместить содержимое, которое не требует сессий (например, страницы help и т.д.) в отдельное ASP-приложение, у которого сессии выключены. Кроме того, на страницах, где объект Session не используется, применяйте следующую директиву, помещещаемую вверху страницы:

<% @EnableSessionState=False %>

Одна из основных причин ее применения — то, что Session создает интересную проблему в случае использования фрэймов (frameset). ASP гарантирует, что в любое время будет выполняться только один запрос от Session. Это делается для того, чтобы при одновременном запросе одним пользователем нескольких страниц, только один ASP-запрос был обработан сессией, что помогает избежать проблем многопоточного доступа к объекту Session. К сожалению, в результате этого все страницы в frameset будут загружаться последовательно, а не одновременно, и пользователю придется продолжительное время ждать полной загрузки. Мораль этой истории: если вы не уверены, что с использованием фрэймов и Session ваше приложение правильно работает, то используйте:

<% @EnableSessionState=False %>

Альтернативой использованию объекта Session являются многочисленные параметры управления Session. При передаче малых объемов данных (менее 4 Кб) обычно рекомендуется использовать Cookies, переменные QueryString и скрытые (hidden) переменные форм. При использовании большого количества передаваемых параметров (например, корзина произведенных заказов в он-лайн магазине) наиболее лучший выбор — конечная база данных.

Статья будет полезна как новичкам, так и профессионалам. Первые получат сведения об основных возможностях MySQL, без чтения документации. А уже имея представление о…