14

我有以下代码用于从缓存中获取信息。我不知道我的应用程序是否打开了太多的连接,或者只是这个错误是由于 azure redis 缓存上的暂时故障造成的。

这是堆栈跟踪

[RedisConnectionException:没有可用于服务此操作的连接:GET UserProfileInformation|globaladmin@xx.onmicrosoft.com] StackExchange.Redis.ConnectionMultiplexer.ExecuteSyncImpl(消息消息,ResultProcessor 1 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\buildAgent\work \3ae0647004edff78\StackExchange.Redis\StackExchange\Redis\RedisBase.cs:80 StackExchange.Redis.RedisDatabase.StringGet(RedisKey key, CommandFlags flags) 在 c:\TeamCity\buildAgent\work\3ae0647004edff78\StackExchange.Redis\StackExchange\Redis\ RedisDatabase.cs:1431 xx.Utils.SampleStackExchangeRedisExtensions.Get(IDatabase cache, String key) 在 C:\Proyectos\xx\xx\Utils\SampleStackExchangeRedisExtensions.cs:20
xx.Cache.UserProfile.GetUserProfile(String identityname) 在 C:\Proyectos\xx\xx\Cache\UserProfile.cs:22
x.Controllers.UserProfileController.GetPropertiesForUser() 在 C:\Proyectos\xx\xx\Controllers\UserProfileController .cs:16
lambda_method(Closure, ControllerBase, 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 缓存的瞬态故障处理?

4

7 回答 7

10

我相信这些是暂时的错误。在我实现简单的重试逻辑之前,我已经在我的应用程序日志中看到了其中的许多内容。我也有很多超时。非常简单的重试逻辑,加上添加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;
}
于 2015-07-10T12:55:12.100 回答
3

正如鲁昂所说,这些可能是暂时的连接错误。这是一个使用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);
            });
于 2018-04-02T16:03:58.960 回答
2

Stack Exchange 客户端还具有内置的重试逻辑,客户端将自行重试。以下是有关配置选项的更多信息。 https://azure.microsoft.com/en-us/documentation/articles/cache-faq/#what-do-the-stackexchangeredis-configuration-options-do

于 2015-07-22T00:16:37.557 回答
2

我在我的缓存存储库中使用Polly来重试所有带有此异常的操作。我尝试了 Polly 的 Retry 方法,但这个错误的决定,现在我正在使用 WaitAndRetry。使用此方法,您可以在一些睡眠时间重试操作 - 使用此方法,您可以将操作排队到 Redis

于 2015-09-18T13:20:34.500 回答
1

将您的 redis nuget 包更新到最新版本,它应该可以解决您的问题,就像我的问题一样!

于 2016-04-04T14:37:01.770 回答
0

如果您使用的是 Azure 的 redis 缓存,请检查您是否在基本层上。如果是这样,您将在 Microsoft 更新您的服务器时收到此消息(我发现每隔几周就会发生这种情况)。

我的确切错误消息示例:

没有活动/可用于服务此操作的连接:GET 43da9f64-da42-4281-845b-82a7d2b7f400#Settings; 无法连接到 redis 服务器。立即连接错误。要允许此多路复用器继续重试直到能够连接,请在连接字符串中使用 abortConnect=false 或 AbortOnConnectFail=false; 在你的代码中。ConnectTimeout, mc: 1/1/0, mgr: 10 of 10 available, clientName: RDA04A5E790CA0, IOCP: (Busy=1,Free=999,Min=2,Max=1000), WORKER: (Busy=0,Free= 32767,Min=2,Max=32767), v: 2.2.4.27433 无法连接到 redis 服务器。立即连接错误。要允许此多路复用器继续重试直到能够连接,请在连接字符串中使用 abortConnect=false 或 AbortOnConnectFail=false; 在你的代码中。连接超时

下面的屏幕截图是在 Azure 中运行自动诊断后显示的,是确凿的证据。一些推荐亮点:

  • 升级到标准层或更高层以解决问题。
  • 实施重试策略以隐藏问题。
  • 设置更新计划以减轻问题损害。

微软关于redis维护停机的建议

于 2021-12-07T21:25:38.317 回答
0

使用 Azure Cache for Redis 在 Azure 上托管 - 我为修复此错误所做的唯一更改是在 Azure 提供的连接字符串的末尾添加,sslprotocols=tls12

我还更新到 StackExchange.Redis 2.2.88 的最新版本,但我不确定这是否与此相关。(可能不是)

在这篇 GitHub 帖子中找到了答案。

于 2021-12-11T15:01:51.913 回答