45890 (Менеджер подключений к базам данных), страница 2

2016-07-31СтудИзба

Описание файла

Документ из архива "Менеджер подключений к базам данных", который расположен в категории "". Всё это находится в предмете "информатика" из , которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "рефераты, доклады и презентации", в предмете "информатика, программирование" в общих файлах.

Онлайн просмотр документа "45890"

Текст 2 страницы из документа "45890"

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

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

Повторное использование подключений

В то время как с повторным использованием командных объектов (SqlCommand, OleDbCommand и т.п.) все, в общем, понятно, вопрос повторного использования объекта подключения остается открытым. Нужно ли это кому-нибудь, а если нужно, то зачем?

Под «повторным» мы здесь понимаем такое использование, когда один и тот же объект подключения используется снова и снова во всех частях приложения, где нужен доступ к соответствующей базе данных. При этом мы сознаем, что все стандартные для ASP.NET объекты подключения не являются безопасными для многопоточного использования (non thread safe), поэтому для начала будем считать, что наше приложение имеет только один поток.

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

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

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

Основные проблемы ясны и достаточно серьезны. Какие преимущества могут быть у данного подхода? Перевешивают ли они недостатки?

Во-первых, мы можем существенно ускорить работу в тех приложениях, где свежеоткрытое подключение нужно специально готовить. Например, приложение может использовать application roles. Для входа в роль MS SQL Server требует выполнения хранимой процедуры sp_setapprole:

EXEC sp_setapprole 'SalesApprole', 'AsDeFXX'

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

Естественно, я говорю не о простейшем случае, когда все пять обращений к базе находятся в одном методе. В конце концов, мы живем во времена победившего объектно-ориентированного подхода, так что «макаронный» код почти почил в бозе. Все эти обращения совершаются разными компонентами, обслуживающими запрос. Как быть в этом случае? Предлагается открыть подключение и выполнить эту команду в начале обработки запроса, а затем передать объект подключения в дальнейшее использование.

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

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

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

Режимы функционирования менеджера

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

На практике менеджер, осуществляющий кэширование объектов подключения, должен успешно проходить вот такой тест:

DbManager.Mode = DbManagerMode.CacheConnections;

DbManager dbmgr =DbManager.Get();

IDbConnection c1 = dbmgr["beta"];

IDbConnection c2 = dbmgr["beta"];

Assert.IsTrue( c1 == c2 ); // Менеджер возвращает один и тот же экземпляр

А «простой» менеджер, т.е. не осуществляющий кэширование, такой:

DbManager.Mode = DbManagerMode.DoNotCacheConnections;

DbManager dbmgr =DbManager.Get();

IDbConnection c1 = dbmgr["beta"];

IDbConnection c2 = dbmgr["beta"];

Assert.IsTrue( c1 != c2 ); // Менеджер возвращает разные экземпляры

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

Многопоточность

Ранее мы рассматривали приложение, в котором есть только один поток (thread). Какие сложности могут встретиться, если потоков будет несколько?

По сути, сложность здесь только одна – один объект подключения можно одновременно использовать только в одном потоке. Если для каждого обращения создается новый объект подключения, то нас эта проблема совершенно не касается. Соответственно, менеджер подключений, работающий в этом режиме, очень прост в реализации. Метод, возвращающий объект подключения по имени, может выглядеть так:

public override IDbConnection this[String name]

{

get

{

ConnectionInfo info = (ConnectionInfo)_config.Connections[name];

if( info != null )

{

return CreateConnection( info );

}

return null;

}

}

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

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

[ThreadStatic] private static DbManager _instance;

private ListDictionary _connections = new ListDictionary();

internal static new DbManager Get()

{

// Если экземпляр уже есть, вернуть его

if( _instance != null ) return _instance;

// Создать новый экземпляр

_instance = new CachingDbManager();

_instance.Init();

return _instance;

}

public override IDbConnection this[String name]

{

get

{

// Пытаемся взять готовый объект из словаря

IDbConnection result = (IDbConnection)_connections[name];

if( result == null )

{

// Ищем описание подключения в конфигурации

ConnectionInfo info = (ConnectionInfo)_config.Connections[name];

if( info != null )

result = CreateConnection( info );

}

return result;

}

}

Менеджер и ASP.NET

Как известно, приложения ASP.NET весьма активно используют многопоточность. В то же время делают они это настолько неявно, что этот факт легко оставить без внимания и получить неожиданные ошибки.

Вспомним, в общих чертах, структуру обычного приложения ASP.NET. В домене приложения (AppDomain) есть несколько экземпляров класса HttpApplication. Каждый из этих экземпляров обладает набором сопутствующих ему модулей (HttpModule). Набор модулей у каждого приложения одинаков, да и сами приложения, по идее, не должны ничем отличаться. Далее, домен приложения имеет набор рабочих потоков (working threads), готовых обслуживать пользовательские запросы. Со всей очевидностью, потоков существует по крайне мере столько же, сколько объектов Http-приложения.

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

Предположим теперь, что в нашем приложении мы создали командный объект (SqlCommand) и сохранили его для дальнейшего использования. Команда связана с определенным объектом подключения, а именно, с тем объектом, который был возвращен менеджером подключений в момент создания и первого выполнения команды. Не будем, однако, забывать, что данный объект HttpApplication при обслуживании следующего (в его хронологии) запроса, скорее всего, будет работать уже с другим рабочим потоком, а поэтому менеджер подключений вернет не то подключение, с которым связана наша команда. Хуже того, с возвращенным подключением, вероятно, будет связана аналогичная команда в другом объекте приложения.

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

Код модуля предельно прост:

public class AspAdapter : IHttpModule

{

private HttpApplication application;

private DbManager manager;

public void Init(System.Web.HttpApplication context)

{

application = context;

manager = DbManager.Get();

application.BeginRequest += new EventHandler( OnBeginRequest );

}

protected void OnBeginRequest( object sender, EventArgs e )

{

manager.Init();

}

public void Dispose()

{

application.BeginRequest -= new EventHandler( OnBeginRequest );

}

}

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

Пожалуйста, закрывайте двери!

Общеизвестно, что при использовании пула подключений (connection pool) основополагающий принцип работы с подключениями гласит: открывай поздно, закрывай рано. Иными словами, открывать нужно перед самым использованием, а закрывать сразу после оного. При этом нужно помнить, что в блоке использования подключения к базе может произойти какая-нибудь ошибка (exception), которая помешает закрыть подключение.

Свежие статьи
Популярно сейчас
А знаете ли Вы, что из года в год задания практически не меняются? Математика, преподаваемая в учебных заведениях, никак не менялась минимум 30 лет. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
5209
Авторов
на СтудИзбе
430
Средний доход
с одного платного файла
Обучение Подробнее