The*_*sen 3 c# concurrency design-patterns asp.net-web-api asp.net-core
由多个 Web api 端点组成的 ASP.NET 核心 2.1 应用程序(实体框架)。其中之一是“加入”端点,用户可以在其中加入队列。另一个是“离开”端点,用户可以在此离开队列。
队列有 10 个可用位置。
如果所有 10 个位置都填满,我们会返回一条消息,说“队列已满”。
如果正好有 3 个用户加入,我们返回 true。
如果加入的用户数不是 3,那么我们返回 false。
200 个对触发满意的用户已准备好加入和离开不同的队列。他们都同时调用“加入”和“离开”端点。
这意味着我们必须以顺序方式处理传入的请求,以确保以良好且可控的方式将用户添加和删除到正确的队列中。(对?)
一种选择是将添加QueueService类作为AddSingleton<>中IServiceCollection再进行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;
}
}
Run Code Online (Sandbox Code Playgroud)
另一种选择是使QueueService瞬态,但随后我失去了服务的状态,并且为每个请求提供了一个新实例,这使得 lock() 毫无意义。
问题:
[1] 我应该在内存中处理队列的状态吗?如果是,如何将其与数据库对齐?
[2] 有没有我错过的精彩模式?或者我怎么能以不同的方式处理这个?
我最终得到了这个简单的解决方案。
创建一个带有 a 的静态(或单例)类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;
}
Run Code Online (Sandbox Code Playgroud)
_context 不再有问题。
通过这种方式,我可以以良好且受控的方式处理并发请求。
如果在某个时候多个服务器发挥作用,消息代理或 ESB 也可能是一种解决方案。
这意味着我们必须以顺序方式处理传入的请求,以确保以良好且可控的方式将用户添加和删除到正确的队列中。(对?)
基本上是的。请求可以并发执行,但它们必须以某种方式彼此同步(例如锁定或数据库事务)。
我应该处理内存中队列的状态吗?
通常,最好使 Web 应用程序无状态且请求独立。在此模型下,状态存储在数据库中。巨大的优势是不需要进行同步,应用程序可以在多个服务器上运行。此外,如果应用程序重新启动(或崩溃),则不会丢失任何状态。
这在这里似乎完全可能和合适。
将状态放在关系数据库中,并使用并发控制使并发访问安全。例如,使用可序列化的隔离级别并在死锁的情况下重试。这为您提供了一个非常好的编程模型,您可以假装自己是数据库的唯一用户,但它是完全安全的。所有访问都必须放入一个事务中,并且在死锁的情况下必须重试整个事务。
如果您坚持内存中状态,则拆分单例和瞬态组件。将全局状态放入单例类中,并将作用于该状态的操作放入临时类中。这样,诸如数据库访问之类的瞬态组件的依赖注入非常简单和干净。全局类应该很小(也许只是数据字段)。
| 归档时间: |
|
| 查看次数: |
5133 次 |
| 最近记录: |