Оптимизация веб-службы ASMX с помощью нескольких длительных операций - PullRequest
3 голосов
/ 11 сентября 2009

Я пишу веб-сервис 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 секунды), и я хотел бы освободить ресурсы веб-сервера для обработки других запросов, пока он жду ответов. Еще одно примечание - у меня действительно есть поиск удаленного веб-сервиса в асинхронном режиме, я просто не хотел, чтобы приведенный выше пример был слишком запутанным. То, с чем я бьюсь, - это как поиск удаленной службы, так и поиск в базе данных, запущенной одновременно, и выяснение того, когда они завершат ОБА.

Спасибо за любые предложения.

Ответы [ 4 ]

2 голосов
/ 11 сентября 2009

Вы можете использовать пару AutoResetEvents, по одной для каждой нити. В конце выполнения потока вы вызываете AutoResetEvents.Set(), чтобы вызвать событие.

После порождения потоков вы используете WaitAll() с двумя событиями AutoResetEvents. Это приведет к блокировке потока, пока не будут установлены оба события.

Предостережение в этом подходе состоит в том, что вы должны гарантировать, что Set () гарантированно будет вызван, иначе вы будете блокироваться навсегда. Кроме того, убедитесь, что с потоками вы выполняете правильную обработку исключений, иначе вы непреднамеренно вызовете дополнительные проблемы с производительностью, когда неочищенные исключения приводят к перезапуску вашего веб-приложения.

MSDN Имеет пример кода, касающийся использования AutoResetEvent.

1 голос
/ 11 сентября 2009

См. Асинхронные методы веб-службы XML , Как: создать методы асинхронной веб-службы и Как: цепочка асинхронных вызовов с помощью метода веб-службы .

Но обратите внимание на первый абзац этих статей:

Эта тема относится к устаревшей технологии. Веб-службы XML и клиенты веб-служб XML теперь должны создаваться с использованием Windows Communication Foundation (WCF) .


Кстати, важно делать то, что говорится в этих статьях, потому что это освобождает рабочий поток ASP.NET во время выполнения долгосрочной задачи. В противном случае вы можете заблокировать рабочий поток, запретить ему обслуживать дальнейшие запросы и повлиять на масштабируемость.

0 голосов
/ 11 сентября 2009

Полагаю, у меня недостаточно представителей, чтобы поднять голос или оставить комментарий. Так что это комментарий к ответу Джона Сондерса и комментарий Алана к нему.

Вы определенно хотите согласиться с ответом Джона, если вы обеспокоены масштабируемостью и потреблением ресурсов.

Здесь есть два соображения: ускорение индивидуального запроса и обеспечение эффективной обработки многими параллельными запросами вашей системы. Первый ответ Алана и Джона достигается путем параллельного выполнения внешних вызовов.

Последнее, и, похоже, это было вашей главной заботой, достигается тем, что нигде не заблокированы потоки, т. Е. Ответ Джона.

Не порождай свои собственные темы. Потоки дороги, и в пуле потоков ввода-вывода уже есть множество потоков, которые будут обрабатывать ваши внешние вызовы, если вы используете асинхронные методы, предоставляемые .net framework.

Веб-метод вашего сервиса также должен быть асинхронным. В противном случае рабочий поток будет заблокирован до тех пор, пока не будут выполнены ваши внешние вызовы (это все равно 1-2 секунды, даже если они выполняются параллельно). И у вас есть только 12 потоков на процессор, обрабатывающих входящие запросы (если ваш machine.config настроен в соответствии с рекомендацией .) Т.е. вы могли бы максимально обработать 12 одновременных запросов (умножить на количество процессоров). С другой стороны, если ваш веб-метод является асинхронным, Begin будет возвращаться в значительной степени мгновенно, и поток возвращается в пул рабочих потоков, готовый обработать другой входящий запрос, в то время как ваши внешние вызовы ожидаются портом завершения ввода-вывода, где они будут обрабатываться потоками из пула потоков ввода-вывода после их возврата.

0 голосов
/ 11 сентября 2009

Если вы можете использовать функцию обратного вызова как для веб-запроса, так и для поиска в базе данных, тогда что-то в этом направлении может работать

bool webLookupDone = false;
bool databaseLookupDone = false;

private void FinishedDBLookupCallBack()
{
    databaseLookupDone = true;
    if(webLookupDone)
    {
        FinishMethod();
    }
}

private void FinishedWebLookupCallBack()
{
    webLookupDone = true;
    if(databaseLookupDone)
    {
        FinishMethod();
    }
}
...