Одна из причин успеха ASP.NET заключается в том, что он снижает входной барьер для веб-разработчиков. Вам не обязательно иметь степень доктора компьютерных наук, чтобы писать код ASP.NET. Многие разработчики ASP.NET, с которыми я встречаюсь на работе, являются самоучками и писали электронные таблицы Microsoft® Excel® до того, как начали писать C# или Visual Basic®. Теперь они пишут веб-приложения и в целом заслуживают похвалы за свою работу.
Но с силой приходит ответственность, и даже опытные разработчики ASP.NET могут допускать ошибки. За многие годы консультирования по проектам ASP.NET я обнаружил, что определенные ошибки с наибольшей вероятностью приводят к повторяющимся дефектам. Некоторые из этих ошибок могут повлиять на производительность. Другие ошибки могут препятствовать масштабируемости. Некоторые ошибки также могут стоить командам разработчиков драгоценного времени на отслеживание ошибок и неожиданного поведения.
Вот 10 ошибок, которые могут вызвать проблемы при выпуске производственных приложений ASP.NET, и способы их избежать. Все примеры взяты из моего собственного опыта создания реальных веб-приложений в реальных компаниях, и в некоторых случаях я привожу контекст, описывая некоторые проблемы, с которыми команда разработчиков ASP.NET столкнулась в процессе разработки.
LoadControl и кэширование вывода Существует очень мало приложений ASP.NET, которые не используют пользовательские элементы управления. До появления главных страниц разработчики использовали пользовательские элементы управления для извлечения общего содержимого, такого как верхние и нижние колонтитулы. Даже в ASP.NET 2.0 пользовательские элементы управления предоставляют эффективный способ инкапсуляции содержимого и поведения, а также разделения страницы на области, кэшированием которых можно управлять независимо от страницы в целом (процесс, называемый сегментами. Особая форма кэширования вывода). ).
Пользовательские элементы управления можно загружать декларативно или принудительно. Принудительная загрузка зависит от Page.LoadControl, который создает экземпляр пользовательского элемента управления и возвращает ссылку на элемент управления. Если пользовательский элемент управления содержит элементы пользовательского типа (например, общедоступное свойство), вы можете привести ссылку и получить доступ к пользовательскому элементу из своего кода. Пользовательский элемент управления на рис. 1 реализует свойство BackColor. Следующий код загружает пользовательский элемент управления и присваивает значение BackColor:
protected void Page_Load(object sender, EventArgs e){// Загрузите пользовательский элемент управления и добавьте его на страницу Control control = LoadControl("~/MyUserControl.ascx") ;PlaceHolder1 .Controls.Add(control);//Установим цвет фона ((MyUserControl)control).BackColor = Color.Yellow;}
Приведенный выше код на самом деле очень прост, но это ловушка, в которую может попасть неосторожный разработчик. Сможете ли вы найти недостаток?
Если вы догадались, что проблема связана с кэшированием вывода, вы правы. Как видите, приведенный выше пример кода компилируется и работает нормально, но если вы попытаетесь добавить в MyUserControl.ascx следующий оператор (который совершенно законен):
<%@ OutputCache Duration="5" VaryByParam="None" %>
Затем при следующем запуске страницы вы увидите исключение InvalidCastException (о, радость!) и следующее сообщение об ошибке:
«Невозможно привести объект типа «System.Web.UI.PartialCachingControl» к типу «MyUserControl».
Таким образом, этот код работает нормально без директивы OutputCache, но завершается ошибкой, если директива OutputCache добавлена. ASP.NET не должен вести себя таким образом. Страницы (и элементы управления) не должны зависеть от кэширования вывода. Итак, что это значит?
Проблема в том, что когда для пользовательского элемента управления включено кэширование вывода, LoadControl больше не возвращает ссылку на экземпляр элемента управления, а вместо этого возвращает ссылку на экземпляр PartialCachingControl, который может обертывать или не обертывать экземпляр элемента управления, в зависимости от того, является ли Выходные данные элемента управления — это кеш. Следовательно, если разработчик вызывает LoadControl для динамической загрузки пользовательского элемента управления и преобразует ссылку на элемент управления для доступа к методам и свойствам, специфичным для элемента управления, он должен обратить внимание на то, как он это делает, чтобы код выполнялся независимо от того, существует ли Директива OutputCache.
На рис. 2 показан правильный способ динамической загрузки пользовательского элемента управления и преобразования возвращаемой ссылки на элемент управления. Вот краткое описание того, как это работает:
• Если в файле ASCX отсутствует директива OutputCache, LoadControl возвращает ссылку MyUserControl. Page_Load преобразует ссылку в MyUserControl и устанавливает свойство BackColor элемента управления.
• Если файл ASCX содержит директиву OutputCache и выходные данные элемента управления не кэшируются, LoadControl возвращает ссылку на PartialCachingControl, свойство CachedControl которого содержит ссылку на базовый MyUserControl. Page_Load преобразует PartialCachingControl.CachedControl в MyUserControl и устанавливает свойство BackColor элемента управления.
• Если файл ASCX содержит директиву OutputCache и выходные данные элемента управления кэшируются, LoadControl возвращает ссылку на PartialCachingControl, свойство CachedControl которого пусто. Обратите внимание, что Page_Load больше не продолжается. Свойство BackColor элемента управления невозможно установить, поскольку выходные данные элемента управления поступают из выходного кэша. Другими словами, MyUserControl для установки свойств вообще не существует.
Код на рис. 2 будет выполняться независимо от того, есть ли в файле .ascx директива OutputCache. Хоть это и выглядит немного сложнее, но позволит избежать досадных ошибок. Простота не всегда означает простоту обслуживания.
В начало Сеансы и кэширование вывода Говоря о кэшировании вывода, как ASP.NET 1.1, так и ASP.NET 2.0 имеют потенциальную проблему, которая затрагивает страницы кэша вывода на серверах под управлением Windows Server™ 2003 и IIS 6.0. Я лично видел, как эта проблема возникала дважды на рабочем сервере ASP.NET, и оба раза она была решена путем отключения буферизации вывода. Позже я узнал, что есть лучшее решение, чем отключение кэширования вывода. Вот как это выглядело, когда я впервые столкнулся с этой проблемой.
Произошло следующее: веб-сайт (назовем его здесь Contoso.com, на котором работает общедоступное приложение электронной коммерции в небольшой веб-области ASP.NET) связался с моей командой и пожаловался, что у них возникла ошибка «перекрестной многопоточности». Клиенты, использующие веб-сайт Contoso.com, часто внезапно теряют введенные данные, но вместо этого видят данные, относящиеся к другому пользователю. После небольшого анализа мы обнаружили, что описание перекрестной обработки неточно; ошибка «перекрестная сессия» более уместна. Похоже, что Contoso.com хранит данные в состоянии сеанса, и по какой-то причине пользователи время от времени случайным образом подключаются к сеансам других пользователей.
Один из членов моей команды написал диагностический инструмент, который регистрирует ключевые элементы каждого HTTP-запроса и ответа, включая заголовок Cookie. Затем он установил инструмент на веб-сервер Contoso.com и дал ему поработать несколько дней. Результаты очень очевидны. Примерно один раз на каждые 100 000 запросов ASP.NET правильно назначает идентификатор сеанса совершенно новому сеансу и возвращает идентификатор сеанса в заголовке Set-Cookie. Затем он возвращает тот же идентификатор сеанса (то есть тот же заголовок Set-Cookie) в следующем непосредственно соседнем запросе, даже если запрос уже был связан с действительным сеансом и идентификатор сеанса в файле cookie был отправлен правильно. По сути, ASP.NET случайным образом переключает пользователей из их собственных сеансов и подключает их к другим сеансам.
Мы были удивлены и решили выяснить, почему. Сначала мы проверили исходный код Contoso.com и, к нашему облегчению, проблемы не оказалось. Далее, чтобы убедиться, что проблема не связана с хостом приложения в веб-среде, мы оставили работающим только один сервер и отключили все остальные. Проблема сохраняется, что неудивительно, поскольку наши журналы показывают, что совпадающие заголовки Set-Cookie никогда не приходят с двух разных серверов. ASP.NET случайно генерирует повторяющиеся идентификаторы сеансов, что невероятно, поскольку для генерации этих идентификаторов используется класс .NET Framework RNGCryptoServiceProvider, а идентификаторы сеансов достаточно длинные, чтобы гарантировать, что один и тот же идентификатор никогда не будет сгенерирован дважды (по крайней мере, в следующем сеансе). не будет генерироваться дважды за триллионы лет). Кроме того, даже если RNGCryptoServiceProvider ошибочно генерирует повторяющиеся случайные числа, это не объясняет, почему ASP.NET таинственным образом заменяет действительный идентификатор сеанса новым (который не является уникальным).
Догадавшись, мы решили взглянуть на кэширование вывода. Когда OutputCacheModule кэширует ответ HTTP, он должен быть осторожным, чтобы не кэшировать заголовок Set-Cookie; в противном случае кэшированный ответ, содержащий новый идентификатор сеанса, подключит всех получателей кэшированного ответа (и пользователя, чей запрос сгенерировал кэшированный ответ). на ту же сессию. Мы проверили исходный код; на сайте Contoso.com включено кэширование вывода на обеих страницах. Мы отключили кэширование вывода. В результате приложение работало несколько дней без единой проблемы межсессионного взаимодействия. После этого он работал без каких-либо ошибок более двух лет. В другой компании с другим приложением и другим набором веб-серверов мы увидели, что та же самая проблема исчезла. Как и в случае с Contoso.com, устранение кэша вывода решает проблему.
Позже Microsoft подтвердила, что такое поведение связано с проблемой в OutputCacheModule. (К тому времени, когда вы читаете эту статью, возможно, было выпущено обновление.) Когда ASP.NET используется с IIS 6.0 и включено кэширование в режиме ядра, OutputCacheModule иногда не удается удалить заголовок Set-Cookie из кэшированных ответов, которые он передает. в Http.sys. Ниже приведена конкретная последовательность событий, которая приводит к ошибке:
• Пользователь, который недавно не посещал сайт (и, следовательно, не имеет соответствующего сеанса), запрашивает страницу, на которой включено кэширование вывода, но выходные данные которого в настоящее время недоступны. в кэше.
• Запрос выполняет код, который обращается к последнему созданному сеансу пользователя, в результате чего файл cookie идентификатора сеанса возвращается в заголовке Set-Cookie ответа.
• OutputCacheModule предоставляет выходные данные для Http.sys, но не может удалить заголовок Set-Cookie из ответа.
• Http.sys возвращает кэшированные ответы на последующие запросы, ошибочно подключая к сеансу других пользователей.
Мораль этой истории в том, что состояние сеанса и кэширование вывода в режиме ядра несовместимы. Если вы используете состояние сеанса на странице с включенным кэшированием вывода, а приложение работает в IIS 6.0, вам необходимо отключить кэширование вывода в режиме ядра. Вы по-прежнему получите выгоду от кэширования вывода, но поскольку кэширование вывода в режиме ядра выполняется намного быстрее, чем обычное кэширование вывода, кэширование не будет таким эффективным. Дополнительные сведения об этой проблеме см. на странице support.microsoft.com/kb/917072.
Вы можете отключить кэширование вывода в режиме ядра для отдельной страницы, включив атрибут VaryByParam="*" в директиву OutputCache страницы, хотя это может привести к внезапному увеличению требований к памяти. Другой, более безопасный подход — отключить кэширование в режиме ядра для всего приложения, включив в web.config следующий элемент:
<httpRuntime EnableKernelOutputCache="false" />
Вы также можете использовать параметр реестра, чтобы отключить глобальное кэширование вывода в режиме ядра, то есть отключить кэширование вывода в режиме ядра для всех серверов. Подробности см. на странице support.microsoft.com/kb/820129.
Каждый раз, когда я слышу, как клиенты сообщают о загадочных проблемах с сеансом, я спрашиваю их, используют ли они кэширование вывода на каких-либо страницах. Если они используют кэширование вывода, а в качестве операционной системы используется Windows Server 2003, я бы рекомендовал отключить кэширование вывода в режиме ядра. Проблема обычно решается. Если проблема не решена, значит, ошибка существует в коде. Будьте бдительны!
Вернуться наверх
!
Срок действия билета проверки подлинности с помощью форм Можете ли вы определить проблему с помощью следующего кода?
FormsAuthentication.RedirectFromLoginPage(имя пользователя, правда);
Этот код может показаться нормальным, но его никогда не следует использовать в приложении ASP.NET 1.x, если только код в другом месте приложения не компенсирует негативные последствия этого оператора. Если вы не уверены, почему, продолжайте читать.
FormsAuthentication.RedirectFromLoginPage выполняет две задачи. Во-первых, когда FormsAuthenticationModule перенаправляет пользователя на страницу входа, FormsAuthentication.RedirectFromLoginPage перенаправляет пользователя на первоначально запрошенную страницу. Во-вторых, он выдает билет аутентификации (обычно содержащийся в файле cookie и всегда содержащийся в файле cookie в ASP.NET 1.x), который позволяет пользователю оставаться аутентифицированным в течение заранее определенного периода времени.
Проблема заключается именно в этом временном периоде. В ASP.NET 1.x при передаче в RedirectFromLoginPage другого параметра, имеющего значение false, выдается временный билет проверки подлинности, срок действия которого по умолчанию истекает через 30 минут. (Вы можете изменить период ожидания, используя атрибут Timeout в элементе web.config.) Однако передача другого параметра true приведет к выдаче постоянного билета аутентификации, действительного в течение 50 лет. Это создает проблему, потому что если кто-то украдет эту аутентификацию! билет, они могут использовать личность жертвы для доступа к веб-сайту в течение срока действия билета. Существует множество способов украсть билеты аутентификации — проверка незашифрованного трафика в общедоступных точках беспроводного доступа, создание сценариев на веб-сайтах, получение физического доступа к компьютеру жертвы и т. д. — поэтому передача true в RedirectFromLoginPage более безопасна, чем отключение вашего веб-сайта. Не намного лучше. К счастью, эта проблема решена в ASP.NET 2.0. RedirectFromLoginPage теперь одинаково принимает тайм-ауты, указанные в web.config для временных и постоянных билетов аутентификации.
Одно из решений — никогда не передавать true во втором параметре RedirectFromLoginPage в приложениях ASP.NET 1.x. Но это непрактично, поскольку на страницах входа часто имеется поле «Оставать меня в системе», которое пользователь может проверить, чтобы получить постоянный, а не временный файл cookie аутентификации. Другое решение — использовать фрагмент кода в Global.asax (или HTTP-модуле, если хотите), который изменяет файл cookie, содержащий билет постоянной аутентификации, прежде чем он будет возвращен браузеру.
На рис. 3 показан один такой фрагмент кода. Если этот фрагмент кода находится в Global.asax, он изменяет свойство Expires исходящего постоянного файла cookie проверки подлинности с помощью форм, так что срок действия файла cookie истекает через 24 часа. Вы можете установить тайм-аут на любую дату, изменив строку с комментарием «Новая дата истечения срока действия».
Вам может показаться странным, что метод Application_EndRequest вызывает локальный вспомогательный метод (GetCookieFromResponse) для проверки файла cookie аутентификации на предмет исходящего ответа. Метод Helper — это обходной путь для другой ошибки в ASP.NET 1.1, из-за которой в ответ добавлялись ложные файлы cookie, если вы использовали генератор строкового индекса HttpCookieCollection для проверки несуществующих файлов cookie. Использование генератора целочисленных индексов в качестве GetCookieFromResponse решает проблему.
В начало Состояние просмотра: тихий убийца производительности В каком-то смысле состояние просмотра — это величайшая вещь на свете. В конце концов, состояние просмотра позволяет страницам и элементам управления сохранять состояние между обратными передачами. Таким образом, вам не нужно писать код, чтобы предотвратить исчезновение текста в текстовом поле при нажатии кнопки или запрашивать базу данных и повторно привязывать DataGrid после обратной передачи, как это было бы в традиционном ASP.
Но у состояния просмотра есть обратная сторона: когда оно становится слишком большим, оно становится молчаливым убийцей производительности. Некоторые элементы управления, такие как текстовые поля, принимают решения на основе состояния просмотра. Другие элементы управления (в частности, DataGrid и GridView) определяют свое состояние просмотра на основе объема отображаемой информации. Я бы испугался, если бы GridView отображал 200 или 300 строк данных. Несмотря на то, что состояние представления ASP.NET 2.0 примерно в два раза меньше состояния представления ASP.NET 1.x, плохой GridView может легко снизить эффективную пропускную способность соединения между браузером и веб-сервером на 50 % и более.
Вы можете отключить состояние просмотра для отдельных элементов управления, установив для EnableViewState значение false, но некоторые элементы управления (особенно DataGrid) теряют некоторые функциональные возможности, когда они не могут использовать состояние просмотра. Лучшее решение для управления состоянием просмотра — хранить его на сервере. В ASP.NET 1.x вы могли переопределить методы страницы LoadPageStateFromPersistenceMedium и SavePageStateToPersistenceMedium и обрабатывать состояние представления по своему усмотрению. Код на рисунке 4 показывает переопределение, которое предотвращает сохранение состояния представления в скрытых полях и вместо этого сохраняет его в состоянии сеанса. Сохранение состояния представления в состоянии сеанса особенно эффективно при использовании с моделью процесса состояния сеанса по умолчанию (то есть, когда состояние сеанса хранится в рабочем процессе ASP.NET в памяти). Напротив, если состояние сеанса хранится в базе данных, только тестирование может показать, улучшает или снижает производительность сохранение состояния представления в состоянии сеанса.
Тот же подход используется в ASP.NET 2.0, но ASP.NET 2.0 предоставляет более простой способ сохранения состояния представления в состоянии сеанса.
Сначала определите адаптер пользовательской страницы, метод GetStatePersister которого возвращает экземпляркласса SessionPageStatePersister
.NET Framework:
; }}
Затем зарегистрируйте настраиваемый адаптер страницы в качестве адаптера страницы по умолчанию, поместив файл App.browsers в папку App_Browsers вашего приложения следующим образом:
<browsers><browser refID="Default"><controlAdapters><adapter controlType=" System.Web. UI.Page"adapterType="SessionPageStateAdapter" /></controlAdapters></browser></browsers>
(Вы можете назвать файл как угодно, лишь бы он имел расширение .browsers.) После этого ASP.NET загружает адаптер страницы и использует возвращенный SessionPageStatePersister для сохранения всего состояния страницы, включая состояние просмотра.
Одним из недостатков использования пользовательского адаптера страницы является то, что он применяется глобально к каждой странице приложения. Если вы предпочитаете сохранять состояние просмотра некоторых страниц в состоянии сеанса, но не других, используйте метод, показанный на рисунке 4. Кроме того, при использовании этого метода вы можете столкнуться с проблемами, если пользователь создает несколько окон браузера в одном сеансе.
Вернуться наверх
Состояние сеанса SQL Server: еще один убийца производительности
ASP.NET упрощает сохранение состояния сеанса в базе данных: просто переключите переключатель в web.config, и состояние сеанса легко переносится во внутреннюю базу данных. Это важная функция для приложений, работающих в веб-среде, поскольку она позволяет каждому серверу в этой области использовать общий репозиторий состояния сеанса. Добавленная активность базы данных снижает производительность отдельных запросов, но повышенная масштабируемость компенсирует потерю производительности.
Все это звучит хорошо, но все меняется, если учесть несколько моментов:
• Даже в приложениях, использующих состояние сеанса, большинство страниц не используют состояние сеанса.
• По умолчанию диспетчер состояния сеанса ASP.NET выполняет два доступа (один доступ для чтения и один доступ для записи) к хранилищу данных сеанса в каждом запросе, независимо от того, использует ли запрошенная страница состояние сеанса.
Другими словами, когда вы используете опцию состояния сеанса SQL Server™, вы платите цену (два доступа к базе данных) за каждый запрос — даже за запросы страниц, которые не имеют ничего общего с состоянием сеанса. Это оказывает прямое негативное влияние на пропускную способность всего сайта.
Рис. 5. Устраните ненужный доступ к базе данных о состоянии сеанса
Итак, что же вам следует делать? Все просто: отключите состояние сеанса на страницах, которые не используют состояние сеанса. Это всегда хорошая идея, но она особенно важна, когда состояние сеанса хранится в базе данных. На рис. 5 показано, как отключить состояние сеанса. Если страница вообще не использует состояние сеанса, включите EnableSessionState="false" в директиву Page, например:
<%@ Page EnableSessionState="false" ... %>
Эта директива запрещает менеджеру состояния сеанса читать и записывать в базу данных состояния сеанса при каждом запросе. Если страница считывает данные из состояния сеанса, но не записывает данные (то есть не изменяет содержимое сеанса пользователя), установите для EnableSessionState значение ReadOnly следующим образом:
<%@ Page EnableSessionState="ReadOnly" ... %>
Наконец, если страница требует доступа для чтения/записи к состоянию сеанса, опустите свойство EnableSessionState или установите для него значение true:
<%@ Page EnableSessionState="true" ... %>
Контролируя состояние сеанса таким образом, вы гарантируете, что ASP.NET обращается к базе данных состояния сеанса только тогда, когда это действительно необходимо. Устранение ненужного доступа к базе данных — это первый шаг к созданию высокопроизводительных приложений.
Кстати, свойство EnableSessionState является общедоступным. Это свойство было задокументировано начиная с ASP.NET 1.0, но я до сих пор редко вижу, чтобы разработчики им воспользовались. Возможно, потому, что это не очень важно для модели состояния сеанса по умолчанию в памяти. Но это важно для модели SQL Server.
В начало Некэшированные роли Следующий оператор часто встречается в файле web.config приложения ASP.NET 2.0 и в примерах, представляющих диспетчер ролей ASP.NET 2.0:
<roleManager Enabled="true" />
Но, как показано выше, это утверждение оказывает существенное негативное влияние на производительность. Знаешь почему?
По умолчанию диспетчер ролей ASP.NET 2.0 не кэширует данные роли. Вместо этого он обращается к хранилищу данных ролей каждый раз, когда ему необходимо определить, к какой роли принадлежит пользователь (если таковая имеется). Это означает, что после аутентификации пользователя любые страницы, использующие данные роли (например, страницы, использующие карты сайта с включенными настройками ограничения безопасности, и страницы, доступ к которым ограничен с помощью директив URL-адресов на основе ролей в web.config), будут менеджер для запроса хранилища данных роли. Если роли хранятся в базе данных, вы можете легко обойтись без доступа к нескольким базам данных для каждого запроса. Решение состоит в том, чтобы настроить диспетчер ролей для кэширования данных роли в файлах cookie:
<roleManager Enabled="true"cacheRolesInCookie="true" />
Вы можете использовать другие атрибуты <roleManager> для управления характеристиками файла cookie роли, например, как долго файл cookie должен оставаться действительным (и, следовательно, как часто диспетчер ролей возвращается в базу данных ролей). Ролевые файлы cookie по умолчанию подписываются и шифруются, поэтому риск безопасности, хотя и не нулевой, но снижается.
В началоСериализация свойств файла конфигурации
Служба профилей ASP.NET 2.0 предоставляет готовое решение проблемы поддержания состояния каждого пользователя, такого как настройки персонализации и языковые настройки. Чтобы использовать службу профилей, вы определяете профиль XML, содержащий атрибуты, которые вы хотите сохранить от имени отдельного пользователя. Затем ASP.NET компилирует класс, содержащий те же свойства, и обеспечивает строго типизированный доступ к экземплярам класса через свойства файла конфигурации, добавленные на страницу.
Гибкость профиля настолько велика, что позволяет даже использовать пользовательские типы данных в качестве свойств профиля. Однако есть проблема, которая, как я лично видел, заставляет разработчиков совершать ошибки. На рис. 6 показан простой класс Posts и определение профиля, в котором Posts используется в качестве атрибута профиля. Однако этот класс и этот файл конфигурации приводят к неожиданному поведению во время выполнения. Можете ли вы понять, почему?
Проблема в том, что Posts содержит закрытое поле под названием _count, которое необходимо сериализовать и десериализовать, чтобы полностью заморозить и повторно заморозить экземпляр класса. Однако _count не сериализуется и не десериализуется, поскольку он является частным, а диспетчер профилей ASP.NET по умолчанию использует сериализацию XML для сериализации и десериализации пользовательских типов. Сериализатор XML игнорирует непубличные члены. Таким образом, экземпляры Posts сериализуются и десериализуются, но каждый раз, когда экземпляр класса десериализуется, _count сбрасывается в 0.
Одним из решений является сделать _count общедоступным полем вместо частного. Другое решение — инкапсулировать _count с общедоступным свойством чтения/записи. Лучшее решение — пометить сообщения как сериализуемые (с помощью атрибута SerializableAttribute) и настроить менеджер профилей на использование двоичного сериализатора .NET Framework для сериализации и десериализации экземпляров классов. Это решение сохраняет дизайн самого класса. В отличие от сериализаторов XML, бинарные сериализаторы сериализуют поля независимо от того, доступны ли они. На рис. 7 показана фиксированная версия класса Posts и выделено измененное определение сопутствующего профиля.
Следует иметь в виду одну вещь: если вы используете пользовательский тип данных в качестве свойства профиля и этот тип данных имеет закрытые элементы данных, которые необходимо сериализовать для полной сериализации экземпляра типа, используйте SerializeAs=" Binary» в свойствах объявления свойства и убедитесь, что сам тип является сериализуемым. В противном случае полная сериализация не произойдет, и вы потратите время, пытаясь определить, почему профиль не работает.
В начало Насыщение пула потоков Меня часто очень удивляет фактическое количество страниц ASP.NET, которые я вижу при выполнении запроса к базе данных и ожидании возврата результатов запроса 15 или более секунд. (Я также ждал 15 минут, прежде чем увидеть результаты моего запроса!) Иногда задержка является неизбежным следствием большого объема возвращаемых данных; в других случаях задержка связана с плохой конструкцией базы данных. Но независимо от причины, длинные запросы к базе данных или любые длинные операции ввода-вывода приведут к снижению пропускной способности в приложениях ASP.NET.
Я подробно описывал эту проблему ранее, поэтому не буду здесь вдаваться в подробности. Достаточно сказать, что ASP.NET использует ограниченный пул потоков для обработки запросов. Если все потоки заняты в ожидании завершения запроса к базе данных, вызова веб-службы или другой операции ввода-вывода, они будут освобождены при выполнении операции. Прежде чем поток будет выпущен, другие запросы должны быть поставлены в очередь и ожидать. Когда запросы ставятся в очередь, производительность резко падает. Если очередь заполнена, ASP.NET приводит к сбою последующих запросов с ошибкой HTTP 503. Это не та ситуация, которую нам хотелось бы видеть в рабочем приложении на рабочем веб-сервере.
Решение — асинхронные страницы — одна из лучших, но малоизвестных функций ASP.NET 2.0. Запрос асинхронной страницы начинается в потоке, но когда он запускает операцию ввода-вывода, он возвращается в этот поток и интерфейс IAsyncResult ASP.NET. По завершении операции запрос уведомляет ASP.NET через IAsyncResult, а ASP.NET извлекает другой поток из пула и завершает обработку запроса. Стоит отметить, что при выполнении операций ввода-вывода потоки пула потоков не заняты. Это может значительно повысить пропускную способность, предотвращая ожидание в очереди запросов для других страниц (страниц, которые не выполняют длительные операции ввода-вывода).
Вы можете прочитать все об асинхронных страницах в октябрьском выпуске журнала MSDN® Magazine за 2005 год. Любая страница, которая привязана к вводу-выводу, а не к машине и требует много времени для выполнения, имеет хорошие шансы стать асинхронной страницей.
Когда я рассказываю разработчикам об асинхронных страницах, они часто отвечают: «Это здорово, но они мне не нужны в моем приложении». На что я отвечаю: «Нужно ли каким-либо вашим страницам запрашивать базу данных?»
Проверяли ли вы счетчики производительности ASP.NET на предмет статистики по запросам в очереди и среднему времени ожидания. Несмотря нато,
что ваше приложение пока работает нормально, по мере роста размера вашего клиента нагрузка может увеличиться.
Реальных приложений ASP.NET требуются асинхронные страницы. Пожалуйста, помните об этом!
В начало Олицетворение и авторизация ACL Ниже приведена простая директива конфигурации, но у меня загораются глаза каждый раз, когда я вижу ее в web.config:
<identity impersonate="true" />
Эта директива включает олицетворение на стороне клиента в приложениях ASP.NET. Он прикрепляет токен доступа, представляющий клиента, к потоку, обрабатывающему запрос, чтобы проверки безопасности, выполняемые операционной системой, проводились по идентификатору клиента, а не по идентификатору рабочего процесса. Приложения ASP.NET редко требуют макетирования; мой опыт подсказывает мне, что разработчики часто включают макетирование по неправильным причинам. Вот почему.
Разработчики часто включают олицетворение в приложениях ASP.NET, чтобы можно было использовать разрешения файловой системы для ограничения доступа к страницам. Если у Боба нет разрешения на просмотр Salaries.aspx, разработчик включит олицетворение, чтобы запретить Бобу просматривать Salaries.aspx, установив в списке управления доступом (ACL) запрет на чтение Боба. Но есть следующая скрытая опасность: для авторизации ACL не требуется олицетворение. Когда вы включаете проверку подлинности Windows в приложении ASP.NET, ASP.NET автоматически проверяет ACL для каждой запрошенной страницы .aspx и отклоняет запросы от вызывающих сторон, у которых нет разрешения на чтение файла. Он по-прежнему ведет себя так, даже если симуляция отключена.
Иногда необходимо обосновать симуляцию. Но обычно этого можно избежать с помощью хорошего дизайна. Например, предположим, что Salaries.aspx запрашивает в базе данных информацию о зарплате, которая известна только менеджерам. С помощью олицетворения вы можете использовать разрешения базы данных, чтобы запретить неуправленческому персоналу запрашивать данные о заработной плате. Или вы можете игнорировать олицетворение и ограничить доступ к данным о заработной плате, настроив список управления доступом для Salaries.aspx, чтобы пользователи, не являющиеся администраторами, не имели доступа для чтения. Последний подход обеспечивает лучшую производительность, поскольку полностью позволяет избежать макетирования. Это также исключает ненужный доступ к базе данных. Почему запрос к базе данных запрещен только по соображениям безопасности?
Кстати, однажды я помог устранить неполадки устаревшего приложения ASP, которое периодически перезапускалось из-за неограниченного использования памяти. Неопытный разработчик преобразовал целевой оператор SELECT в SELECT *, не учтя, что запрашиваемая таблица содержит изображения, которые были большими и многочисленными. Проблема усугубляется необнаруженной утечкой памяти. (Моя область управляемого кода!) Приложение, которое работало нормально в течение многих лет, внезапно перестало работать, потому что операторы SELECT, которые раньше возвращали килобайт или два данных, теперь возвращали несколько мегабайт. Добавьте к этому проблему неадекватного контроля версий, и жизнь команды разработчиков станет «гиперактивной» — а под «гиперактивностью» это все равно, что наблюдать, как ваши дети играют в раздражающую игру, пока вы собираетесь спать ночью. Скучная футбольная игра.
Теоретически традиционные утечки памяти не могут возникнуть в приложениях ASP.NET, полностью состоящих из управляемого кода. Но недостаточное использование памяти может повлиять на производительность, заставляя сбор мусора выполняться чаще. Даже в приложениях ASP.NET будьте осторожны с SELECT *!
В начало Не полагайтесь полностью на него — настройте файл конфигурации базы данных!
Меня как консультанта часто спрашивают, почему приложения не работают должным образом. Недавно кто-то спросил мою команду, почему приложение ASP.NET выполняет только примерно 1/100 пропускной способности (запросов в секунду), необходимой для запроса документа. Проблемы, которые мы обнаружили ранее, уникальны среди проблем, которые мы видели в веб-приложениях, которые не работали должным образом, и к этим урокам мы все должны отнестись серьезно.
Мы запускаем SQL Server Profiler и отслеживаем взаимодействие между этим приложением и серверной базой данных. В более экстремальном случае всего одно нажатие кнопки привело к возникновению более 1500 ошибок в базе данных. Вы не можете создавать высокопроизводительные приложения таким образом. Хорошая архитектура всегда начинается с хорошего дизайна базы данных. Независимо от того, насколько эффективен ваш код, он не будет работать, если его отягощают плохо написанную базу данных.
Плохая архитектура доступа к данным обычно является результатом одного или нескольких из следующих:
• Плохая конструкция базы данных (обычно разработанная разработчиками, а не администраторами базы данных).
• Использование наборов данных и DataAdapters, особенно DataAdapter.update, который хорошо работает для приложений Windows Forms и других богатых клиентов, но, как правило, не идеально подходит для веб -приложений.
• Плохо разработан уровень доступа данных (DAL), который имеет плохо запрограммированные расчеты и потребляет много циклов ЦП для выполнения относительно простых операций.
Проблема должна быть выявлена, прежде чем ее можно будет лечить. Способ определения проблем доступа к данным состоит в том, чтобы запустить SQL Server Profiler или эквивалентный инструмент, чтобы увидеть, что происходит за кулисами. Настройка производительности завершается после проверки связи между приложением и базой данных. Попробуйте - вы можете быть удивлены тем, что найдете.
Вернемся к высшему выводу, теперь вы знаете некоторые проблемы и их решения, с которыми вы можете столкнуться при создании производственного приложения ASP.NET. Следующий шаг - поближе познакомиться с вашим собственным кодом и попытаться избежать некоторых проблем, которые я здесь изложил. ASP.NET, возможно, снизил барьер для входа для веб -разработчиков, но ваши приложения имеют все основания быть гибкими, стабильными и эффективными. Пожалуйста, рассмотрите это тщательно, чтобы избежать ошибок начинающих.
На рисунке 8 представлен короткий контрольный список, который вы можете использовать, чтобы избежать ловушек, описанных в этой статье. Вы можете создать аналогичный контрольный список дефектов безопасности. Например:
• Вы зашифровали разделы конфигурации, которые содержат конфиденциальные данные?
• Вы проверяете и проверяете вход, используемый в операциях базы данных, и используете ли вы, кодированный HTML, в качестве вывода?
• Содержит ли ваш виртуальный каталог файлы с незащищенными расширениями?
Эти вопросы важны, если вы цените целостность своего веб -сайта, серверов, которые его размещают, и ресурсы, на которые они полагаются.
Джефф Просис является редактором журнала MSDN и автором нескольких книг, включая программирование Microsoft .net (Microsoft Press, 2002). Он также является соучредителем компании Wintellect, программной консультации и образовательной компании.
Из июля 2006 года выпуск журнала MSDN.