ASP.NET MVC 4: разрешать только один запрос за раз
В моем приложении ASP.NET MVC я хочу обрабатывать все запросы последовательно; код действия / контроллера не должен выполняться одновременно с другим. Если два запроса поступают в одно и то же время, сначала он должен выполнить первый, а затем второй, когда первый будет выполнен.
Есть ли лучший способ сделать это, кроме использования глобальной переменной блокировки?
РЕДАКТИРОВАТЬ: приложение представляет собой больше пакет / сервис через Интернет, который выполняет вызовы веб-сервисов и очищает базу данных. Различные URL-адреса на сайте приводят к различным пакетным операциям. Это не сайт для конечных пользователей. Таким образом, мне нужно сделать так, чтобы за один раз выполнялся только один запрос к URL (который будет выполнять некоторые пакетные операции), в противном случае пакетная операция может быть повреждена, если код для нее выполняется одновременно с самим собой или другими пакетными операциями., Фактически, если другой запрос приходит, когда он выполняется в данный момент, он не должен выполняться вообще, даже после того, как предыдущий завершается; это должно просто дать сообщение об ошибке.
Я хотел бы знать, если бы был способ сделать это в IIS вместо кода. Если бы у меня была глобальная переменная блокировки, это усложнило бы код, и я мог бы работать в тупике, где переменная блокировки установлена в значение true, но никогда не может быть установлена в false.
РЕДАКТИРОВАТЬ: Образец кода плана реализации
[HttpPost]
public ActionResult Batch1()
{
//Config.Lock is a global static boolean variable
if(Config.Lock) { Response.Write("Error: another batch process is running"); return View(); }
Config.Lock = true;
//Run some batch calls and web services (this code cannot be interleaved with ExecuteBatchCode2() or itself)
ExecuteBatchCode();
Config.Lock = false;
return View();
}
[HttpPost]
public ActionResult Batch2()
{
if(Config.Lock) { Response.Write("Error: another batch process is running"); return View(); }
Config.Lock = true;
//Run some batch calls and web services (this code cannot be interleaved with ExecuteBatchCode1() or itself)
ExecuteBatchCode2();
Config.Lock = false;
return View();
}
Должен ли я беспокоиться о случае, когда код не достигает Config.Lock = false, что приводит к Config.Lock = true навсегда, что приводит к тому, что больше нет запросов на обслуживание?
3 ответа
Вы можете написать атрибут:
public class ExclusiveActionAttribute : ActionFilterAttribute
{
private static int isExecuting = 0;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Interlocked.CompareExchange(ref isExecuting, 1, 0) == 0)
{
base.OnActionExecuting(filterContext);
return;
}
filterContext.Result =
new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
Interlocked.Exchange(ref isExecuting, 0);
}
}
затем используйте его на контроллерах / методах, которыми вы хотите управлять:
[ExclusiveAction] //either here, for every action in the controller
public class MyController : Controller
{
[ExclusiveAction] //or here for specific methods
public ActionResult DoTheThing()
{
//foo
return SomeActionResult();
}
}
Вы принимаете запрос столько, сколько можете, людям не нравится ждать перед браузером. Но после, на стороне подачи, вы можете подтолкнуть их (скажем) Queue<T>
и обработать их в последовательности.
Короче:
- принять в асинхронном режиме
- процесс, на сервере, в последовательности
приведенный выше код не работает, вероятно, потому что, когда выполняется запрос 1 и отправляется запрос 2, служба возврата приложения недоступна, это хорошо, но если запрос 1 не выполняется и снова отправляется запрос 2 в приложение, приложение выполняет оба запроса одновременно. Я просматриваю код и меняю его.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ExclusiveActionAttribute : ActionFilterAttribute
{
private static int _isExecuting = 0;
private static int _isDuplicated = 0;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Interlocked.CompareExchange(ref _isExecuting, 1, 0) == 0)
{
base.OnActionExecuting(filterContext);
return;
}
Interlocked.Exchange(ref _isDuplicated, 1);
filterContext.Result = new StatusCodeResult((int)HttpStatusCode.ServiceUnavailable);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
if (_isDuplicated == 1)
{
Interlocked.Exchange(ref _isDuplicated, 0);
return;
}
Interlocked.Exchange(ref _isExecuting, 0);
}