vto*_*ola 6 asp.net session web-farm azure cookieless
我有一个带有一个WebRole的Azure项目有问题,但有多个实例使用无cookie会话.该应用程序不需要会话存储,因此它不使用任何会话存储提供程序,但我需要跟踪SessionID.显然,SessionID应该与WebRole实例相同,但它会突然改变,无需解释.我们使用SessionID来跟踪一些数据,因此非常重要.
为了重现这个问题:
创建云项目.
添加ASP.NET Web角色.已有的代码可以.
打开 Default.aspx
添加控件以查看当前SessionID
和按钮以进行回发
<p><%= Session.SessionID %></p>
<asp:Button ID="Button1" runat="server" Text="PostBack" onclick="Button1_Click" />
Run Code Online (Sandbox Code Playgroud)为按钮添加一个事件处理程序,它会稍微延迟响应:
protected void Button1_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(150);
}
Run Code Online (Sandbox Code Playgroud)打开 Web.Config
启用无Cookie会话:
<system.web>
<sessionState cookieless="true" />
</system.web>
Run Code Online (Sandbox Code Playgroud)运行项目,快速点击"PostBack"按钮一段时间,注意地址栏中的会话ID.什么都没发生,会话ID始终是相同的:).停下来.
打开 ServiceConfiguration.csfg
启用四个实例:
<Instances count="4" />
Run Code Online (Sandbox Code Playgroud)确保在Web.config中有一行与Visual Studio自动添加的机器密钥相关联.(在system.web的末尾).
SessionID
一段时间后的变化.为什么会这样?据我所知,如果所有机器都共享machineKey
,那么会话应该是相同的.使用cookie没有问题,问题显然是在使用无cookie会话时.
我最好的猜测是,当有多个实例,当SessionID
一个实例生成一个实例WebRole
,被拒绝并重新生成时,会发生错误.这没有意义,因为所有WebRole
的都有相同的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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
并使用它在WebConfig中更改此行:
<sessionState cookieless="true" sessionIDManagerType="WebRole1.MySessionIDManager" />
Run Code Online (Sandbox Code Playgroud)
现在你可以看到SessionID
它不会改变,无论你打多快和多久.如果取消注释这两行,您将看到ASP.NET如何创建新的sessionID,即使已经存在.
为了强制ASP.NET创建新会话,只需重定向到站点中的绝对URL:
Response.Redirect(Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, String.Empty));
Run Code Online (Sandbox Code Playgroud)
为什么这个事情发生在无cookie会话中?
我的解决方案有多可靠MySessionIDManager
?
亲切的问候.
更新:
我尝试过这种解决方法: 用户指定的机器密钥被站点级自动配置覆盖,但问题仍然存在.
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();
}
Run Code Online (Sandbox Code Playgroud)我也试过这个关于放置会话启动处理程序并添加一些数据,但没有运气.
void Session_Start(object sender, EventArgs e)
{
Session.Add("dummyObject", "dummy");
}
Run Code Online (Sandbox Code Playgroud)赏金!
简而言之,除非您使用 cookie 或会话提供程序,否则会话 ID 无法从一个 Web 角色实例传递到另一个 Web 角色实例。您提到的帖子说,如果您不使用 cookie 或会话存储,则 SessionID 不会在 Web 角色之间保持相同。
检查上一个问题,了解在 Azure 中处理状态存储的方法,例如使用表存储
machineKey 与会话或应用程序域无关,它是用于加密、解密、验证身份验证和查看状态数据的密钥。要验证这一点,请使用 Reflector 打开 SessionIDManager.CreateSessionID。您将看到 ID 值只是一个编码为字符串的随机 16 字节值。
AspCookielessSession 值已由 SessionIDManager 在 GetSessionID 方法中检查,而不是 CreateSessionID,因此在执行代码之前检查已完成。由于默认的会话状态模式是 InProc,因此单独的 Web 角色将无法验证会话密钥,因此它们会创建一个新的会话密钥。
事实上,角色随时可能迁移到不同的物理机,在这种情况下其状态将会丢失。SQL Azure 团队的这篇文章描述了一种使用 SQL Azure 存储状态的方法,正是出于这个原因。
编辑我终于让 TableStorageSessionStateProvider 在 cookieless 模式下工作!
虽然 TableStorageSessionStateProvider 通过重写SessionStateStoreProviderBase.CreateUnititializedItem确实支持 cookieless 模式,但它无法在private 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(),
}
Run Code Online (Sandbox Code Playgroud)
创建新会话时,此块返回一个空会话数据对象。不幸的是,空数据对象没有存储到 blob 存储中。
将第一行替换为以下行,使其在 blob 为空时返回空对象:
if (actions == SessionStateActions.InitializeItem || stream.Length==0)
Run Code Online (Sandbox Code Playgroud)
只要提供者支持,cookies 会话状态就可以工作。不过,您必须决定使用无 cookie 状态是否适合使用示例提供程序。也许 vtortola 应该检查AppFabric Caching CTP。它包含开箱即用的 ASP.NET 提供程序,速度更快,而且肯定比示例提供程序有更好的支持。甚至还有关于如何使用它设置会话状态的分步教程。
归档时间: |
|
查看次数: |
2686 次 |
最近记录: |