در این مقاله قصد دارم روشی را برای محاسبهی تعداد کاربران لاگین شدهی فعال در یک پروژهی Asp.net Core، توضیح دهم. در این روش، کاربرانی را آنلاین در نظر گرفتهایم که در 10 دقیقهی گذشته، فعالیتی روی سامانه داشتهاند. البته این زمان را میتوانید تغییر دهید. برای اینکار ابتدا یک Middleware را به صورت زیر طراحی میکنیم :
در اینجا به کمک IMemoryCache، یک کلید را به نام OnlineUsers در کش ایجاد کردهایم. ابتدا بررسی میکنیم، اگر این کلید وجود نداشت، آن را ایجاد کند. نوع این کلید، یک Dictionary است که نام کاربری و ساعت آخرین در خواست کاربران، در آن ذخیره میشود.
اما یک نکته وجود دارد و اینکه در MiddleWare به ازای هر درخواست، کل دیکشنری را بررسی کردهایم و کاربرانی را که 10 دقیقه از آخرین فعالیت آنها گذشته است، حذف کردهایم. این بررسی به ازای هر درخواست، کار مناسبی نیست؛ چرا که ممکن است در ترافیک بالا، مشکل ساز شود. لذا میتوان سناریوهای مختلفی را برای آن در نظر گرفت:
public class OnlineUserMiddleWare { private readonly RequestDelegate _next; private readonly IMemoryCache _memoryCache; public OnlineUserMiddleWare(RequestDelegate next, IMemoryCache memoryCache) { _next = next; _memoryCache = memoryCache; } public async Task Invoke(HttpContext context) { if (!_memoryCache.TryGetValue("OnlineUsers", out Dictionary<string,DateTime> onlineUsers)) { onlineUsers = new Dictionary<string, DateTime>(); _memoryCache.Set("OnlineUsers", onlineUsers); } if (context.User.Identity.IsAuthenticated) { var name = context.User.Identity.Name; if (name != null) { if (onlineUsers.ContainsKey(name)) onlineUsers[name] = DateTime.Now; else onlineUsers.Add(name, DateTime.Now); } } foreach (var online in onlineUsers) { if (online.Value < DateTime.Now.AddMinutes(-10)) onlineUsers.Remove(online.Key); } await _next(context); } }
پس از اینکه مقدار OnlineUsers را از کش دریافت کردیم، چک میکنیم اگر کاربر لاگین شدهاست، نام کاربری را به همراه زمان جاری، در دیکشنری ذخیره کند و اگر کاربر درون دیکشنری موجود بود، فقط زمان آخرین درخواست را تغییر دهد (بهروز کند). بنابراین تا به اینجا هر کسی در سامانه لاگین کند، نام کاربری او، به همراه زمان آخرین درخواست، در دیکشنری ذخیره میشود.
نهایتا باید بررسی کنیم، اگر کاربری 10 دقیقه از آخرین درخواستش گذشته باشد، از دیکشنری حذف شود.
لازم است این Middleware به Startup اضافه شود:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider serviceProvider) { ... app.UseMiddleware<OnlineUserMiddleWare>(); ... }
برای نمایش تعداد کاربران آنلاین، کافی است در View یا مکان مورد نظر، مقدار دیکشنری را از memorycache فراخوانی کنیم: (حتی میتوان لیست کاربران را هم نمایش داد؛ چون نام کاربری آنها در دیکشنری ذخیره شدهاست)
public class DashboardController { private readonly IMemoryCache _memoryCache; public DashboardController(memoryCache) { _memoryCache = memoryCache; } public IActionResult Index() { _memoryCache.TryGetValue("OnlineUsers", out Dictionary<string, DateTime> onlineUsers); ViewBag.OnlineUsers = onlineUsers.Count; return View(); } }
- مثلا فقط جائیکه میخواهیم آمار نمایش داده شود، این بررسی صورت گیرد. البته اگر این آمار در صفحهای پرتکرار نمایش داده شود، این سناریو میتواند مفید باشد. ولی مثلا اگر قرار است آمار تنها برای مدیر نمایش داده شود و مدیر چندین روز این صفحه را بررسی نکند، ممکن است حجم این دیکشنری هر روز حجیمتر شود.
- میتوان در صفحاتی که اطمینان داریم روزانه چندین بار فراخوانی میشوند، این فرآیند را بررسی کنیم؛ مثل صفحه لاگین . لذا قسمت بررسی در MiddleWare را به صورت زیر تغییر دادم :
if(context.Request.Path.StartsWithSegments("/Identity/Account/Login")) { foreach (var online in onlineUsers) { if (online.Value < DateTime.Now.AddMinutes(-10)) onlineUsers.Remove(online.Key); } }