1

我有一个 AspNetCore(核心 2.1)Web 应用程序,可以在任何单个服务器环境中正常工作,但在具有 2 个负载平衡 Web 服务器的环境中几秒钟后就会超时。

这是我的startup.cs课程和其他课程,以及我的AppSessionState表格的屏幕截图。我希望有人能指出我正确的道路。我在这上面花了 2 天时间,找不到其他需要设置的东西或我正在做的事情有什么问题。

以下代码的一些解释:

正如所见,我已按照步骤配置应用程序以使用分布式 SQL Server 缓存,并拥有一个帮助程序静态类HttpSessionService来处理从会话状态添加/获取值。此外,我有一个 Session-Timeout 属性,我对每个控制器进行注释以控制会话超时。在应用程序中点击几秒钟或几秒钟后,每个控制器操作都会进行此调用

HttpSessionService.Redirect()

Redirect()方法从该行获取 NULL 用户会话,这会导致应用程序超时。

var userSession = GetValues<UserIdentityView>(SessionKeys.User);

我已将两个 VS 调试器附加到两台服务器上,并且我注意到即使所有会话都进入其中一个调试器实例(一台服务器),AspNet 会话仍然为上述userSession值返回 NULL。

同样,这仅发生在分布式环境中,即,如果我在其中一个 Web 服务器上停止其中一个站点,则一切正常。

在此处输入图像描述

我已经查看并使用 SQLServer 实现了会话状态分布式缓存,如在不同页面中解释(相同),这里很少。

https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-3.0

https://www.c-sharpcorner.com/article/configure-sql-server-session-state-in-asp-net-core/

我确实看到会话被写入我创建AppSessionState的表中,但应用程序在具有 2 个负载平衡服务器的环境中继续超时。

启动.cs:

    public void ConfigureServices(IServiceCollection services)
    {
         // Session State distributed cache configuration against SQLServer.
         var aspStateConnStr = ConfigurationManager.ConnectionStrings["ASPState"].ConnectionString;
         var aspSessionStateSchemaName = _config.GetValue<string>("AppSettings:AspSessionStateSchemaName");
         var aspSessionStateTbl = _config.GetValue<string>("AppSettings:AspSessionStateTable");
         services.AddDistributedSqlServerCache(options =>
         {
            options.ConnectionString = aspStateConnStr;
            options.SchemaName = aspSessionStateSchemaName;
            options.TableName = aspSessionStateTbl;
         });

         ....
         services.AddSession(options =>
         {
            options.IdleTimeout = 1200;
            options.Cookie.HttpOnly = true;
            options.Cookie.IsEssential = true;
         });

         services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

         ...
         services.AddMvc().AddJsonOptions(opt => opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());

    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime, IDistributedCache distCache)
    {
        var distCacheOptions = new DistributedCacheEntryOptions()
                    .SetSlidingExpiration(TimeSpan.FromMinutes(5));

        // Session State distributed cache configuration.
        lifetime.ApplicationStarted.Register(() =>
        {
            var currentTimeUTC = DateTime.UtcNow.ToString();
            byte[] encodedCurrentTimeUTC = Encoding.UTF8.GetBytes(currentTimeUTC);
            distCache.Set("cachedTimeUTC", encodedCurrentTimeUTC, distCacheOptions);
        });

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseSession();   // This must be called before the app.UseMvc()

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });

        HttpSessionService.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>(), distCache, distCacheOptions);

     }

HttpSessionService(帮助类):

    public class HttpSessionService
    {
        private static IHttpContextAccessor _httpContextAccessor;
        private static IDistributedCache _distributedCache;

        private static ISession _session => _httpContextAccessor.HttpContext.Session;

        public static void Configure(IHttpContextAccessor httpContextAccessor, IDistributedCache distCache)
        {
            _httpContextAccessor = httpContextAccessor;
            _distributedCache = distCache;
        }

        public static void SetValues<T>(string key, T value)
        {
            _session.Set<T>(key, value);
        }

        public static T GetValues<T>(string key)
        {
            var sessionValue = _session.Get<T>(key);
            return sessionValue == null ? default(T) : sessionValue;
        }

        public static bool Redirect()
        {
            var result = false;
            var userSession = GetValues<UserIdentityView>(SessionKeys.User);

            if (userSession == null || userSession?.IsAuthenticated == false)
            {
                result = true;
            }

            return result;
        }
    }

会话超时属性:

    public class SessionTimeoutAttribute : ActionFilterAttribute
    {

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            var redirect = HttpSessionService.Redirect();

            if (redirect)
            {
                context.Result = new RedirectResult("~/Account/SessionTimeOut");
                return;
            }

            base.OnActionExecuting(context);
        }

    }

我的控制器

    [SessionTimeout]
    public class MyController : Controller
    {

         // Every action in this and any other controller time out and I get redirected by SessionTimeoutAttribute to "~/Account/SessionTimeOut"

    }
4

2 回答 2

0

看起来您正在从静态HttpSessionService 实例中的 HttpContext 捕获 Session 值。该值是每个请求的,所以如果你这样捕获它肯定会随机失败。如果要获取最新值,则IHttpContextAccessor 每次要访问HttpContext时都需要经过。

另外,我建议您将 HttpContext 传递给您的辅助方法,而不是使用IHttpContextAccessor. 它具有性能影响,通常仅在您绝对无法通过时才应使用HttpContext。您在此处显示的位置似乎有HttpContext可用的,所以我建议使用它而不是访问器。

于 2019-10-28T18:11:31.963 回答
0

抱歉,对此迟到的回复。我已经更改了我的原始实现,将 IDistributedCache 接口注入我的所有控制器并在 ConfigureServices() 函数的 Statusup.cs 类中使用此设置。

services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = aspStateConnStr;
    options.SchemaName = aspSessionStateSchemaName;
    options.TableName = aspSessionStateTbl;
    options.ExpiredItemsDeletionInterval = null;
});

这使它在网络农场中工作。如您所见,我将 ExpiredItemsDeletionInterval 设置为 null 以防止某些基本缓存条目从缓存中清除,但是这样做我遇到了另一个问题,即当我尝试获取它们时,即使条目是,我仍然会返回 null在数据库表中。所以,这是我试图弄清楚的另一件事。

于 2020-06-02T00:18:54.180 回答