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

Для ресайзинга картинок вы можете использовать готовое решение – imageresizing.net. Оно выполнено в виде HTTP Module для ASP.NET и производит ресайзинг картинок "на лету". То есть во время загрузки картинки производится только ее загрузка, без трансформаций. Ресайзинг же производится тогда, когда запрашивается специфичный размер самой картинки и выполняется асинхронно, поскольку страница уже отображается у пользователя. Также используется кэш, чтобы избежать постоянной регенерации картинок.

Но так как вопрос был про "многопоточность asp.net", то поделюсь соображениями еще и по этой теме. Сразу скажу, что есть готовое решение – HangFire , но также интересны и причины его появления.

В ASP.NET каждый запрос выполняется внутри отдельного потока. Так как создание потока является достаточно "дорогой" операцией, то для каждого запроса отдается "в аренду" готовый поток из пула потоков.

В интернете ходит множество слухов, что долгоиграющие запросы ASP.NET могут "забить" все потоки из пула, и новые запросы будут нещадно попадать в request queue и ожидать своей очереди на исполнение. Такая ситуация называется Thread pool starvation (или thread pool contention) и приводит к снижению пропускной способности сервера при увеличении количества запросов.

Однако начиная с IIS 7 количество доступных потоков в ASP.NET приложении достаточно большое . Но так как ресайзинг картинки является cpu intensive задачей, то снижение пропускной способности все-таки может произойти из-за нехватки ресурсов процессора, которая усугубляется дорогими и постоянными переключениями между потоками.

В ASP.NET в общем и в ASP.NET MVC в частности есть понятие асинхронной обработки запросов. В MVC 3 был AsyncController, начиная с MVC 4 и C# 5.0 была введена поддержка async-методов . Однако в этой программной модели запрос должен ждать завершения всех асинхронных операций, и в вашем случае получится, что вы отдаете ресайз картинки на выполнение в другом потоке, и ожидаете его завершение в потоке, который исполняет запрос. В этом случае вы ничего не выигрываете, и даже проигрываете, потому что вместо одного потока на обработку запроса начинает выделяться два потока.

В ASP.NET приложении вы также можете либо создать поток самостоятельно (что плохо, потому что это дорогая операция), либо использовать некоторый пул предварительно инициированных потоков самостоятельно, либо использовать тот же самый Thread Pool (посредством Task-based API либо ThreadPool.QueueUserWorkItem) и не ожидать его завершения, при этом ресайз картинки будет происходить вне пределов контекста обработки запроса. В этом случае пользователю не нужно будет ожидать завершения долгой операции, но здесь может возникнуть следующая проблема.

Если в качестве веб-сервера выступает IIS (что скорее всего так, пока Owin не пошел в массы), то ваше приложение хостится в одном или нескольких рабочих процессах IIS, которые работают под управлением пула приложения. А пул приложения имеет множество всевозможных настроек, которые могут привести к recycling"у приложения . Это хороший процесс, который экономит ресурсы сервера, особенно на shared хостингах. Вот только у него есть таймаут на его завершение, который по-умолчанию равен 90 секундам.

Когда application pool начинает процесс ресайклинга, он отсылает ASP.NET приложению команду на остановку (shutdown). При этом само ASP.NET приложение перестает принимать новые запросы, и ждет завершения текущих с собственным дефолтным таймаутом (ShutdownTimeout в конфиге application pool) в 30 секунд. Если текущие запросы успевают выполниться за отведенное время, то сразу после этого происходит выгрузка домена приложения.

Вот тут и кроется первая проблема – ASP.NET по-умолчанию просто не обращает внимания на все ваши фоновые потоки, как созданные собственноручно, так и потоки из Thread Pool. И если после завершения обработки текущих запросов ваша фоновая задача не была завершена – это ваши проблемы, она будет просто прервана исключением ThreadAbortException. И если не были предприняты конкретные шаги по ее перезапуску, то сами потом будете объяснять пользователям, почему иногда картинки или отчеты до них так и не доходят. Конечно же, вероятность вроде бы небольшая, но запросов много, причин у recycling"а много, да и время жизни проекта тоже большое. А бороться с последствиями довольно неприятно.

Еще в ASP.NET есть интерфейс IRegisteredObject и методы HostEnvironment.RegisterObject и HosterEnvironment.UnregisterObject (а вот и ), которые уведомляют ASP.NET о том, что есть операция, завершения которой нужно ожидать при получения события об остановке приложения. Правда вот ShutdownTimeout при этом никуда не девается, а гарантировать, что все фоновые задачи будут завершены за 30 секунд очень сложно.

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

В этой ситуации 100% работающее решение – написать собственное приложение, которое будет "крутиться" постоянно и оформить его в виде службы Windows. Она будет перезапускаться гораздо реже, и таймауты можно настроить так, как душе угодно. Однако в добавок нужно еще организовать протокол общения (тут можно взять, конечно WCF), и разработать грамотную модель обработки задач. Но и первоначальная разработка, и последующее тестирование несколько сложно. А оправдать такое монстроидальное решение тем, что "нам нужна гарантия при ресайзе картинок" бывает тоже довольно трудно.

Самым обидным для меня было то, что для Ruby on Rails было полно всяких решения, которые решали подобную проблему с long-running процессами на стороне веб-сервера на общий манер: Resque , Sidekiq , delayed_job и иже с ними. Для платформы.NET есть несколько разработок на NuGet, но они пока еще слишком слабые, и иногда не дают гарантий того, что задача будет выполнена всегда после ее создания.

Подождав год-другой, я решил все-таки сделать подобный общий инструмент для.NET – HangFire . На данный момент у него версия 0.7, что означает его неполную готовность для массового использования, однако он гораздо стабильнее и функциональнее, чем большинство собственных разработок. Плюс, он сейчас находится в активной разработке. Схема и публичное API довольно стабильно, и оно уже используется в нашей системе, которая находится в продакшене. Так что можете попробовать, а если возникнут вопросы – то смело создавайте тикет на гитхабе.

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

АККОМПАНИРОВАТЬ 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-струнной гитаре. От романсов к року и джазу , Манилов В.. В книге вы найдете необходимые сведения об аккордах, их последовательностях и способах усложнения партии ритм-гитары, о стандартной аппликатуре и типовых ритмических моделях различных жанров…

Я хочу, чтобы мой сайт мог размещаться в облаке в будущем, а также чтобы он мог обрабатывать множество запросов.

Насколько безопасны статические переменные?

Являются ли они небезопасными, потому что отдельные запросы отдельных пользователей фактически используют эти статические переменные? Или это потому, что если вы распространяете сайт поверх потоков/осколков или подобных, (для обработки больших нагрузок) потоки разделяют статические переменные?

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

E.G Вот пример того, что я делаю:

Using System; using System.Collections.Generic; using System.Linq; using System.Web; using Mvc.Mailer; namespace MVCWebsite.Helpers { public class AppSettings { public static void OnAppInit() { //General AppName = "MyApp"; DesktopBaseURLs = new Dictionary(); DesktopBaseURLs.Add("dev", "localhost:50560"); DesktopBaseURLs.Add("test", "www.test.whatever.com"); DesktopBaseURLs.Add("live", "www.whatever.com"); MobileBaseURLs = new Dictionary(); MobileBaseURLs.Add("dev", "m.local.whatever.com"); MobileBaseURLs.Add("test", "m.test.whatever.com"); MobileBaseURLs.Add("live", "m.whatever.com"); //Emails EmailHostName = AppName + ".com"; //For the moment atleast NoReplyEmailAddress = "no-reply@" + EmailHostName.ToLower(); SupportEmailAddress = "support@" + EmailHostName.ToLower(); ErrorEmailAddress = "errors@" + EmailHostName.ToLower(); //Resources TempFileURL = "/content/temp/"; UserDataURL = "/content/user-content/"; ProfilePicturesURL = UserDataURL + "profile-pictures/"; var a = GlobalHelper.GetURLAsServerPath(ProfilePicturesURL); var b = a; } //General public static string AppName { get; set; } public static Dictionary DesktopBaseURLs; public static Dictionary MobileBaseURLs; //Emails public static string EmailHostName { get; set; } public static string NoReplyEmailAddress { get; set; } public static string SupportEmailAddress { get; set; } public static string ErrorEmailAddress { get; set; } //Resources public static string UserDataURL { get; set; } public static string TempFileURL { get; set; } public static string ProfilePicturesURL { get; set; } //Methods public static void SetAppURL() { } } }

2 ответов

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

Говорят, что статические переменные и методы, как правило, являются плохими идеями в приложениях, это неверно. Это плохая идея, если вы не знаете, как правильно их использовать, или если вы не знаете, как синхронизировать доступ к ним, если вам нужно, чтобы это выполнялось из параллельных потоков. Написание потокобезопасного кода - очень сложный вопрос, и если у вас есть опасения ошибиться (кто не пишет многопоточные приложения, такие как приложение ASP.NET?) Просто не разделяет это состояние. Используйте хорошо установленные места для этого в приложении ASP.NET:

  • Backend (может быть, например, реляционная база данных)
  • Состояние приложения
  • Контекстное состояние Http
  • Состояние сеанса
  • Файлы cookie клиента

Эти места специально предназначены для хранения состояния в приложении ASP.NET(кроме первого, конечно, который может быть использован в любом типе приложения).

Об опасностях выполнения фоновых задач в 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: вполне приличная документация, достаточное количество примеров, чтобы быстро начать использовать, а также расширяемый функционал, который можно добавлять по мере возрастания потребностей.

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