![](/img/trans.png)
[英]how to handle 1000 concurrent user requests per second in asp.net web api
[英]Concurrent web api requests and how to handle state in ASP.NET core
由多個Web api端點組成的ASP.NET core 2.1應用程序(實體框架)。 其中之一是“加入”端點,用戶可以在其中加入隊列。 另一個是“離開”端點,用戶可以在其中離開隊列。
隊列中有10個可用位置。
如果所有10個地點均已填寫完畢,我們將向您返回一條消息,說“隊列已滿”。
如果剛好有3個用戶加入,則返回true。
如果加入的用戶數不是3,則返回false。
200個觸發滿意的用戶准備加入並離開不同的隊列。 他們都同時調用“ join”和“ leave”端點。
這意味着我們必須按順序處理傳入的請求,以確保以良好且可控制的方式將用戶添加和刪除到正確的隊列。 (對?)
一種選擇是將QueueService
類添加為IServiceCollection
中的AddSingleton<>
,然后進行lock()
以確保一次只能輸入一個用戶。 但是,如何處理dbContext
,因為它已注冊為AddTransient<>
或AddScoped<>
?
連接部分的偽代碼:
public class QueueService
{
private readonly object _myLock = new object();
private readonly QueueContext _context;
public QueueService(QueueContext context)
{
_context = context;
}
public bool Join(int queueId, int userId)
{
lock (_myLock)
{
var numberOfUsersInQueue = _context.GetNumberOfUsersInQueue(queueId); <- Problem.
if (numberOfUsersInQueue >= 10)
{
throw new Exception("Queue is full.");
}
else
{
_context.AddUserToQueue(queueId, userId); <- Problem.
}
numberOfUsersInQueue = _context.GetNumberOfUsersInQueue(queueId); <- Problem.
if (numberOfUsersInQueue == 3)
{
return true;
}
}
return false;
}
}
另一個選擇是使QueueService
瞬態狀態,但是隨后我丟失了服務的狀態,並且為每個請求提供了一個新實例,從而使lock()毫無意義。
問題:
[1]我應該改為處理內存中隊列的狀態嗎? 如果是,如何使其與數據庫對齊?
[2]是否有我錯過的出色模式? 還是我該如何處理?
這意味着我們必須按順序處理傳入的請求,以確保以良好且可控制的方式將用戶添加和刪除到正確的隊列。 (對?)
本質上是。 這些請求可以並發執行,但是它們必須以某種方式彼此同步(例如,鎖定或數據庫事務)。
我應該處理內存中隊列的狀態嗎?
通常,最好使Web應用程序成為無狀態且獨立的請求。 在這種模式下狀態存儲在數據庫中。 巨大的優勢在於,無需進行同步,並且該應用程序可以在多台服務器上運行。 另外,如果應用程序重新啟動(或崩潰),則不會丟失任何狀態。
在這里,這似乎是完全可行和適當的。
將狀態放在關系數據庫中,並使用並發控制使並發訪問安全。 例如,使用可序列化的隔離級別,並在出現死鎖的情況下重試。 這為您提供了一個非常不錯的編程模型,在該模型中,您假裝自己是數據庫的唯一用戶,但它絕對安全。 必須將所有訪問權限放入事務中,如果出現死鎖,則必須重試整個事務。
如果您堅持使用內存中狀態,則請拆分單例和瞬態組件。 將全局狀態置於單例類中,並將對該狀態進行操作的操作置於瞬時狀態中。 通過這種方式,臨時組件(例如數據庫訪問)的依賴項注入非常容易且干凈。 全局類應該很小(也許只是數據字段)。
我最終得到了這個簡單的解決方案。
創建一個帶有ConcurrentDictionary<int, object>
的靜態(或單例)類,該類帶有queueId和一個鎖。
創建新隊列后,將queueId和新的鎖定對象添加到字典中。
使QueueService類AddTransient<>
,然后:
public bool Join(int queueId, int userId)
{
var someLock = ConcurrentQueuePlaceHolder.Queues[queueId];
lock (someLock)
{
var numberOfUsersInQueue = _context.GetNumberOfUsersInQueue(queueId); <- Working
if (numberOfUsersInQueue >= 10)
{
throw new Exception("Queue is full.");
}
else
{
_context.AddUserToQueue(queueId, userId); <- Working
}
numberOfUsersInQueue = _context.GetNumberOfUsersInQueue(queueId); <- Working
if (numberOfUsersInQueue == 3)
{
return true;
}
}
return false;
}
_context不再有問題。
這樣,我就可以以一種可控的方式處理並發請求。
如果某個時候可以使用多個服務器,那么消息代理或ESB也可以成為解決方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.