发布到IIS后静态类Globals的用户数据无法持久化求助
问题原因分析
ASP.NET应用部署到IIS后,静态类Globals的users列表被清空的核心原因是应用程序域(AppDomain)重启:
- 调试模式下,Web服务器(IIS Express)单进程运行,AppDomain不会轻易重启,静态成员状态能稳定保持。
- 生产环境的IIS中,应用程序池会因多种触发条件重启:预设回收时间、内存占用过高、web.config/bin目录文件变更、服务器资源调整等。每次重启都会重新初始化所有静态类,导致之前存储的
users列表被重置为空。
另外当前方案存在架构缺陷:静态列表是进程内存储,若后续扩展到多服务器负载均衡,不同服务器的静态列表相互独立,用户请求切换服务器后会无法找到自身数据。
解决方案
1. 改用持久化存储(推荐)
将用户数据存储到外部持久化介质,彻底摆脱对进程内静态状态的依赖:
- 数据库(如SQL Server):创建用户会话表,包含
identityCode、userIsLogIn、Expires等字段,操作时直接读写数据库。 - Redis缓存:利用Redis键值对存储用户数据,设置与Cookie一致的过期时间,读写性能更高,适配高并发场景。
修改后的核心逻辑示例:
// 替换静态列表为数据库/Redis操作 public static class UserStorage { // 示例:从Redis获取用户 public static UserData GetUser(string identityCode) { var redis = ConnectionMultiplexer.Connect("your_redis_connection"); var db = redis.GetDatabase(); var userJson = db.StringGet($"user:{identityCode}"); return userJson.HasValue ? JsonConvert.DeserializeObject<UserData>(userJson) : null; } // 示例:保存用户到Redis public static void SaveUser(UserData user, TimeSpan expires) { var redis = ConnectionMultiplexer.Connect("your_redis_connection"); var db = redis.GetDatabase(); var userJson = JsonConvert.SerializeObject(user); db.StringSet($"user:{user.identityCode}", userJson, expires); } } // 修改CookieHandler逻辑 public static class CookieHandler { public static CookieHeaderValue CheckUpCookie(HttpRequestMessage Request) { CookieHeaderValue cookieValue = Request.Headers.GetCookies("identityCode").FirstOrDefault(); if (cookieValue == null) { string cookieCode = Guid.NewGuid().ToString(); // 用Guid生成唯一标识码 var newUser = new UserData() { identityCode = cookieCode }; // 保存到持久化存储,过期时间和Cookie一致 UserStorage.SaveUser(newUser, TimeSpan.FromDays(1)); var resCookie = new CookieHeaderValue("identityCode", cookieCode ); resCookie.Expires = DateTimeOffset.Now.AddDays(1); resCookie.Domain = Request.RequestUri.Host; resCookie.Path = "/"; resCookie.HttpOnly = true; resCookie.Secure = Request.RequestUri.Scheme == Uri.UriSchemeHttps; // 生产环境建议设为true return resCookie; } else { var identityCode = cookieValue["identityCode"].Value; var existingUser = UserStorage.GetUser(identityCode); if(existingUser == null) { // 若存储中无此用户,重新生成 string newCookieCode = Guid.NewGuid().ToString(); var newUser = new UserData() { identityCode = newCookieCode }; UserStorage.SaveUser(newUser, TimeSpan.FromDays(1)); var resCookie = new CookieHeaderValue("identityCode", newCookieCode ); resCookie.Expires = DateTimeOffset.Now.AddDays(1); resCookie.Domain = Request.RequestUri.Host; resCookie.Path = "/"; resCookie.HttpOnly = true; resCookie.Secure = Request.RequestUri.Scheme == Uri.UriSchemeHttps; return resCookie; } // 更新Cookie过期时间 cookieValue.Expires = DateTimeOffset.Now.AddDays(1); cookieValue.Domain = Request.RequestUri.Host; cookieValue.Path = "/"; cookieValue.HttpOnly = true; cookieValue.Secure = Request.RequestUri.Scheme == Uri.UriSchemeHttps; return cookieValue; } } }
2. 临时方案:改用MemoryCache
若暂时无法接入外部存储,可改用MemoryCache替代静态列表,它支持过期策略和更稳定的内存管理:
public static class Globals { private static readonly ObjectCache _cache = MemoryCache.Default; private static readonly CacheItemPolicy _policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddDays(1) }; public static UserData GetUser(string identityCode) { return _cache.Get($"user:{identityCode}") as UserData; } public static void AddUser(UserData user) { _cache.Set($"user:{user.identityCode}", user, _policy); } }
注意:此方案仍会在AppDomain重启时丢失数据,仅适合临时测试场景,不推荐生产环境使用。
3. 优化IIS应用程序池配置
进一步调整配置减少重启频率:
- 延长定期回收时间间隔(默认1740分钟)
- 取消**回收工作进程(分钟)**的勾选,改为基于内存/CPU阈值触发回收
- 确保禁用重叠回收选项未勾选(默认勾选,重叠回收会启动新进程,旧进程的静态数据不会迁移到新进程)
内容的提问来源于stack exchange,提问作者Saleh
相关产品推荐
相关产品推荐

