该不该放弃 AppFabric Caching : Session
我所在的上个公司用 Velocity 来缓存数据, 对程序的效率起了很大的帮助,所以我在上个项目里尝试使用了 AppFabric 1.1 Caching (Window Server AppFabric),但没有用它来存 Session.
当前项目,因为要从外部接口(非内部WCF服务)取数据,而且速度慢,所以,我把取回的数据通过 Session 缓存到 AppFabric Caching 里, 只在几个关键点去重新请求。
但是AppFabric 1.1 有几个问题:
内存泄露,莫名其妙的ERRCA0012等
其中 ERRCA0012 我没有找到答案。
内存泄露我搜了MSDN,说要安装补丁包:KB983182 KB2527387
但是这两个安装包并没有对外开放,要想得这两个包,还要在线提申请,我填到最后一步,发现要:
技术支持服务包, 专业级(Professional)和其他服务 (需要访问ID)
软件保障许可证: 仅适用于服务器 (需要软件保障访问 ID)
看一下支持合同的价格:
http://60.195.251.18:82/ShoppingCart.aspx
最便宜的,单次价格:150,我当然不会出这个钱,公司也不会无缘无故的出这个钱。
内存泄露表现如下:
用 Get-CacheStatistics 命令查看的所有数据大小不超过30M,但是 DistributeCacheService.exe 却要占到 700M甚至更高内存(测试环境)。用 Invoke-CacheGC 命令,效果甚微。(之前没有将 Session 保存到 AppFabric 的时候,并没有发现有内存泄露)
将 Session 存到 AppFabric 的配置如下:
<sessionState sessionIDManagerType="XXX.Frameworks.Configurations.SessionIDManager, XXX.Frameworks.Configurations" mode="Custom" customProvider="AppFabricCacheSessionStoreProvider" timeout="30">
<providers>
<add name="AppFabricCacheSessionStoreProvider"
type="Microsoft.ApplicationServer.Caching.DataCacheSessionStoreProvider, Microsoft.ApplicationServer.Caching.Client, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
cacheName="default"
sharedId="OnlineSiteSession"
/>
</providers>
</sessionState>
其中 timeout 被设为 30 分钟,是为了满足业务需要,因为要录入的数据很多,还要跳转到其它业务系统里走一圈。
其中 SessionIDManager 是为了解决二级域名共享 Session 而设的, 下文我会贴出供参考。
其中,default 是说把 session 都保存到 default 这个 cache 里。
在用 Get-Cache 看一下,default 下有大量的:Default_Region_XXX(Primary) 的 Region, 我不知道是不是因为把 session 保存到其它 cache 下就不会有这个问题,没有尝试。在用 Get-CacheStatistics 查看一下 default 的大小,size: 0, Item Count: 0。
在看一下 Get-CacheConfig
CacheName : default
TimeToLive : 10 mins
CacheType : Partitioned
Secondaries : 0
MinSecondaries : 0
IsExpirable : True
EvictionType : LRU
NotificationsEnabled : False
WriteBehindEnabled : False
WriteBehindInterval : 300
WriteBehindRetryInterval : 60
WriteBehindRetryCount : -1
ReadThroughEnabled : False
ProviderType :
ProviderSettings : {}
完全默认 LRU, 10 分钟 , 可过期
在看一下 Client 的设置:
<hosts>
<host name="172.18.21.23" cachePort="22233"/>
</hosts>
<securityProperties mode="None" protectionLevel="None" />
<!--<transportProperties connectionBufferSize="131072" maxBufferPoolSize="268435456"
maxBufferSize="8388608" maxOutputDelay="2" channelInitializationTimeout="60000"
receiveTimeout="600000"/>-->
对,只有一台缓存主机,因为是测试环境。生产环境也只有一台16G的 CacheHost。
但是:最小缓存集群推荐3个缓存主机,缓存主机的功能要单一:推荐只放缓存服务,不放其它服务。
但是:所有的我都不能解决,我也说不上话,我只是一个开发人员。加一台服务器是要成本的。
这个问题出现很久了,特别是 ERRCA0012 等莫名的错误,它们让我饱受同事鄙夷、质疑、不信任的眼光。你们说是我错信了MS的威力还是神马??
我期待 AppFabric Caching 方面有经验的大神的帮助。
为了不久的正式上线,我提出将 Session 转移到 SqlServer 里,这是在回避问题,但是也是无奈的解决方法(我们的多个业务系统需要共享 Session)
具体步骤:
1, 生成数据库:
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC>ASPNET_REGSQL.EXE –ssadd -U sa –S XXX\TEST -sstype c -d Session
会提示你输入密码,是 sa 的密码。
2, 配置 web.config
<sessionState mode="SQLServer" allowCustomSqlDatabase="true" sqlConnectionString="data source=XXX\TEST;Initial Catalog=Session;user id=sa;password=xxx" sessionIDManagerType="XXX.Frameworks.Configurations.SessionIDManager,XXX.Frameworks.Configurations" timeout="30" />
换用之后,效果明显,到目前为止以经1个小时了,没有在出现ERRCA0012 这个错误。
上文中的 SessionIDManager 代码,仅供参考:
public class SessionIDManager : ISessionIDManager {
private SessionStateSection pConfig = null;
public void Initialize() {
if(pConfig == null) {
var cfg = WebConfigurationManager.OpenWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
pConfig = (SessionStateSection)cfg.GetSection("system.web/sessionState");
}
}
public bool InitializeRequest(HttpContext context ,
bool suppressAutoDetectRedirect ,
out bool supportSessionIDReissue) {
if(pConfig.Cookieless == HttpCookieMode.UseCookies) {
supportSessionIDReissue = false;
return false;
} else {
supportSessionIDReissue = true;
return context.Response.IsRequestBeingRedirected;
}
}
public string GetSessionID(HttpContext context) {
string id = null;
if(pConfig.Cookieless == HttpCookieMode.UseUri) {
// Retrieve the SessionID from the URI.
} else {
var cookie = context.Request.Cookies[pConfig.CookieName];
if(cookie != null)
id = context.Request.Cookies[pConfig.CookieName].Value;
}
// Verify that the retrieved SessionID is valid. If not, return null.
if(!Validate(id))
id = null;
return id;
}
public string CreateSessionID(HttpContext context) {
return Guid.NewGuid().ToString();
}
public void RemoveSessionID(HttpContext context) {
context.Response.Cookies.Remove(pConfig.CookieName);
}
public void SaveSessionID(HttpContext context , string id , out bool redirected , out bool cookieAdded) {
redirected = false;
cookieAdded = false;
if(pConfig.Cookieless == HttpCookieMode.UseUri) {
// Add the SessionID to the URI. Set the redirected variable as appropriate.
redirected = true;
return;
} else {
context.Response.Cookies.Add(new HttpCookie(pConfig.CookieName , id) {
Domain = ConfigurationHelper.GetSection<Paths>().RootDomain.Path
});
cookieAdded = true;
}
}
public bool Validate(string id) {
try {
Guid testGuid = new Guid(id);
if(id == testGuid.ToString())
return true;
} catch {
}
return false;
}
}
注意在方法 SaveSessionID 将 Session 的 Cookie 存到的是根域名下。这样可以解决因为二级域名带来的 Session 不能共享的问题。
另外:MBCA 结合 AppFabricCachingBPA 可以检查 AppFabric 缓存主机存在的问题。
另外,没有被吓倒的同志可以参考下:
Windows Server AppFabric 缓存容量规划指南
ASP.NET 4 缓存会话状态提供程序的配置设置(AppFabric 1.1 缓存)
应用程序配置设置(Windows Server AppFabric 缓存)
服务器不可用性疑难解答(Windows Server AppFabric 缓存)
错误代码字符串表(Windows Server AppFabric 缓存)
运行状况监控工具(Windows Server AppFabric 缓存)
我以心力交瘁,期待大神的指点