Распределенный критический раздел в веб-ферме - PullRequest
3 голосов
/ 18 марта 2011

У меня около 50 веб-сайтов с балансировкой нагрузки на 5 веб-серверах. Все они используют Enterprise Library Caching и имеют доступ к одной и той же базе данных Caching. Элементы в базе данных Caching обновляются каждые несколько часов с использованием реализации ICacheItemRefreshAction.

Я хочу гарантировать, что только один веб-сайт обновляет кеш, поместив код обновления в критическую секцию .

  • Если бы веб-сайты работали в одном пуле приложений на одном сервере, я мог бы использовать lock ()

  • Если бы веб-сайты работали в отдельных пулах приложений на одном сервере, я мог бы использовать Mutex .

Однако это не обеспечит критическую секцию между несколькими веб-серверами.

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

public class TakeLongTimeToRefresh : ICacheItemRefreshAction
{
    #region ICacheItemRefreshAction Members

    public void Refresh(string removedKey, object expiredValue, CacheItemRemovedReason removalReason)
    {
        string lockKey = "lockKey";
        ICacheManager cm = CacheFactory.GetCacheManager();

        if (!cm.Contains(lockKey))
        {
            Debug.WriteLine("Entering critical section");
            // Add a lock-key which will never expire for synchronisation.
            // I can see a small window of opportunity for another process to enter
            // the critical section here...
            cm.Add(lockKey, lockKey, 
                   CacheItemPriority.NotRemovable, null, 
                   new NeverExpired());

            object newValue = SomeLengthyWebserviceCall();
            cm.Remove(removedKey);
            Utilities.AddToCache(removedKey, newValue);

            cm.Remove("lockkey");
        }
    }
}

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

РЕДАКТИРОВАТЬ Я должен добавить, что я не могу использовать общий файл, так как политики развертывания будут препятствовать этому.

Ссылки StackOverflow:

Ответы [ 2 ]

4 голосов
/ 18 марта 2011

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

set transaction isolation serializable;
update t set lock = 1 where lock = 0;

проверки строк, на которые влияют, и, если ее 1, у вас есть блокировка, снимите ее, обновив блокировку до0. Это по сути встраивается в блокировку строк в SQLServer: если два запускаются одновременно, только одна из них получит блокировку U после блокировки S, другая заблокирует и впоследствии вернет 0 затронутых строк (поскольку первая транзакция перевернула его на 1).

1 голос
/ 18 марта 2011

Я предлагаю вам переместить логику для создания / возврата дескриптора блокировки в базу данных и объединить их, и это гарантирует, что блокировка всегда будет у одного процесса.

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

...