6

我对一个具有一个 WebRole 但多个使用无 cookie 会话的实例的 Azure 项目有疑问。该应用程序不需要会话存储,因此它没有使用任何会话存储提供程序,但我需要跟踪 SessionID。显然,SessionID 在 WebRole 实例中应该是相同的,但它会突然改变而没有解释。我们正在使用 SessionID 来跟踪一些数据,所以它非常重要。

为了重现该问题:

  1. 创建一个云项目

  2. 添加ASP.NET Web 角色。已经在里面的代码就可以了。

  3. 打开Default.aspx

  4. 添加一个控件以查看当前SessionID和一个按钮以导致回发

            <p><%= Session.SessionID %></p>
            <asp:Button ID="Button1" runat="server" Text="PostBack" onclick="Button1_Click" />
    
  5. 为按钮添加一个事件处理程序,这将稍微延迟响应:

    protected void Button1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(150);
    }
    
  6. 打开Web.Config

  7. 启用无 cookie 会话:

    <system.web>
            <sessionState cookieless="true" />
    </system.web>
    
  8. 运行该项目,然后快速反复点击“PostBack”按钮一段时间,注意地址栏中的会话 ID。没有任何反应,会话 ID 始终相同:)。停下来。

  9. 打开ServiceConfiguration.csfg

  10. 启用四个实例:

    <Instances count="4" />
    
  11. 确保在 Web.config 中有一行与 Visual Studio 自动添加的机器密钥相关。(在 system.web 的末尾)。

  12. 重新运行项目,快速并重复点击“回发”按钮一段时间,并注意地址栏中的会话 ID。SessionID一段时间后你会看到变化如何。

为什么会这样?据我所知,如果所有机器共享machineKey,它们之间的会话应该是相同的。使用 cookie 没有问题,问题显然只是在使用无 cookie 会话时。

我最好的猜测是,当有几个实例时发生了错误,当一个SessionID生成的实例WebRole转到另一个实例时,被拒绝并重新生成。这没有意义,因为所有WebRoles 都具有相同的machineKey.

为了找出问题并更清楚地看到它,我创建了自己的SessionIDManager

public class MySessionIDManager : SessionIDManager
{
    public override string CreateSessionID(HttpContext context)
    {
        if (context.Items.Contains("AspCookielessSession"))
        {
            String formerSessionID = context.Items["AspCookielessSession"].ToString();

           // if (!String.IsNullOrWhiteSpace(formerSessionID) && formerSessionID != base.CreateSessionID(context))
               // Debugger.Break();

            return formerSessionID;
        }
        else
        {
            return base.CreateSessionID(context);
        }
    }
}

要使用它,请在 WebConfig 中更改这一行:

    <sessionState cookieless="true" sessionIDManagerType="WebRole1.MySessionIDManager" />

现在您可以看到,SessionID无论您击打多快和多长时间,它都不会改变。如果取消注释这两行,您将看到 ASP.NET 如何创建一个新的 sessionID,即使已经有一个。

为了强制 ASP.NET 创建新会话,只需重定向到站点中的绝对 URL:

 Response.Redirect(Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, String.Empty));

为什么无 cookie 会话会发生这种情况?

我的解决方案有多可靠MySessionIDManager

亲切的问候。

更新

  • 我已经尝试过这种解决方法: User-Specified Machine Keys Overwritten by Site-Level Auto Configuration,但问题仍然存在。

    public override bool OnStart()
    {
        // For information on handling configuration changes
        // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
    
        using (var server = new ServerManager())
        {
            try
            {
                // get the site's web configuration
                var siteNameFromServiceModel = "Web"; // update this site name for your site. 
                var siteName =
                    string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
                var siteConfig = server.Sites[siteName].GetWebConfiguration();
    
                // get the appSettings section
                var appSettings = siteConfig.GetSection("appSettings").GetCollection()
                    .ToDictionary(e => (string)e["key"], e => (string)e["value"]);
    
                // reconfigure the machine key
                var machineKeySection = siteConfig.GetSection("system.web/machineKey");
                machineKeySection.SetAttributeValue("validationKey", appSettings["validationKey"]);
                machineKeySection.SetAttributeValue("validation", appSettings["validation"]);
                machineKeySection.SetAttributeValue("decryptionKey", appSettings["decryptionKey"]);
                machineKeySection.SetAttributeValue("decryption", appSettings["decryption"]);
    
                server.CommitChanges();
                _init = true;
            }
            catch
            {
            }
        }
        return base.OnStart();
    }
    
  • 我也试过这个关于放置一个会话启动处理程序并添加一些数据,但没有运气。

    void Session_Start(object sender, EventArgs e)
    {
        Session.Add("dummyObject", "dummy");
    }
    

赏金!

4

3 回答 3

3

简而言之,除非您使用 cookie 或会话提供程序,否则会话 ID 无法从一个 Web 角色实例传递到另一个。您提到的帖子说,如果您不使用 cookie 或会话存储,则 SessionID 不会在 Web 角色之间保持不变。
检查上一个问题以了解在 Azure 中处理状态存储的方法,例如使用表存储

machineKey 与会话或应用程序域无关,它是用于加密、解密、验证身份验证和视图状态数据的密钥。使用反射器验证此打开的 SessionIDManager.CreateSessionID。您将看到 ID 值只是编码为字符串的随机 16 字节值。

AspCookielessSession 值已由 SessionIDManager 在 GetSessionID 方法中检查,而不是 CreateSessionID,因此在您的代码执行之前检查已经完成。由于默认会话状态模式是 InProc,因此单独的 Web 角色将无法验证会话密钥,因此他们创建了一个新的。

事实上,一个角色可能随时迁移到不同的物理机器,在这种情况下,它的状态将会丢失。SQL Azure 团队的这篇文章描述了一种使用 SQL Azure 存储状态的方法,正是出于这个原因。

编辑我终于让 TableStorageSessionStateProvider 在无 cookie 模式下工作!

虽然 TableStorageSessionStateProvider 通过覆盖SessionStateStoreProviderBase.CreateUnititializedItem确实支持无 cookie 模式,但它无法在私有 SessionStateStoreData GetSession(HttpContext context, string id, out bool locked, out TimeSpan lockAge,out object lockId, out SessionStateActions actions,bool Exclusive) 中正确处理空会话。如果在底层 blob 存储中未找到数据,则解决方案是返回一个空的 SessionStateStoreData。

该方法有 145 行长,所以我不会在这里粘贴。搜索以下代码块

if (actions == SessionStateActions.InitializeItem) 
{
     // Return an empty SessionStateStoreData                    
     result = new SessionStateStoreData(new SessionStateItemCollection(),
}

创建新会话时,此块返回一个空的会话数据对象。不幸的是,空数据对象没有存储到 blob 存储中。

将第一行替换为以下行,使其在 blob 为空时返回一个空对象:

if (actions == SessionStateActions.InitializeItem || stream.Length==0)

只要提供者支持,长篇短小cookieles 会话状态就可以工作。不过,您必须决定使用无 cookie 状态是否证明使用示例提供程序是合理的。也许 vtortola 应该检查AppFabric Caching CTP。它包括开箱即用的 ASP.NET 提供程序,速度要快得多,而且它肯定比示例提供程序提供更好的支持。甚至还有一个关于如何设置会话状态的分步教程。

于 2011-03-02T13:54:49.247 回答
1

听起来很棘手。

我有一个建议/问题给你。不知道它是否会有所帮助——但你听起来好像已经准备好尝试任何事情了!

听起来新机器上的会话管理器可能正在检查中央会话存储提供程序,当它发现会话存储为空时,它会发出一个新的会话密钥。

我认为一个解决方案可能来自: - 使用上面的 Session_Start 以便将某些内容插入到会话存储中 - 加上在 web.config 中插入一些描述的持久会话存储提供程序 - 例如,一些最古老的 Azure 示例提供了一个表基于提供程序,或一些较新的示例提供 AppFabric 缓存解决方案。

我知道你的设计没有使用会话存储,但也许你需要放入一些东西(有点像你的 Session_Start),另外你需要定义一些除了进程内会话管理之外的东西。

或者,您需要围绕 ASP.NET 会话以外的其他内容重新设计您的应用程序。

希望有帮助 - 祝你好运!

于 2011-02-28T19:25:10.623 回答
1

我遇到了同样的问题,经过大量研究和调试后,我发现问题的发生是因为 Azure SDK 中的“虚拟服务器”将网站映射到 IIS 元数据库中的不同路径。(您可以通过 Request.ServerVariables["APPL_MD_PATH"] 看到这一点。)

我现在才发现这一点,但想发布它,以便人们可以开始对其进行测试。我的理论是这个问题一旦发布到 Azure 就可能会消失。我会更新我找到的任何结果。

于 2011-03-10T18:10:32.270 回答