Оптимизация веб-службы ASMX с помощью нескольких длительных операций
-
05-07-2019 - |
Вопрос
Я пишу веб-сервис ASP.NET с использованием C #, который имеет функцию DoLookup (). Для каждого вызова функции DoLookup () мне нужно, чтобы мой код выполнял два отдельных запроса: один к другому веб-сервису на удаленном сайте и один к локальной базе данных. Оба запроса должны быть завершены, прежде чем я смогу скомпилировать результаты и вернуть их в ответ на метод DoLookup. Проблема, с которой я сталкиваюсь, заключается в том, что я хочу сделать это максимально эффективным, как с точки зрения времени отклика, так и использования ресурсов на веб-сервере. Мы ожидаем до нескольких тысяч запросов в час. Вот приблизительный C # -подобный обзор того, что я имею до сих пор:
public class SomeService : System.Web.Services.WebService
{
public SomeResponse DoLookup()
{
// Do the lookup at the remote web service and get the response
WebResponse wr = RemoteProvider.DoRemoteLookup();
// Do the lookup at the local database and get the response
DBResponse dbr = DoDatabaseLookup();
SomeResponse resp = new SomeResponse( wr, dbr);
return resp;
}
}
Приведенный выше код выполняет все последовательно и прекрасно работает, но теперь я хочу сделать его более масштабируемым. Я знаю, что могу асинхронно вызывать функцию DoRemoteLookup () (RemoteProvider имеет методы BeginRemoteLookup / EndRemoteLookup) и что я также могу выполнять поиск в базе данных асинхронно, используя методы BeginExecuteNonQuery / EndExecuteNonQuery.
Мой вопрос (наконец-то) заключается в следующем: как запустить поиск удаленной веб-службы и базу данных одновременно в отдельных потоках и убедиться, что они оба завершены, прежде чем возвращать ответ?
Причина, по которой я хочу выполнить оба запроса в отдельных потоках, заключается в том, что они оба могут иметь длительное время ответа (1 или 2 секунды), и я хотел бы освободить ресурсы веб-сервера для обработки других запросов, пока он жду ответов. Еще одно примечание - у меня действительно есть поиск удаленного веб-сервиса в асинхронном режиме, я просто не хотел, чтобы приведенный выше пример был слишком запутанным. То, с чем я бьюсь, - это как поиск удаленной службы, так и поиск в базе данных, запущенной одновременно, и выяснение того, когда они завершат ОБА.
Спасибо за любые предложения.
Решение
Вы можете использовать пару AutoResetEvents
, по одному для каждого потока. В конце выполнения потока вы вызываете AutoResetEvents.Set ()
, чтобы вызвать событие.
После порождения потоков вы используете WaitAll ()
с двумя AutoResetEvents. Это приведет к блокировке потока, пока не будут установлены оба события.
Предостережение в этом подходе заключается в том, что вы должны гарантировать, что Set () гарантированно будет вызван, иначе вы будете блокироваться навсегда. Кроме того, убедитесь, что с потоками вы выполняете надлежащую обработку исключений, иначе вы непреднамеренно вызовете дополнительные проблемы с производительностью, когда неочищенные исключения приводят к перезагрузке веб-приложения.
MSDN содержит пример кода, касающийся использования AutoResetEvent. р>
Другие советы
См. методы асинхронной веб-службы XML , Как создать методы асинхронной веб-службы и Практическое руководство. Цепочка асинхронных вызовов с помощью метода веб-службы .
Но обратите внимание на первый абзац этих статей:
<Ч>Эта тема относится к устаревшей технологии. Веб-службы XML и клиенты веб-служб XML теперь должны создаваться с использованием Windows Communication Foundation (WCF) а>.
Кстати, важно делать то, что говорится в этих статьях, потому что это освобождает рабочий поток ASP.NET во время выполнения долгосрочной задачи. В противном случае вы можете заблокировать рабочий поток, запретить ему обслуживать дальнейшие запросы и повлиять на масштабируемость.
Предполагая, что у вас может быть функция обратного вызова как для веб-запроса, так и для поиска в базе данных, тогда что-то в этом роде может работать
bool webLookupDone = false;
bool databaseLookupDone = false;
private void FinishedDBLookupCallBack()
{
databaseLookupDone = true;
if(webLookupDone)
{
FinishMethod();
}
}
private void FinishedWebLookupCallBack()
{
webLookupDone = true;
if(databaseLookupDone)
{
FinishMethod();
}
}
Я полагаю, у меня недостаточно репутации, чтобы высказываться или комментировать. Так что это комментарий к ответу Джона Сондерса и комментарий Алана к нему.
Вы определенно хотите согласиться с ответом Джона, если вы обеспокоены масштабируемостью и потреблением ресурсов. Р>
Здесь есть два соображения: ускорение индивидуального запроса и эффективная обработка вашей системы многими параллельными запросами. Первый ответ Алана и Джона достигается путем параллельного выполнения внешних вызовов. Р>
Последнее, и, похоже, это было вашей главной заботой, достигается тем, что нигде не заблокированы потоки, т. е. ответ Джона.
Не создавайте свои собственные темы. Потоки дороги, и в пуле потоков ввода-вывода уже есть множество потоков, которые будут обрабатывать ваши внешние вызовы, если вы используете асинхронные методы, предоставляемые платформой .net.
Веб-метод вашего сервиса также должен быть асинхронным. В противном случае рабочий поток будет заблокирован до тех пор, пока не будут выполнены ваши внешние вызовы (это все равно 1-2 секунды, даже если они выполняются параллельно). И у вас есть только 12 потоков на процессор, обрабатывающий входящие запросы (если ваш machine.config установлен в соответствии с рекомендация .) Т.е. вы могли бы максимально обработать 12 одновременных запросов (умножить на количество процессоров). С другой стороны, если ваш веб-метод является асинхронным, Begin будет возвращаться в значительной степени мгновенно, и поток возвращается в пул рабочих потоков, готовый обработать другой входящий запрос, в то время как ваши внешние вызовы ожидаются портом завершения ввода-вывода, где они будут обрабатываться потоками из пула потоков ввода-вывода после их возврата.