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

StackOverflow https://stackoverflow.com/questions/1407772

Вопрос

Я пишу веб-сервис 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.

Другие советы

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top