RedisSessionStateProvider Elasticache死锁

时间:2015-05-06 16:36:04

标签: asp.net asp.net-mvc redis stackexchange.redis

我们希望在我们的应用和服务之间共享ASP.NET会话状态。我们选择Elasticache / redis来实现这一目标。它进展顺利,但我们遇到了僵局。

这是死锁序列:

  • 用户导航到App 1提供的页面
  • App 1使用RedisSessionStateProvider,在几毫秒内成功获取会话
  • App 1向App 2发出HttpWebRequest,附加了ASP.NET_SessionId cookie
  • App 2也使用RedisSessionStateProvider,它尝试从同一个redis实例获取Session,并在~2分钟后超时

推测App 1的RedisSessionStateProvider在包含Session的缓存项上持有(写?)锁。从我的说法中可以看出,我不是雷迪斯大师......

AFAICT Elasticache让您无法看到这样的情况,只有性能图表。并且RedisSessionStateProvider是封闭源代码,所以我不能在那里捅。

我还试图让RedisSessionStateProvider记录(通过loggingClassName参数),但App 1或App 2都没有写入(虽然我调用了Log()方法)。< / p>

为了证明它是RedisSessionStateProvider死锁(而不是我们自己的代码死锁),我将App 1切换回使用InProc会话,一切运行正常。

有没有人有任何建议?顺便说一下,我们的会话数据是所有意图和目的都是不可变的,因此确实没有必要将其锁定。

非常感谢, 皮特

编辑:sessionState配置按要求。请注意,较大的operationTimeoutInMilliseconds值是为了在调试应用程序时不会出现异常。这将在生产中改为~5000。

  <sessionState mode="Custom" customProvider="RedisSessionProvider">
    <providers>
      <add name="RedisSessionProvider"
        type="Microsoft.Web.Redis.RedisSessionStateProvider"
        host = "ec2-184-73-3-249.compute-1.amazonaws.com"
        port = "6379"
        ssl = "false"
        throwOnError = "true"
        retryTimeoutInMilliseconds = "2000"
        applicationName = "PE"
        connectionTimeoutInMilliseconds = "2000"
        operationTimeoutInMilliseconds = "1800000"    
    </providers>
  </sessionState>

1 个答案:

答案 0 :(得分:1)

  

这不是答案,但不适用于评论部分。

在页面asp.net页面执行生命周期的开始,它调用GetItemExclusive从存储中获取会话(在本例中为redis)并对该会话设置锁定,以便其他并行请求在此请求工作时无法修改会话。此锁定的超时时间相当于您可以使用web.config设置的请求超时,如下所示。

<configuration>
  <system.web>
    <httpRuntime executionTimeout="10"/>
  </system.web>
</configuration> 

现在,页面执行并根据天气在会话中修改或未修改任何内容,它调用SetAndReleaseItemExclusive或ReleaseItemExclusive来释放锁定。如果此请求由于某种原因而失败,则会根据retryTimeoutInMilliseconds值重试。如果retryTimeoutInMilliseconds与operationTimeoutInMilliseconds非常小或相同,那么它可能根本不会重试。如果SetAndReleaseItemExclusive或ReleaseItemExclusive未成功完成,那么基本上您的会话将被锁定以完成您在上面设置的“executionTimeout”时间,以秒为单位。所有其他请求将被阻止,并且在锁定时将无法访问该会话。锁定将在到期时自动释放。

使用web.config属性loggingClassName和loggingMethodName配置日志记录。升级到上面的包时,可以在web.config注释中找到更多详细信息。您基本上可以提供一个返回TextWriter的公共静态方法。会话状态提供程序和StackExchange.Redis.StrongName都将使用此TextWriter对象来记录详细信息。

这将有助于我们获得有关问题的更多详细信息。请注意,启用日志记录会降低性能。

使用日志记录的示例:

namespace SSPWebAppLatest3
{
    public static class Logger
    {
        public static TextWriter GetLogger()
        { 
            return File.CreateText("C:\\Logger.txt");
        }
    }
}

的Web.config:

<add name="MySessionStateStore" type="Microsoft.Web.Redis.RedisSessionStateProvider" host="127.0.0.1" accessKey="" ssl="false" 
             loggingClassName="Logger, SSPWebAppLatest3, Version=1.0.0.0, Culture=neutral ……."
             loggingMethodName="GetLogger"/>

请给我一个可重复的测试应用程序,我可以进一步调试它。您也可以像会话状态一样执行,输出缓存提供程序代码现在是开源的。 (https://github.com/Azure/aspnet-redis-providers