Пул соединений с базой данных с многопоточным сервисом - PullRequest
9 голосов
/ 06 января 2012

У меня есть служба .NET 4 C #, которая использует библиотеки TPL для многопоточности. Недавно мы перешли на использование пула подключений, поскольку одно соединение становилось узким местом для обработки.

Ранее мы использовали предложение блокировки для управления безопасностью потока на объекте соединения. Поскольку работа будет резервироваться, очередь будет существовать в виде задач, и многие предложения (задачи) будут ожидать в предложении блокировки. Теперь в большинстве сценариев потоки ожидают ввода-вывода базы данных и работают НАМНОГО быстрее.

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

Все мои IDisposables находятся в пределах использования операторов, и я правильно управляю своими соединениями. Этот сценарий происходит из-за того, что запрашивается больше работы, чем может обработать пул (что ожидается). Я понимаю, почему выбрасывается это исключение, и знаю способы его обработки. Простая повторная попытка выглядит как хак. Я также понимаю, что могу увеличить время ожидания через строку подключения, однако это не похоже на надежное решение. В предыдущем проекте (без объединения в пул) рабочие элементы обрабатывались из-за блокировки в приложении.

Каков хороший способ обработки этого сценария для обеспечения обработки всей работы?

Ответы [ 3 ]

11 голосов
/ 06 января 2012

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

Что-то вроде этого должно сделать:

//Assuming mySemaphore is a semaphore instance, e.g. 
// public static Semaphore mySemaphore = new Semaphore(100,100);
try {
  mySemaphore.WaitOne(); // This will block until a slot is available.
  DosomeDatabaseLogic();
} finally {
  mySemaphore.Release();
}
2 голосов
/ 06 января 2012

Вы можете контролировать степень параллелизма, используя метод Parallel.ForEach() следующим образом:

var items = ; // your collection of work items
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 100 };
Parallel.ForEach(items, parallelOptions, ProcessItem)

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

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

0 голосов
/ 06 января 2012

Самое простое решение - увеличить время ожидания соединения до того времени, когда вы готовы заблокировать запрос перед возвратом ошибки. Должно быть какое-то «слишком длинное» время.

Это эффективно использует пул соединений в качестве рабочей очереди с таймаутом. Это намного проще, чем пытаться реализовать его самостоятельно. Вы должны проверить, что пул соединений честен (FIFO).

...