2012-06-15 2 views
0

Моя основная программа работает с 8 задачами, используя Task.Factory.StartNewКак анализировать XML из HttpWebRequest асинхронно?

Каждая задача запрашивает результат XML-формата из webservice, а затем разбирается в коллекции, которая может быть записана в MSSQL с использованием TVP.

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

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

Есть три типа кодирования, я нашел и несколько ссылок ссылки на них:

How to use HttpWebRequest (.NET) asynchronously? -Когда я использую этот метод, я прохожу в XDocument вокруг с помощью пользовательского объекта в методе загрузки раздела

Асинхронное программирование в C# используя итераторы http://tomasp.net/blog/csharp-async.aspx -string/поток возвращается и анализировать с помощью XDocument.Load/разбор в основном методом

Ниже блока кода показывает последний метод найден и реализован в моем коде

Главный класс, начать задачи

private static void test() { 
    DBReader dbReader = new DBReader(); 
    Dictionary<string, DateTime> jobs = dbReader.getJob(); 
    JobHandler jh = new JobHandler(); 
    Stopwatch swCharge = new Stopwatch(); 
    Stopwatch swDetail = new Stopwatch(); 
    Stopwatch swHeader = new Stopwatch(); 
    //more stopwatch 

    Task[] tasks = new Task[] { 
    Task.Factory.StartNew(() => jh.processData<RawChargeCollection, RawCharge>(jobs["RawCharge"], 15, swCharge)), 
    Task.Factory.StartNew(() => jh.processData<RawDetailCollection, RawDetail>(jobs["RawDetail"], 15, swDetail)), 
    Task.Factory.StartNew(() => jh.processData<RawHeaderCollection, RawHeader>(jobs["RawHeader"], 15, swHeader)) 
    }; 
    Task.WaitAll(tasks); 
} 

Метод ProcessData

public void processData<T, S>(DateTime x, int mins, Stopwatch sw) 
      where T : List<S>, new() 
      where S : new() { 
      DateTime start = x; 
      DateTime end = x.AddMinutes(mins); 
      string fromDate, toDate; 
      StringBuilder str = new StringBuilder(); 
      XMLParser xmlParser = new XMLParser(); 
      DBWriter dbWriter = new DBWriter(); 
      while (end <= DateTime.UtcNow) { 
       fromDate = String.Format("{0:yyyy'-'MM'-'dd HH':'mm':'ss}", start); 
       toDate = String.Format("{0:yyyy'-'MM'-'dd HH':'mm':'ss}", end); 
       try { 
        sw.Restart(); 
        WebserviceClient ws = new WebserviceClient(); 

        XDocument xDoc = null; 
        var task = ws.GetRawData<S>(fromDate, toDate);  
        xDoc = XDocument.Parse(task.Result);  
        //show the download time  

        sw.Restart(); 
        T rawData = xmlParser.ParseXML<T, S>(xDoc); 

        if (rawData.Count != 0) { 
         sw.Restart(); 
         dbWriter.writeRawData<T, S>(rawData, start, end); 
         //log success 
        } 
        else { 
         //log no data 
        } 
       } 
       catch (Exception e) { 
        //log fail 
       } 
       finally { 
        start = start.AddMinutes(mins); 
        end = end.AddMinutes(mins); 
       } 
      } 
     } 

GetRawData просто ответственность для построения нужен URL, используемый в GetData.

Скачать раздел данных:

private static Task<string> GetData(string param) { 
      string url = String.Format("my working URL/{0}", param); 
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); 
      request.MediaType = "application/xml"; 
      Task<WebResponse> task = Task.Factory.FromAsync(
       request.BeginGetResponse, 
       asyncResult => request.EndGetResponse(asyncResult), 
       (object)null); 

      return task.ContinueWith(t => ReadStreamFromResponse(t.Result)); 
     } 

    private static string ReadStreamFromResponse(WebResponse response) { 
     using (Stream responseStream = response.GetResponseStream()) 
     using (StreamReader sr = new StreamReader(responseStream)) { 
      //Need to return this response 
      string strContent = sr.ReadToEnd(); 
      return strContent; 
     } 
    } 

В методе ProcessData я приурочил код, необходимый для загрузки с веб-сервиса. Скачивание занимает от 400 мс до 100000 мс. Нормальное время от 3000 до 8000 мс. Если я просто запустил 1 задачу, время клиентского процесса немного больше времени сервера.

Однако после запуска большего количества задач загрузка, которая занимает от 450 мс до 3000 мс (или что-то еще) на сервере, теперь может занять до 8000мс -90000мс для завершения клиентом процесса загрузки.

В моем сценарии узкое место должно быть на стороне сервера, из моего журнала это показывает, что клиент есть.

Основная статья, найденная для асинхронного программирования C#, кажется, демонстрирует чтение и обработку потока/строки без примера для XML. Является ли мой код неудачным из-за XML ?? Если нет, то в чем проблема моего кода?

EDIT: Да моя DEV машина и пользователи/целевая машина XP, слишком много, чтобы использовать .net 4.5 или CTP.

ServicePointManager.DefaultConnectionLimit и app.config connectionManagement похоже на одно и то же, поэтому я выбираю app.config, так как это можно изменить.

Вначале очень важно сменить максимальное подключение, но на самом деле не решило проблему. После кода временного кода с помощью Thread.Sleep (random) кажется, что «блокировка» не относится к параллельному коду.

Процесс сначала загружается из webservice (здесь требуется максимальное соединение), затем выполняйте некоторые незначительные сопоставления, наконец, записывайте в БД, запись в БД никогда не занимает более 1 секунды, по сравнению с загрузкой ничего не было, но после добавления максимального соединения к DB (тот же номер, что и webservice), не было никакого ожидания внезапно.

Таким образом, максимальное соединение с БД также имеет значение. Но я не понимаю, почему запись в DB с 150-600ms может вызвать ожидание более 20 секунд.

Что меня даже смущает, время ожидания находилось в блоке загрузки, а не в блоке записи БД.

+3

, учитывая тег 4.0, я предполагаю, что это не вариант для вас, но если/когда вы можете начать использовать 4.5, новый класс HttpClient является «изначально» асинхронным и делает асинхронную обработку ответов намного проще ИМХО - http://msdn.microsoft.com/en-us/library/system.net.http.httpclient(VS.110).aspx –

ответ

1

Я бы вернулся к более простой форме, по крайней мере, для отладки, где они были «нормальным»/синхронным кодом. Поскольку вы будете в худшем случае блокировать 8 потоков без необходимости, я бы не стал считать это большой проблемой.

Я бы предположил, что вы нажимаете вместо этого поведение по умолчанию, ограничивающее количество одновременных запросов.

Отсюда связаны SO нить ...

Max number of concurrent HttpWebRequests

... Вы можете посмотреть на то, что Джон Скит указал, на connectionManagement элемент:

http://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx

Элемент connectionManagement определяет максимальное количество соединений с сервером или группой серверов.

Кроме того, рекомендация Джона заменить HTTP-вызовы просто Thread.Sleep, чтобы увидеть, влияет ли параллелизм, отлично. Если ваши 8 задач могут выполнять параллельные вызовы Thread.Sleep, ваша проблема не является «совпадением верхнего уровня», а вместо этого ограничена тем, что они делают (например, ограничение по умолчанию для одновременного подключения).

+2

Или установите предел, используя 'ServicePointManager.DefaultConnectionLimit'. – svick

Смежные вопросы