Может ли Linq AsParallel() избавиться от объектов SoapHttpClientProtocol преждевременно?
В веб-приложении ASP.Net MVC 4, над которым я работаю. У меня есть одна страница, которая в основном генерирует отчет, получая данные от службы SOAP.
Мой код в основном выглядит так
List<CustomThings> serverInfos = ServerInfos;
serverInfos.AsParallel().ForAll(srvInfo =>
{
SoapHttpClientProtocol soapProxy = CreateProxy(srvInfo);
//call make soap calls through the soap client
//store results in the proper places
}
Причина, по которой я делаю AsParallel здесь, заключается в том, что выполнение нескольких запросов по HTTP в последовательном режиме занимает вечно. Я должен добавить, что этот код работает, хотя время от времени.
Возможно ли, что вещи будут выбрасываться непредсказуемым образом, и PLINQ не является хорошим решением для того, что я пытаюсь сделать здесь?
Возможно ли, что другая проблема с потоками может вызвать ошибку, из-за которой мыльный клиент "сдается"?
Дополнительная информация
Этот конкретный прокси-сервер взаимодействует с ArcGIS Server. Как правило, вы можете проверить журналы сервера и посмотреть, когда конкретные запросы инициируются и если запросы не удалось. В этих журналах ничего не отображается.
Вот пример внутренней трассировки стека исключений, которую я получаю из кода AsParallel.
Исключение: System.AggregateException: произошла одна или несколько ошибок. ---> System.Net.WebException: лежащее в основе соединение было закрыто: соединение, которое, как ожидали, будет сохранено живым, было закрыто сервером. ---> System.IO.IOException: невозможно прочитать данные из транспортного соединения: существующее соединение было принудительно закрыто удаленным хостом. ---> System.Net.Sockets.SocketException: существующее соединение было принудительно закрыто удаленным узлом в System.Net.Sockets.Socket.Receive(буфер Byte[], смещение Int32, размер Int32, SocketFlags socketFlags) в System.NET.Sockets.NetworkStream.Read(буфер Byte[], смещение Int32, размер Int32) --- конец трассировки стека внутренних исключений --- в System.Net.Sockets.NetworkStream.Read(буфер Byte[], смещение Int32, Размер Int32) в System.Net.PooledStream.Read(буфер Byte[], смещение Int32, размер Int32) в System.Net.Connection.SyncRead(запрос HttpWebRequest, логический userRetrievedStream, Boolean probeRead) --- Конец внутренней трассировки стека исключений --- в System.Web.Services.Protocols.WebClientProtocol.GetWebResponse(запрос веб-запроса) в System.Web.Services.Protocols.HttpWebClientProtocol.GetWebResponse(запрос веб-запроса) в System.Web.Services.Protocols.Inlient, Параметры Object[]) в ESRI.ArcGIS.SOAP.FeatureServerProxy.Query(Int32 LayerOrTableID, String DefinitionExpression, QueryFilter QueryFilter, ServiceDataOptions ServiceDataOptions, String GdbVersion, Double MaximumAllowableOffset) в System.Linq.Parallel.SelectQueryOperator
2.SelectQueryOperatorResults.GetElement(Int32 index) at System.Linq.Parallel.QueryResults
1.get_Item (индекс Int32) в System.Linq.Parallel.PartitionedDataSource1.ListContiguousIndexRangeEnumerator.MoveNext(T& currentElement, Int32& currentKey) at System.Linq.Parallel.PipelineSpoolingTask
2.SpoolingWork () в System.Linq.Parallel.SpoolingTaskBase.Work() в System.Linq.Parallel.QueryTask.BaseWork(объект не используется) в System.Linq.Parallel.QueryTask. <. Cctor> b__0 (объект o) в System.Threading.Tasks.Task.InnerInvoke() в System.Threading.Tasks.Task.Execute()
2 ответа
PLINQ даже не знает, что ваш объект соединения существует. Это не может закрыть это.
Внимательно прочитайте сообщение:
Существующее соединение было принудительно закрыто удаленным хостом.
Сервер неожиданно закрыл соединение. Ваш клиент не виноват.
Точная интерпретация исключения является важным навыком отладки. Эта информация была прямо в сообщении об исключении.
Возможно, вы создаете слишком большую нагрузку. Установите устойчивую степень параллелизма. Эвристика по умолчанию предназначена для работы процессора, а не для ввода-вывода.
.WithDegreeOfParallelism(10)
Соединение, которое, как ожидали, будет поддерживаться в живых, было закрыто сервером.
Это может означать, что сервер не поддерживает HTTP-поддержку.
Я не думаю, что вы делаете что-то ужасно неправильно AsParallel
для ваших запросов HTTP Soap, и я не думаю, что это проблема многопоточности.
Однако параллельные запросы, очевидно, подталкивают ваш клиент / сервер к количеству ограничений на соединения, и именно поэтому вы видите, что соединения закрываются.
Я бы поспорил, что ваш клиент, сервер или оба не настроены для обработки количества одновременных соединений, которые вы устанавливаете. Вот почему это работает, когда вы запускаете запросы в последовательном режиме.
Я полагаю, у вас нет доступа к конфигурации сервера, поэтому вы могли бы контролировать количество параллельных запросов, которые вы одновременно отправляете на сервер, установив ParallelEnumerable.WithDegreeOfParallelism
установка как в следующем фрагменте:
.AsParallel()
.WithDegreeOfParallelism(15)
Таким образом вы управляете параллелизмом и не рискуете перегружать сервер большим количеством запросов по небольшому количеству соединений.
Что касается клиента, вы должны убедиться, что вы установили макс. количество одновременных клиентских подключений к соответствующему номеру, просто чтобы убедиться, что ваши запросы могут использовать отдельные подключения к серверу, и предотвратить повторное использование подключений, которые могут вызвать проблемы Keep-Alive. Сервер может закрыть соединение, если число запросов, использующих соединение, превысило максимальное количество соединений для активности в реальном времени или если оно превышает настройки тайм-аута.
Вы можете установить лимит клиентского соединения программно, используя ServicePointManager.DefaultConnectionLimit
установка. Например, вы можете установить его на 50:
System.Net.ServicePointManager.DefaultConnectionLimit = 50;
Вот пример настройки макс. Соединения до 50 с использованием файла конфигурации:
<configuration>
<system.net>
<connectionManagement>
<add address="*" maxconnection="50" />
</connectionManagement>
</system.net>
Я использовал "50" в качестве примера, вы должны определить / рассчитать / измерить, что является лучшим параметром для вашей установки.
Также убедитесь, что вы правильно утилизируете свои HTTP-соединения после каждого запроса, чтобы предотвратить тайм-ауты соединения.