You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

发布到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 17:44:52