Исходники.Ру - Программирование
Исходники
Статьи
Книги и учебники
Скрипты
Новости RSS
Магазин программиста

Главная » Статьи по программированию » .NET - Framework »

Обсудить на форуме Обсудить на форуме

Стратегии кэширования Web-сервисов XML
Несмотря на возрастание скоростей работы процессоров и сетей, производительность остается основной заботой разработчиков приложений. А значит, независимо от того, пишете ли вы Web-сервис на основе XML (XML Web Service), загружаете битовые карты изображений в память видеоплаты или даже проектируете какой-нибудь потрясающий чип нового поколения, вам безусловно стоит подумать об универсальном механизме повышения производительности: о кэшировании.

Введение

Несмотря на возрастание скоростей работы процессоров и сетей, производительность остается основной заботой разработчиков приложений. А значит, независимо от того, пишете ли вы Web-сервис на основе XML (XML Web Service), загружаете битовые карты изображений в память видеоплаты или даже проектируете какой-нибудь потрясающий чип нового поколения, вам безусловно стоит подумать об универсальном механизме повышения производительности: о кэшировании.

В этом выпуске рубрики At Your Service будет рассмотрено, как вы, разработчик и потребитель Web-сервисов, можете задействовать преимущества кэширования. Мы обсудим кэширование на уровне приложения в ASP.NET, а также кэширование в HTTP и его применение к Web-сервисам XML. Наконец, мы подумаем, как использовать пример сервиса Discovery вымышленной компании MSDN Pencil Company, и реализовать для него стратегию кэширования, имеющую смысл при ежедневном обновлении каталога продукции этой компании.

 

На что обращать внимание при выборе вариантов кэширования

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

Много ли у меня динамических данных?

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

Не конфиденциальные ли это данные?

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

Использует ли Web-сервис ресурсы, общие для нескольких запросов?

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

Можно ли предсказать будущую потребность в ресурсах?

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

Где кэшировать данные Web-сервиса?

Зачастую правильный ответ на этот вопрос: где угодно. Но каковы варианты их кэширования? Чтобы ответить на этот вопрос, рассмотрим потенциально возможный сценарий работы Web-сервиса, приведенный на рис. 1.

Браузер конечного пользователя

HTTP-прокси

Web-сервер

Web-сервис

Внутренний Web-сервис

SQL-сервер

Рис. 1. Возможные варианты кэширования для одного из сценариев работы Web-сервиса XML

Как видите, конечный пользователь обращается к сайту (он показан в желтом прямоугольнике), не зная, что этот сайт находится за HTTP-прокси. Затем Web-сервер посылает SOAP-запрос к Web-сервису в другой организации (в зеленом прямоугольнике). SOAP-запрос тоже проходит через HTTP-прокси. После этого первый Web-сервис переправляет запрос другому, внутреннему Web-сервису, который запрашивает требуемые данные у Microsoft® SQL Server и наконец возвращает ответ. SQL-данные используются при подготовке ответа от внутреннего Web-сервиса, а на основе его ответа создается ответ от первого Web-сервиса. На сайте из ответа Web-сервиса формируется HTML-страница, которая и возвращается браузеру конечного пользователя.

Запрос и ответ проходят через множество прокси-серверов и маршрутизаторов. Так где же кэшировать данные ответа? В этом сценарии - на каждом этапе. SQL-сервер может кэшировать результаты запроса к сервису, внутренний Web-сервис - результаты SQL-запроса, первый Web-сервис - результаты внутреннего Web-сервиса (равно как и HTTP-прокси в "зеленой" организации), Web-сервер - ответ Web-сервиса, прокси-сервер в "желтой" организации - ответ Web-сервера, а браузер конечного пользователя - HTML-страницу.

Когда заканчивается срок хранения данных?

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

Как уведомлять потребителей Web-сервиса о том, что данные устарели?

Ответ на этот вопрос зависит от способа кэширования. Клиентским приложениям часто имеет смысл кэшировать данные. В этом случае клиентские приложения должны быть своевременно информированы об устаревании данных. Для этого, вероятно, потребуется включать в возвращаемые данные сведения о сроке их жизни. Для Web-сервиса это может означать добавление к XML-ответу специального поля, где указывается "срок жизни".

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

Можно ли полагаться на то, что данные будут в кэше?

Если коротко - нет. Почти в любой механизм кэширования встроен алгоритм удаления устаревший информации из кэша. Данные обычно удаляются из-за устаревания, но иногда и из-за того, что к ним давно не обращались; при этом в кэш загружаются другие данные, ведь его емкость ограниченна. Так что большинство механизмов кэширования не гарантирует наличия данных в кэше. Это тем более верно в отношении механизмов кэширования в сетях (shared caching mechanisms) вроде кэшей HTTP-прокси или даже ASP.NET.

Что будет, если потребители Web-сервиса не воспользуются кэшированными данными?

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

 

Сценарии кэширования

Теперь, когда мы рассмотрели некоторые вопросы, требующие ответа для оценки возможностей кэширования, обсудим, каковы эти возможности для разработчиков Web-сервисов XML. Сначала мы поговорим о двух подходах к кэшированию: на прикладном уровне и на уровне протокола. Затем проверим, что предлагает ASP.NET для реализации кэширования на обоих уровнях.

Кэширование на прикладном уровне

Словом "приложение" (application) в наше время слишком злоупотребляют. В этой статье под термином "приложение" я понимаю "прикладной уровень" (application level), и он относится как к Web-сервису, так и клиенту этого сервиса, т. е. к тем областям, где разработчик пишет код, быстродействие которого чувствительно к кэшированию.

Следовательно, "кэширование на прикладном уровне" относится и к коду Web-сервиса, и к коду клиента, который тоже так или иначе кэширует данные. Кэширование в Web-сервисе может заключаться в сохранении в памяти компьютера экземпляров повторно используемых классов или не меняющихся данных.

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

Поддержка кэширования обычно связана с предоставлением сведений о сроке жизни данных. Если этот срок всегда постоянный, его можно документировать и "зашить" в код клиента. Тогда указывать его в ответе Web-сервиса не потребуется. Однако во многих случаях срок жизни данных изменчив, и соответствующие сведения нужно хранить вместе с кэшируемыми данными. При кэшировании на прикладном уровне в данные можно включить новое поле, содержащее параметр - срок жизни. Так как обычно срок жизни - это фактически метаинформация, описывающая данные, подходящее место для ее хранения этих сведений - элемент SOAP-заголовков. Хранить метаинформацию о SOAP-сообщении следует именно там.

HTTP-кэширование

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

В настоящее время для обращения к Web-сервисам используются SOAP-сообщения, помещаемые в тело HTTP-запроса POST. В отличие от HTTP-запросов GET, тело POST-запросов выходит за область действия HTTP. Поэтому реализации протокола HTTP в прокси-серверах и клиентах не способны определять, как кэшировать ответы на HTTP-запросы POST. ASP.NET поддерживает вызов Web-методов через GET-запросы, но этот механизм в основном предназначен для отладки и не поддерживается большинством других инструментальных средств SOAP.

Возможности кэширования в ASP.NET

Одна из приятных особенностей разработки Web-сервисов XML в среде ASP.NET - преимущества обширной функциональности, которой уже пользуются разработчики приложений Web Forms. Встроенная функциональность ASP.NET облегчает кэширование Web-сервисов XML. Как раз сейчас Роб Ховард (Rob Howard) в своей колонке Nothing But ASP.NET начал цикл статьей, посвященных кэшированию в этой среде. Чтобы лучше понять специфику ASP.NET с точки зрения кэширования, ознакомьтесь с этими статьями. Я же сосредоточусь на механизмах, помогающих в написании Web-сервисов.

По сути, в ASP.NET существуют три различных подхода к кэшированию: кэширование выходных данных ASP.NET, HTTP-ответов и приложений ASP.NET. При кэширование выходных данных разработчик сообщает ASP.NET, что результат запроса к конкретной страницы можно возвращать в ответ на все дальнейшие запросы к этой странице. Для последующих запросов ASPX-сценарий не исполняется - вместо этого незамедлительно возвращается результат предыдущего запроса. В кэш выходных данных можно поместить как всю страницу целиком, так и отдельные элементы управления ASP.NET. Существуют механизмы для указания срока жизни кэшированных данных, равно как и механизмы для кэширования различных представлений страницы в зависимости от входных данных Web-форм.

Чтобы настроить кэширование выходных данных ASP.NET для Web-сервисов, добавьте параметр CacheDuration к атрибуту WebMethod в определении Web-метода. Этот параметр определяет время (в секундах), в течении которого ответ хранится в кэше выходных данных. Следующий фрагмент кода демонстрирует, как поместить данные в кэш на 60 секунд.

 

<WebMethod(CacheDuration:=60)> _
Public Function HelloWorld() As String
    Return "Hello World"
End Function

В отличие от кэширования выходных данных ASP.NET кэширование HTTP-ответов - просто способ, которым ASP.NET позволяет настроить HTTP-заголовки так, чтобы клиент и прокси-серверы знали, как кэшировать возвращаемые им HTTP-ответы. Для этого применяется класс HttpCachePolicy. В коде Web-сервиса он доступен из Context.Response.Cache, но, как уже говорилось, возможности его применения к SOAP-запросам в теле POST-запросов ограниченны.

Третья форма кэширования в ASP.NET реализована в виде весьма интересного класса Cache. Не путайте классы HttpCachePolicy и Cache - даже несмотря на то, что их родительские классы ссылаются на оба этих свойства как на "cache". Класс Cache доступен прямо из класса HttpContext Web-сервиса. Он предоставляет базовую поддержку кэширования для ASP.NET-приложения. В кэше можно хранить любые данные из его набора (collection). Во многих отношениях этот класс похож на класс HttpApplicationState, хранящий глобальные данные приложения в своем наборе.

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

 

Dim Foo as New MyFooClass()
Context.Cache.Insert("foo", _
                     Foo, _
                     Nothing, _
                     DateAdd(DateInterval.Minute, 30, Now()), _
                     System.Web.Caching.Cache.NoSlidingExpiration)
Функция Insert поддерживает несколько вариантов добавления данных в кэш. Первый аргумент, "foo", - это ключ для ссылки на наш объект в наборе. Второй параметр представляет сам элемент, помещаемый в кэш. Третий - устанавливает зависимости вроде файловой, о которой мы уже упоминали. В нашем случае никаких зависимостей нет, так что этот параметр равен "Nothing". Следующий параметр явно задает срок устаревания элемента в кэше. Вызовом функции DateAdd мы указали, что объект устареет через 30 минут. Наконец, последний параметр задает "скользящее" (sliding) устаревание. Скользящее устаревание означает, что кэшированный элемент удаляется, если к нему не обращаются в течении указанного промежутка времени. Так как мы явно установили срок жизни данных (30 минут), этому параметру присваивается значение NoSlidingExpiration.

 

Кэширование каталога MSDN Pencil Company

Теперь рассмотрим конкретный пример и определим стратегию кэширования в этом сценарии. В последней статье рубрики At Your Service Скотт определил ряд изменений в интерфейсе PencilDiscovery MSDN Pencil Company, так что теперь можно запрашивать весь каталог, не заставляя пользователей по нескольку раз выполнять поиск. Цель этого решения - позволить "умным" клиентским приложениям кэшировать весь каталог и обеспечить возможность запросов к данным. Это снизит нагрузку на наш сервис за счет уменьшения запросов и, кроме того, предоставит дополнительную информацию клиентам, пользующимся нашим сервисом. Мы решили, что в нашей реализации мы скорее всего будем обновлять данные раз в день, добавляя в каталог новые карандаши или удаляя те, которых больше нет на складе.

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

Теперь выберем параметры кэширования прикладного уровня. С клиентской стороны с этим особых проблем нет. Клиентское приложение запрашивает данные раз в день и выполняет к ним любые запросы, пока не настанет следующий день. Но одна проблема все же есть: уведомление клиентского приложения о сроке жизни данных.

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

Выход прост: включать информацию о сроке жизни данных в ответ. В определение интерфейса Скотт включил элемент ValidUntil (в раздел объявления типов каталога карандашей). Мы воспользуемся этим полем для указания срока устаревания данных каталога. Кроме того, включив информацию о времени в SOAP-сообщение, мы получим дополнительное преимущество - поддержку кэширования данных, даже если они передаются не по HTTP, а по другому протоколу. Например, возможна ситуация, когда кто-то запрашивает каталог от Web-сервиса по HTTP, а кто-то - по SMTP. Так как срок жизни хранится не только в HTTP-заголовках, оно не потеряется при отправке сообщения по SMTP.

Следующий клиентский код на Microsoft® Visual Basic® .NET иллюстрирует использование клиентом свойства ValidUntil для того, чтобы определить, надо ли обновить содержимое кэша каталога до выполнения запроса пользователя.

 

Dim PencilResults() As org.pencilsellers.Pencil
If PencilCatalog.ValidUntil < Now() Then
    Dim Discovery As New org.pencilsellers.DiscoveryBinding()
    PencilCatalog = Discovery.GetCatalog()
End If
PencilResults = QueryCachedCatalog(PencilCatalog, QueryCriterion)
На серверной стороне мы должны не только предоставить клиенту сведения о том, когда истекает срок хранения данных (с помощью элемента ValidUntil), но и подумать о том, как избежать создания каталога "с чистого листа" при получении каждого запроса. Один из способов добиться этого - добавить параметр CacheDuration к атрибуту WebMethodAttribute. В нашем случае CacheDuration имеет один недостаток: время хранения данных фиксируется на этапе разработки. Установив CacheDuration равным 24 часам, мы можем столкнуться со следующей проблемой.

Допустим, мы создаем каталог в 6 утра 1-го апреля и устанавливаем элемент ValidUntil, соответствующим 6 утра 2-го апреля. Эти данные попадут в первый же ответ, и он окажется в кэше выходных данных ASP.NET. Теперь предположим, что примерно в 10 вечера 1-го апреля поступает огромное количество запросов к другим ASP.NET-страницам. Так как к каталогу запросы не поступают, система, вероятно, удалит его из кэша, чтобы освободить ресурсы кэша для более важных данных. Затем в 10:30 вечера 1-го апреля поступает еще один запрос к каталогу карандашей. Так как в кэше выходных данных ответа нет, Web-метод запускается еще раз, при этом срок жизни данных устанавливается равным 10:30 вечера 2-го апреля. Тут-то проблема и проявляется: каталог карандашей обновится в 6 утра 2-го апреля, а из кэша будут по-прежнему поступать данные за вчерашний день. Так что нам нужна система кэширования, которая позволяет явно указать срок хранения данных в период выполнения.

Кэш приложений ASP.NET предлагает простой способ решения этой проблемы. Запускаем утилиту WSDL.EXE из .NET Framework SDK с параметром командной строки /Server и создаем различные классы на базе WSDL-определения интерфейса Pencil Discovery, в том числе класс Catalog, основанный на определенном в интерфейсе типе. Мы просто создаем каталог из результатов SQL-запроса и добавляем его к кэшу приложений ASP.NET посредством метода Insert. Срок жизни элемента в кэше устанавливается равным значению ValidUntil каталога. Код для Web-метода GetCatalog приведен ниже.

Заметьте, что я все равно применяю параметр CacheDuration для добавления ответа к кэшу выходных данных ASP.NET, но теперь срок жизни относительно мал - 10 минут. Тем самым я минимизирую время, в течение которого возможен возврат устаревших данных, но все равно увеличиваю производительность за счет кэширования, что весьма полезно, когда к каталогу поступает множество запросов. Мы полагаем, что большинство запросов к каталогу поступает в пределах 10 минут до окончания срока хранения данных.

<System.Web.Services.WebMethodAttribute( _
     CacheDuration:=600), _
 System.Web.Services.Protocols.SoapDocumentMethodAttribute( _
     "http://pencilsellers.org/2002/04/pencil/GetCatalog", _
     RequestNamespace:= _
         "http://pencilsellers.org/2002/04/pencil/discovery", _
     ResponseNamespace:= _
         "http://pencilsellers.org/2002/04/pencil/discovery", _
     Use:=System.Web.Services.Description.SoapBindingUse.Literal, _
     ParameterStyle:= _
         System.Web.Services.Protocols.SoapParameterStyle.Wrapped, _
     Binding:="DiscoveryBinding")> _
Public Overrides Function GetCatalog() As Catalog
    Dim PencilCatalog As Catalog
    If Context.Cache("PencilCatalog") Is Nothing Then
        PencilCatalog = CreateCatalog()
        Context.Cache.Insert("PencilCatalog", _
            PencilCatalog, _
            Nothing, _
            PencilCatalog.ValidUntil, _
            System.Web.Caching.Cache.NoSlidingExpiration)
    Else
        PencilCatalog = Context.Cache("PencilCatalog")
        If PencilCatalog.ValidUntil < Now() Then
            Context.Cache.Remove("PencilCatalog")
            PencilCatalog = CreateCatalog()
            Context.Cache.Insert("PencilCatalog", _
                PencilCatalog, _
                Nothing, _
                PencilCatalog.ValidUntil, _
                System.Web.Caching.Cache.NoSlidingExpiration)
        End If
    End If
    Return PencilCatalog
End Function

Заключение

Весьма вероятно, что при разработке Web-сервисов XML вам захочется реализовать какой-нибудь механизм кэширования данных. Это можно сделать разными способами: воспользоваться ограниченными функциями кэширования HTTP, выполнять кэширование прикладного уровня на сервере, кэшировать ответы на клиенте или просто разработать Web-сервис так, чтобы "интеллектуальные" клиенты снимали часть нагрузки с сервера. В следующей статье Скотт собирается рассмотреть проблему, с которой часто сталкиваются разработчики и потребители Web-сервисов: слияние XML-данных (XML merging). Скотт объединит каталог, возвращаемый Web-методом GetCatalog, с каталогом другой компании, и Web-сайт сможет предоставлять своим пользователям общий каталог карандашей.

Мэт Пауэлл (Matt Powell) - член группы MSDN Architectural Samples. Участвовал в разработке прорывного SOAP Toolkit 1.0. Среди других достижений Мэта - книга "Running Microsoft Internet Information Server" (Microsoft Press), написанная в соавторстве, а также многочисленные статьи в журналах. А дома его всегда ждет замечательная семья.


Может пригодится:


Автор: Matt Powell
Прочитано: 4111
Рейтинг:
Оценить: 1 2 3 4 5

Комментарии: (0)

Добавить комментарий
Ваше имя*:
Ваш email:
URL Вашего сайта:
Ваш комментарий*:
Код безопастности*:

Рассылка новостей
Рейтинги
© 2007, Программирование Исходники.Ру