Нет подключения для обслуживания этой операции: при использовании кэша Redis для Azure

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

Это трассировка стека

[RedisConnectionException: нет подключения для обслуживания этой операции: GET UserProfileInformation|[email protected]] StackExchange.Redis.ConnectionMultiplexer.ExecuteSyncImpl (сообщение сообщения, процессор ResultProcessor1 processor, ServerEndPoint server) in c:\TeamCity\buildAgent\work\3ae0647004edff78\StackExchange.Redis\StackExchange\Redis\ConnectionMultiplexer.cs:1922 StackExchange.Redis.RedisBase.ExecuteSync(Message message, ResultProcessor1, сервер ServerEndPoint) в c: \ TeamCity \ workAgent \ buildAgent 3ae0647004edff78 \ StackExchange.Redis \ StackExchange \ Redis \ RedisBase.cs: 80 StackExchange.Redis.RedisDatabase.StringGet (ключ RedisKey, флаги CommandFlags) в c: \ TeamCity \ buildAgent \ work \ 3ae0647004edffExchange \ StackExchange. .cs: ​​1431 xx.Utils.SampleStackExchangeRedisExtensions.Get (кэш базы данных, строковый ключ) в C: \ Proyectos \ xx \ xx \ Utils \ SampleStackExchangeRedisExtensions.cs: 20
xx.Cache.UserProfile.GetUserProfile (имя идентификатора строки) в C: \ Proyectos \ xx \ xx \ Cache \ UserProfile.cs: 22
x.Controllers.UserProfileController.GetPropertiesForUser () в C: \ Proyectos \ xx \ xx \ Controllers \ UserProfileController.cs: 16
lambda_method ( Закрытие, Co ntrollerBase, Object []) +61
System.Web.Mvc.ActionMethodDispatcher.Execute (контроллер ControllerBase, параметры Object []) +14

И это код

   public static Models.UserProfile GetUserProfile(string identityname)
        {
            /// It needs to be cached for every user because every user can have different modules enabled.

            var cachekeyname = "UserProfileInformation|" + identityname;
            IDatabase cache = CacheConnectionHelper.Connection.GetDatabase();
            Models.UserProfile userProfile = new Models.UserProfile();
            object obj = cache.Get(cachekeyname);
            string userProfileString;
            if (obj != null)
            {
                //get string from cache
                userProfileString = obj.ToString();

                //conver string to our object
                userProfile = JsonConvert.DeserializeObject<Models.UserProfile>(userProfileString);
                return userProfile;
            }
            else
            {
                #region Get User Profile from AD
                Uri serviceRoot = new Uri(SettingsHelper.AzureAdGraphApiEndPoint);
                var token = AppToken.GetAppToken();

                ActiveDirectoryClient adClient = new ActiveDirectoryClient(
                 serviceRoot,
                 async () => await AppToken.GetAppTokenAsync());

                string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

                Microsoft.Azure.ActiveDirectory.GraphClient.Application app = (Microsoft.Azure.ActiveDirectory.GraphClient.Application)adClient.Applications.Where(
                    a => a.AppId == SettingsHelper.ClientId).ExecuteSingleAsync().Result;
                if (app == null)
                {
                    throw new ApplicationException("Unable to get a reference to application in Azure AD.");
                }

                string requestUrl = string.Format("https://graph.windows.net/{0}/users/{1}?api-version=1.5", SettingsHelper.Tenant, identityname);
                HttpClient hc = new HttpClient();
                hc.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
                HttpResponseMessage hrm = hc.GetAsync(new Uri(requestUrl)).Result;

                if (hrm.IsSuccessStatusCode)
                {
                    Models.UserProfile currentUserProfile = JsonConvert.DeserializeObject<Models.UserProfile>(hrm.Content.ReadAsStringAsync().Result);

                    //convert object to json string
                    userProfileString = JsonConvert.SerializeObject(currentUserProfile);

                    cache.Set(cachekeyname, userProfileString, TimeSpan.FromMinutes(SettingsHelper.CacheUserProfileMinutes));
                    return currentUserProfile;
                }
                else
                {
                    return null;
                }
                #endregion
            }
        }




public static class SampleStackExchangeRedisExtensions
    {
        public static T Get<T>(this IDatabase cache, string key)
        {
            return Deserialize<T>(cache.StringGet(key));
        }

        public static object Get(this IDatabase cache, string key)
        {
            return Deserialize<object>(cache.StringGet(key));
        }

        public static void Set(this IDatabase cache, string key, object value, TimeSpan expiration)
        {
            cache.StringSet(key, Serialize(value), expiration);
        }

        static byte[] Serialize(object o)
        {
            if (o == null)
            {
                return null;
            }
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            using (MemoryStream memoryStream = new MemoryStream())
            {
                binaryFormatter.Serialize(memoryStream, o);
                byte[] objectDataAsStream = memoryStream.ToArray();
                return objectDataAsStream;
            }
        }

        static T Deserialize<T>(byte[] stream)
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            if (stream == null)
                return default(T);

            using (MemoryStream memoryStream = new MemoryStream(stream))
            {
                T result = (T)binaryFormatter.Deserialize(memoryStream);
                return result;
            }
        }

Вопросы: 1. Как я могу контролировать исключение подключения, подобное показанному, чтобы пользователь не получил сообщение об ошибке и вместо этого перешел в БД, если redis недоступен? 2. Есть ли способ повторить попытку обработки временных сбоев для кэша azure redis?


person Luis Valencia    schedule 09.07.2015    source источник


Ответы (5)


Я считаю, что это временные ошибки. Я видел многие из них в журналах своих приложений, прежде чем реализовал простую логику повторных попыток. Еще у меня было довольно много тайм-аутов. Очень простая логика повтора, плюс добавление syncTimeout=3000 в строку подключения redis решила все эти проблемы для меня.

public object Get(string key)
{
    return Deserialize(Cache.StringGet(key));
}

public object GetWithRetry(string key, int wait, int retryCount)
{
    int i = 0;
    do
    {
        try
        {
            return Get(key);
        }
        catch (Exception)
        {
            if (i < retryCount + 1)
            {
                Thread.Sleep(wait);
                i++;
            }
            else throw;
        }
    }
    while (i < retryCount + 1);
    return null;
}
person rouen    schedule 10.07.2015

Также клиент Stack Exchange имеет встроенную логику повторных попыток, при которой клиент будет повторять попытку сам. Вот еще немного информации о параметрах конфигурации. https://azure.microsoft.com/en-us/documentation/articles/cache-faq/#what-do-the-stackexchangeredis-configuration-options-do

person pranav rastogi    schedule 22.07.2015
comment
Эта логика повтора, похоже, связана с установкой соединения, а не с выполнением запросов на получение / установку. - person Răzvan Flavius Panda; 01.09.2015

Я использую Polly в моем репозитории кэша, чтобы повторить все операции с этим исключением. Я попробовал метод Retry от Polly, но это неправильное решение, и теперь я использую WaitAndRetry. С помощью этого метода вы можете повторить операцию с некоторым временем ожидания - с помощью этого вы можете поставить операцию в очередь Redis.

person Bartosz Czerwonka    schedule 18.09.2015

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

        var value = await Policy 
            .Handle<RedisConnectionException>() // Possible network issue 
            .WaitAndRetryAsync(3, i => TimeSpan.FromSeconds(3)) // retry 3 times, with a 3 second delay, before giving up
            .ExecuteAsync(async () => {
                return await cache.StringGetAsync(key);
            });
person Elliott    schedule 02.04.2018

Обновите пакет Redis Nuget до последней версии, и он должен решить вашу проблему, как и мою!

person KillerCoder    schedule 04.04.2016
comment
Именно так - сначала надо попробовать :) - person Neo; 11.10.2020