AspNetCore.Session-Distributed Cache сохраняет время ожидания на распределенных серверах - PullRequest
0 голосов
/ 22 октября 2019

У меня есть веб-приложение AspNetCore (ядро 2.1), которое отлично работает в любой среде с одним сервером, но через несколько секунд в среде с 2 веб-серверами с балансировкой нагрузки.

Вот мои startup.cs и другие классы, а также снимок экрана с моей AppSessionState таблицей. Я надеюсь, что кто-то может указать мне правильный путь. Я потратил 2 дня на это и не могу найти ничего другого, что нуждается в настройках или в чем дело с тем, что я делаю.

Некоторое объяснение приведенного ниже кода:

Как видно, яМы выполнили шаги, чтобы настроить приложение на использование распределенного кэширования SQL Server и иметь статический вспомогательный класс HttpSessionService, который обрабатывает добавление / получение значений из состояния сеанса. Кроме того, у меня есть атрибут Session-Timeout, который я аннотирую каждый из моих контроллеров для управления тайм-аутами сеанса. И через несколько секунд или щелчков в приложении, поскольку каждое действие контроллера делает этот вызов

HttpSessionService.Redirect()

, этот Redirect() метод получает сеанс пользователя NULL из этой строки, что вызываетприложение на время ожидания.

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

Я подключил два отладчика VS к обоим серверам, и я заметил, что даже когда все сеансы приходят к одному из экземпляров отладчика (один сервер)сеанс AspNet все еще возвращал NULL для указанного выше значения userSession.

Опять же, это происходит ТОЛЬКО в распределенной среде, т.е. если я остановлю один из сайтов на одном из веб-серверов, все будет работать нормально.

enter image description here

Я рассмотрел и реализовал распределенное кэширование состояния сеанса с помощью 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, но приложение приложениеПродолжает время ожидания в среде с двумя серверами с балансировкой нагрузки.

Startup.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;
        }
    }

SessionTimeoutAttribute:

    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);
        }

    }

MyController

    [SessionTimeout]
    public class MyController : Controller
    {

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

    }

1 Ответ

0 голосов
/ 28 октября 2019

Похоже, вы захватываете значение Session из HttpContext в вашем статическом экземпляре HttpSessionService. Это значение равно за запрос , поэтому оно определенно будет случайным сбоем, если вы его так захватите. Вам нужно проходить через IHttpContextAccessor каждый раз, когда вы хотите получить доступ к HttpContext значению , если вы хотите получить последнее значение.

Кроме того, я бы посоветовал вам пройтиHttpContext в ваших вспомогательных методах вместо использования IHttpContextAccessor. Он влияет на производительность и обычно должен использоваться, только если вы абсолютно не можете пропустить HttpContext. В тех местах, которые вы здесь показываете, похоже, имеется HttpContext, поэтому я рекомендую использовать его вместо аксессора.

...