Служба распределенной блокировки с Windows Server AppFabric Caching - PullRequest
5 голосов
/ 23 февраля 2011

У меня есть метод расширения для объекта Microsoft.ApplicationServer.Caching.DataCache, найденного в Windows Server AppFabric SDK, который выглядит следующим образом:

using System;
using System.Collections.Generic;
using Microsoft.ApplicationServer.Caching;

namespace Caching
{
    public static class CacheExtensions
    {
        private static Dictionary<string, object> locks = new Dictionary<string, object>();

        public static T Fetch<T>(this DataCache @this, string key, Func<T> func)
        {
            return @this.Fetch(key, func, TimeSpan.FromSeconds(30));
        }

        public static T Fetch<T>(this DataCache @this, string key, Func<T> func, TimeSpan timeout)
        {
            var result = @this.Get(key);

            if (result == null)
            {
                lock (GetLock(key))
                {
                    result = @this.Get(key);

                    if (result == null)
                    {
                        result = func();

                        if (result != null)
                        {
                            @this.Put(key, result, timeout);
                        }
                    }
                }
            }

            return (T)result;
        }

        private static object GetLock(string key)
        {
            object @lock = null;

            if (!locks.TryGetValue(key, out @lock))
            {
                lock (locks)
                {
                    if (!locks.TryGetValue(key, out @lock))
                    {
                        @lock = new object();
                        locks.Add(key, @lock);
                    }
                }
            }

            return @lock;
        }
    }
}

Цель состоит в том, чтобы позволить разработчику написать код, который говорит: «Извлеките мне некоторые данные, сначала попробовав кеш. Если они недоступны в кеше, выполните указанную функцию, поместите результаты в кеш для следующего вызывающего, а затем верните Результаты". Как это:

var data = dataCache.Fetch("key", () => SomeLongRunningOperation());

Ограничения блокировок при выполнении потенциально долго выполняющегося вызова функции для одного потока, но только внутри одного процесса на одном компьютере. Как бы вы расширили этот шаблон, чтобы распределить блокировку, чтобы предотвратить одновременное выполнение этой функции несколькими процессами / машинами?

Ответы [ 2 ]

8 голосов
/ 24 февраля 2011

AppFabric имеет собственный механизм распределенной блокировки, доступ к которому можно получить с помощью семейства методов GetAndLock/PutAndUnlock.Если ваш элемент заблокирован, обычный вызов Get все равно будет успешным и вернет последнее значение, но дальнейшие вызовы GetAndLock вызовут исключение.В случае, когда ваш клиент запрашивает кэшированный объект в первый раз, вы все равно можете заблокировать ключ, даже если он еще не существует (это больше похоже на резервирование , чем на надежную блокировку).

public static T Fetch<T>(this DataCache @this, string key, Func<T> func, TimeSpan timeout)
{
    var result = @this.Get(key);

    if (result == null)
    (
        DataCacheLockHandle handle;
        // We need a timespan to allow func time to run
        TimeSpan funcTimespan = New TimeSpan(0,1,0);

        try
        {
            // Lock the key
            // If something goes wrong here it will unlock at the end of funcTimespan
            var result = @this.GetAndLock(key, funcTimespan, handle);

            if (result == null)
            {
                // Still no value so go and run func
                result = func();

                @this.PutAndUnlock(key, result, handle, timeout);
            }
            else
            {
                // There's a value now so we'll unlock the key and reset it's timeout
                @this.Unlock(key, handle, timeout);
            }
        }
        catch (DataCacheException ex)
        {
            if (ex.ErrorCode == DataCacheErrorCode.ObjectLocked)
            {
                // Another process has locked the key so func must be running right now
                // We'll return null to the client
                result = null;
            }
        }

        if (result == null)
        {
            return null;
        }
        else
        {
            return (T)result;
        }
    )
}
4 голосов
/ 30 июля 2013

Я искал хорошую реализацию этого и придумал свой собственный:
Распределенный замок с кэшированием AppFabric

По сути, это метод расширения AcquireLock () для DataCacheкласс, который вы можете использовать так:

DataCache cache = factory.GetCache("MyCache");
using (cache.AcquireLock("MyLock"))
{
    // Lock acquired, perform synchronized work here
}
...