HttpWebRequest Ограничения? Или плохая реализация

2

Я пытаюсь создать консольное приложение С#, которое будет отслеживать около 3000 URL-адресов (просто нужно знать, что HEAD-запрос возвратил 200, не обязательно содержимое и т.д.)

Моя попытка состояла в том, чтобы создать подпрограмму, проверяющую веб-URL, цикл и создание потоков, каждый из которых выполняет подпрограмму. Что происходит, если я запускаю с < 20 потоков, он выполняется нормально в большинстве случаев, но если я использую > 20 потоков, некоторые из тайм-аутов URL-адреса. Я попытался увеличить таймаут до 30 секунд, то же самое происходит. Сеть, в которой я запускаю эту операцию, более чем способна выполнять 50 запросов HTTP HEAD (соединение 10MBIT в ISP), и как процессор, так и сеть работают очень низко при выполнении подпрограммы.

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

Причина, по которой я хочу запустить > 20 потоков, - это то, что я хочу выполнить этот тест каждые 5 минут, причем часть URL-адреса занимает полный 10 секунд (или выше, если тайм-аут установлен выше), я хочу убедиться, что его можно запускать через все URL-адреса в течение 2-3 минут.

Есть ли лучший способ проверить, доступен ли URL-адрес, или я должен смотреть на систему/сеть на предмет проблемы.

MAIN

        while (rdr.Read())
        {
            Thread t = new Thread(new ParameterizedThreadStart(check_web));

            t.Start(rdr[0]);


        }

      static void check_web(object weburl)
      {
          bool isok;
          isok = ConnectionAvailable(weburl.ToString());
      }



      public static bool ConnectionAvailable(string strServer)
      {

          try
          {
              strServer = "http://" + strServer;
              HttpWebRequest reqFP = (HttpWebRequest)HttpWebRequest.Create(strServer);
              reqFP.Timeout = 10000;
              reqFP.Method = "HEAD";

              HttpWebResponse rspFP = (HttpWebResponse)reqFP.GetResponse();
              if (HttpStatusCode.OK == rspFP.StatusCode)
              {
                  Console.WriteLine(strServer + " - OK");
                  rspFP.Close();
                  return true;
              }
              else
              {
                  Console.WriteLine(strServer + " Server returned error..");
                  rspFP.Close();
                  return false;

              }

          }

          catch (WebException x)
          {
              if (x.ToString().Contains("timed out"))
              {
                  Console.WriteLine(strServer + " - Timed out");
              }
              else
              {
                  Console.WriteLine(x.Message.ToString());
              }

              return false;

          }

      }
  • 0
    Предполагая, что для их возврата требуется всего 1 секунда (а вы сказали, что некоторым потребуется 10 или более), для 3000 URL-адресов за 120 секунд вам потребуется проверять 25 в секунду каждую секунду в течение двух минут. Я могу ошибаться, но вам может потребоваться запустить ЛУЧШЕЕ РЕШЕНИЕ, чем ваше, в более чем одном боксе одновременно.
  • 1
    Если вы работаете в XP (после SP2), проверьте, видите ли вы эту ошибку в журнале событий: «TCP / IP достиг предела безопасности, наложенного на число одновременных попыток TCP-соединения»
Теги:
httpwebrequest

3 ответа

8

Просто помните, вы спросили.

Очень плохая реализация.

  • Не создавайте такие потоки. Очень мало иметь больше потоков, чем процессорные ядра. Дополнительные потоки будут в значительной степени конкурировать друг с другом, тем более, что все они работают с одним и тем же кодом.

  • Вам нужно реализовать блоки с использованием. Если вы выбросите исключение (и, скорее всего, вы это сделаете), тогда вы будете терять ресурсы.

  • Какова цель возврата bool? Вы проверяете его где-то? В любом случае, ваша обработка ошибок и исключений - беспорядок.

    • Когда вы получаете ответ не-200, вы не видите код ошибки.
    • Вы сравниваете свойство Message, чтобы решить, является ли он тайм-аутом. Microsoft должна помещать пробел между "временем" и "выходить", просто зная вас.
    • Если это не тайм-аут, вы отображаете только свойство Message, а не все исключение, а свойство Message уже является строкой и вам не нужно вызывать ToString() на нем.

Следующая партия изменений

Это не закончено, я не думаю, но попробую:

public static void Main()
{
    // Don't mind the interpretation. I needed an excuse to define "rdr"
    using (var conn = new SqlConnection())
    {
        conn.Open();
        using (var cmd = new SqlCommand("SELECT Url FROM UrlsToCheck", conn))
        {
            using (var rdr = cmd.ExecuteReader())
            {
                while (rdr.Read())
                {
                    // Use the thread pool. Please.
                    ThreadPool.QueueUserWorkItem(
                        delegate(object weburl)
                            {
                                // I invented a reason for you to return bool
                                if (!ConnectionAvailable(weburl.ToString()))
                                {
                                    // Console would be getting pretty busy with all
                                    // those threads
                                    Debug.WriteLine(
                                        String.Format(
                                            "{0} was not available",
                                            weburl));
                                }
                            },
                            rdr[0]);
                }
            }
        }
    }
}

public static bool ConnectionAvailable(string strServer)
{
    try
    {
        strServer = "http://" + strServer;
        var reqFp = (HttpWebRequest)WebRequest.Create(strServer);
        reqFp.Timeout = 10000;
        reqFp.Method = "HEAD";

        // BTW, what an "FP"?
        using (var rspFp = (HttpWebResponse) reqFp.GetResponse()) // IDisposable 
        {
            if (HttpStatusCode.OK == rspFp.StatusCode)
            {
                Debug.WriteLine(string.Format("{0} - OK", strServer));
                return true; // Dispose called when using is exited
            }

            // Include the error because it nice to know these things
            Debug.WriteLine(String.Format(
                 "{0} Server returned error: {1}", 
                 strServer, rspFp.StatusCode));
            return false;
        }
    }
    catch (WebException x)
    {
        // Don't tempt fate and don't let programs read human-readable messages
        if (x.Status == WebExceptionStatus.Timeout)
        {
            Debug.WriteLine(string.Format("{0} - Timed out", strServer));
        }
        else
        {
            // The FULL exception, please
            Debug.WriteLine(x.ToString());
        }

        return false;
    }
}

Почти выполнено - не протестирован Код поздней ночи

public static void Main()
{
    using (var conn = new SqlConnection())
    {
        conn.Open();
        using (var cmd = new SqlCommand("", conn))
        {
            using (var rdr = cmd.ExecuteReader())
            {
                if (rdr == null)
                {
                    return;
                }

                while (rdr.Read())
                {
                    ThreadPool.QueueUserWorkItem(
                        CheckConnectionAvailable, rdr[0]);
                }
            }
        }
    }
}

private static void CheckConnectionAvailable(object weburl)
{
    try
    {
        // If this works, it a lot simpler
        var strServer = new Uri("http://" + weburl);
        using (var client = new WebClient())
        {
            client.UploadDataCompleted += ClientOnUploadDataCompleted;
            client.UploadDataAsync(
                strServer, "HEAD", new byte[] {}, strServer);
        }
    }
    catch (WebException x)
    {
        Debug.WriteLine(x);
    }
}

private static void ClientOnUploadDataCompleted(
    object sender, UploadDataCompletedEventArgs args)
{
    if (args.Error == null)
    {
        Debug.WriteLine(string.Format("{0} - OK", args.UserState));
    }
    else
    {
        Debug.WriteLine(string.Format("{0} - Error", args.Error));
    }
}
  • 0
    да, вдохни! Еще так много нужно сказать. Он также, вероятно, сталкивается с проблемой соединения в секунду в зависимости от операционной системы, которую он запускает.
  • 0
    Сейчас полночь по Гринвичу-5, и я уже не сплю, поэтому я могу оставить отдых до завтра. Я подумал, что было бы неплохо не блокировать потоки пула потоков без веской причины, поэтому я думаю, что это будет дальше. После всего этого, мне лучше проверить это, или я буду очень смущен, если это не сработает.
1

Не использовать потоки.

Асинхронные обратные вызовы и очереди. Зачем создавать поток, когда ресурс, который все они хотят, - это доступ к внешнему миру. Ограничьте свои потоки примерно до 5, а затем реализуйте класс, который использует очередь. разделите код на две части, выборку и процесс. Один управляет потоком данных, а другой контролирует доступ к внешнему миру.

Используйте любой язык, который вам нравится, но вы не ошибетесь, если считаете, что потоки предназначены для обработки, а хруст числа и асинхронные обратные вызовы предназначены для управления ресурсами.

1

Используйте класс ThreadPool. Не создавайте сотни таких нитей. У потоков есть такие огромные накладные расходы, и в вашем случае происходит то, что ваш процессор будет тратить 99% времени на переключение контекста и 1% на выполнение реальной работы.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню