У меня есть веб-приложение 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
.
Опять же, это происходит ТОЛЬКО в распределенной среде, т.е. если я остановлю один из сайтов на одном из веб-серверов, все будет работать нормально.
Я рассмотрел и реализовал распределенное кэширование состояния сеанса с помощью 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"
}