C # ложный http-ответ

#c# #httpresponse

#c# #httpresponse

Вопрос:

У меня есть поток, который возвращает статус http-ответа сайта, но иногда моя программа возвращает ложные результаты. и через некоторое время это дает хорошие результаты.
Ложный результат: для проверки требуется много времени, а затем говорится, что (например) Google не работает, что довольно неразумно, но через несколько секунд он возвращает хорошие результаты

Можете ли вы взглянуть и сказать мне, что не так? или как я могу его улучшить?
Проверяет все сайты в datagrid:

  private void CheckSites()
        {
            if (CheckSelected())
            {
                int rowCount = dataGrid.BindingContext[dataGrid.DataSource, dataGrid.DataMember].Count;
                string url;
                for (int i = 0; i < rowCount; i  )
                {
                    url = dataGrid.Rows[i].Cells[2].Value.ToString();
                    if (url != null)
                    {
                        Task<string[]> task = Task.Factory.StartNew<string[]>
                         (() => checkSite(url));

                        // We can do other work here and it will execute in parallel:
                        //Loading...

                        // When we need the task's return value, we query its Result property:
                        // If it's still executing, the current thread will now block (wait)
                        // until the task finishes:
                        string[] result = task.Resu<
                        selectRows();
                        if (result[0] != System.Net.HttpStatusCode.OK.ToString() amp;amp; result[0] != System.Net.HttpStatusCode.Found.ToString() amp;amp; result[0] != System.Net.HttpStatusCode.MovedPermanently.ToString())
                        {
                            //bad
                            notifyIcon1.ShowBalloonTip(5000, "Site Down", dataGrid.Rows[i].Cells[2].Value.ToString()   ", has a status code of:"   result, ToolTipIcon.Error);
                            dataGrid.Rows[i].DefaultCellStyle.BackColor = System.Drawing.Color.Wheat;
                            TimeSpan ts;
                            TimeSpan timeTaken = TimeSpan.Parse(result[1]);
                            dataGrid.Rows[i].Cells[3].Value = result[0];
                            dataGrid.Rows[i].Cells[3].Style.BackColor = System.Drawing.Color.Red;
                            dataGrid.Rows[i].Cells[4].Value = timeTaken.Seconds.ToString()   "."   String.Format("{0:0.00000}", timeTaken.Milliseconds.ToString())   " seconds.";
                            string sec = (DateTime.Now.Second < 10) ? "0"   DateTime.Now.Second.ToString() : DateTime.Now.Second.ToString();
                            string min = (DateTime.Now.Minute < 10) ? "0"   DateTime.Now.Minute.ToString() : DateTime.Now.Minute.ToString();
                            string hour = (DateTime.Now.Hour < 10) ? "0"   DateTime.Now.Hour.ToString() : DateTime.Now.Hour.ToString();
                            dataGrid.Rows[i].Cells[5].Value = hour   ":"   min   ":"   sec;

                            //loadbar
                        }
                        else if (result[0] == "catch")//catch
                        {
                            notifyIcon1.ShowBalloonTip(10000, "SITE DOWN", dataGrid.Rows[i].Cells[1].Value.ToString()   ", Error:"  result[1], ToolTipIcon.Error);
                            dataGrid.Rows[i].Cells[3].Value = result[1];
                            dataGrid.Rows[i].Cells[3].Style.BackColor = System.Drawing.Color.Red;
                            //loadbar

                        }
                        else
                        {
                            //good
                            TimeSpan timeTaken = TimeSpan.Parse(result[1]);
                            dataGrid.Rows[i].Cells[3].Value = result[0];
                            dataGrid.Rows[i].Cells[3].Style.BackColor = System.Drawing.Color.LightGreen;
                            dataGrid.Rows[i].Cells[4].Value = timeTaken.Seconds.ToString()   "."   String.Format("{0:0.00000}", timeTaken.Milliseconds.ToString())   " seconds.";
                            string sec = (DateTime.Now.Second < 10) ? "0"   DateTime.Now.Second.ToString() : DateTime.Now.Second.ToString();
                            string min = (DateTime.Now.Minute < 10) ? "0"   DateTime.Now.Minute.ToString() : DateTime.Now.Minute.ToString();
                            string hour = (DateTime.Now.Hour < 10) ? "0"   DateTime.Now.Hour.ToString() : DateTime.Now.Hour.ToString();
                            dataGrid.Rows[i].Cells[5].Value = hour   ":"   min   ":"   sec;
                            //loadbar
                        }
                        selectRows();
                    }
                }
            }
        }
  

Проверяет сайт:

   /////////////////////////////////
        ////Check datagrid websites-button - returns response
        /////////////////////////////////
        private string[] checkSite(string url)
        {
            string[] response = new string[2];
            url = dataGrid.Rows[0].Cells[2].Value.ToString();
            if (url != null)
            {
                try
                {
                    HttpWebRequest httpReq;


                    httpReq.Timeout = 10000;
                    //loadbar
                    dataGrid.Rows[0].DefaultCellStyle.BackColor = System.Drawing.Color.Wheat;
                    System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
                    timer.Start();
                    HttpWebResponse httpRes = (HttpWebResponse)httpReq.GetResponse(); //httpRes.Close();
                    timer.Stop();
                    //loadbar
                    HttpStatusCode httpStatus = httpRes.StatusCode;
                    response[0] = httpStatus.ToString();
                    response[1] = timer.Elapsed.ToString();//*
                    httpRes.Close();
                    return response;
                }
                catch (Exception he)
                {
                    response[0] = "catch";
                    response[1] = he.Message;
                    return response;
                }
            }
            response[0] = "catch";
            response[1] = "No URL entered";
            return response;
            //dataGrid.Rows[i].DefaultCellStyle.BackColor = System.Drawing.Color.Blue;

        }
  

Заранее спасибо.

Комментарии:

1. Пожалуйста, объясните, что именно вы подразумеваете под «ложными результатами» и «хорошими результатами».

2. сколько сайтов проверяют параллельно?

3. Это происходит на всем вашем компьютере? Например, если вы используете свой веб-браузер для перехода в Google один раз, произойдет ли сбой в другой раз?

4. (@Yahia) Он не параллельный, он проверяет один, затем завершает, затем еще один до конца строк данных. (@GabrielGraves) Нет, это происходит только случайным образом (я думаю) и когда я открываю программу в первый раз.

Ответ №1:

Предполагая, что предоставленный код является фактическим используемым кодом:

Прежде всего, ваше определение «Ложного результата» и «Хорошего результата» неверно. Если вы ожидаете A, но получаете B, это не означает, что B недопустимо. Если ваша жена рожает, и вы ожидаете мальчика, но оказывается, что это девочка, это не ложный результат. Просто неожиданно.

Тем не менее: давайте проанализируем вашу работу: требуется ли много времени для проверки сайта только для того, чтобы, наконец, получить??? результат, который не является кодом ответа 200. Мы можем почти с уверенностью предположить, что вы имеете дело с таймаутом. Если у вашего маршрутизатора, Google или любого другого основного сетевого устройства между ними возникают проблемы, ожидается, что он получит неожиданный ответ. «Тайм-аут», «Неверный запрос», «Сервер недоступен» и т.д. Почему это должно произойти? Невозможно сказать наверняка, не имея прямого доступа к вашей среде.

Однако, глядя на ваш код, я вижу, что вы используете TaskScheduler по умолчанию для выполнения каждой проверки как задачи в фоновом режиме (при условии, что вы не изменили планировщик задач по умолчанию, что было бы очень плохой практикой для начала). Планировщик задач по умолчанию планирует каждую задачу в пуле потоков, что приводит к одновременному выполнению множества задач. Здесь у нас есть хороший кандидат для перегрузки вашей сети. Многие сайты (особенно Google) довольно чувствительны к обработке большого количества запросов из одного и того же источника (особенно, если частота высока), поэтому, возможно, Google временно блокирует вас или сдерживает вас. Опять же, на данный момент это чистое предположение, но тот факт, что вы выполняете все проверки одновременно (если пул потоков не находится на максимуме), скорее всего, является причиной вашей проблемы.

Обновить

Я бы рекомендовал работать с LimitedConcurrencyTaskScheduler ( см. Здесь: http://blogs.msdn.com/b/pfxteam/archive/2010/04/09/9990424.aspx ). Здесь вы можете ограничить количество задач, которые могут выполняться асинхронно. Вы должны провести некоторое тестирование, чтобы определить, какое число идеально подходит в вашей ситуации. Также убедитесь, что частота не слишком высока. Трудно определить, что является слишком высоким, только тестирование может это доказать.

Комментарии:

1. Да, я думаю, что проблема в Google, если бы я мог добавить к этому, что бы вы порекомендовали сделать / изменить, чтобы разморозить программу во время проверки?

2. Наконец, считаете ли вы, что мой код может возвращать неправильное время ответа?

3. @agam360 — нет, это невозможно. время ответа привязано к вашей локальной машине. То есть, когда он отправляет запрос и когда он что-то получает (или тайм-аут). В этой простой логике нет известных ошибок 🙂

Ответ №2:

Чтобы смоделировать ваш сценарий, я создал Winform с таблицей данных и кнопкой. При загрузке формы я программно создаю список URL-адресов (в таблице) и привязываю к сетке данных. И при нажатии кнопки мы запускаем процесс загрузки. Вкратце, вам нужно написать больше защитного кода, а следующий код — только скелет того, как вы можете устранить проблему.

 using System;
using System.Data;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace app
{
    public partial class Form1 : Form
    {
        DataTable urls = new DataTable();
        public Form1()
        {
            InitializeComponent();
        }
        //Fill your uri's and bind to a data grid.
        void InitTable()
        {
            //Silly logic to simulate your scenario.
            urls = new DataTable();
            urls.Columns.Add(new DataColumn("Srl", typeof(string)));
            urls.Columns.Add(new DataColumn("Urls", typeof(Uri)));
            urls.Columns.Add(new DataColumn("Result", typeof(string)));

            DataRow dr = urls.NewRow();
            dr["Srl"] = "1";
            dr["Urls"] = new Uri("http://www.microsoft.com");
            dr["Result"] = string.Empty;
            urls.Rows.Add(dr);
            dr = urls.NewRow();
            dr["Srl"] = "2";
            dr["Urls"] = new Uri("http://www.google.com");
            dr["Result"] = string.Empty;
            urls.Rows.Add(dr);
            dr = urls.NewRow();
            dr["Srl"] = "3";
            dr["Urls"] = new Uri("http://www.stackoverflow.com");
            dr["Result"] = string.Empty;
            urls.Rows.Add(dr);
            urls.AcceptChanges();
        }
        void UpdateResult()
        {
            dataGridView1.DataSource = urls;
        }

        //Important
        // This example will freeze UI. You can avoid this while implementing
        //background worker or pool with some event synchronization. I haven't covered those area since
        //we are addressing different issue. Let me know if you would like to address UI freeze
        //issue. Or can do it your self.
        private void button1_Click(object sender, EventArgs e)
        {            
            //Create array for Task to parallelize multiple download.
            var tasks = new Task<string[]>[urls.Rows.Count];
            //Initialize those task based on number of Uri's
            for(int i=0;i<urls.Rows.Count;i  )
            {
                int index = i;//Do not change this. This is to avoid data race
                //Assign responsibility and start task.
                tasks[index] = new Task<string[]>(
                        () => checkSite(
                            new TaskInput(urls.Rows[index]["Urls"].ToString(), urls.Rows[index]["Srl"].ToString())));
                tasks[index].Start();

            }
            //Wait for all task to complete. Check other overloaded if interested.
            Task.WaitAll(tasks);

            //block shows how to access result from task
            foreach (var item in tasks)
            {
               DataRow[] rows=urls.Select("Srl='" item.Result[2] "'");
               foreach (var row in rows)
                      row["Result"]=item.Result[0] "|" item.Result[1];
            }
            UpdateResult();
        }
        //This is dummy method which in your case 'Check Site'. You can have your own
        string[] checkSite(TaskInput input)
        {
            string[] response = new string[3];
            if (input != null)
            {
                try
                {
                    WebResponse wResponse = WebRequest.Create(input.Url).GetResponse();

                    response[0] = wResponse.ContentLength.ToString();
                    response[1] = wResponse.ContentType;
                    response[2] = input.Srl;
                    return response;
                }
                catch (Exception he)
                {
                    response[0] = "catch";
                    response[1] = he.Message;
                   response[2] = input.Srl;
                    return response;
                }
            }
            response[0] = "catch";
            response[1] = "No URL entered";
            response[2] = input.Srl;
            return response;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            InitTable();
            UpdateResult();
        }
    }
    //Supply custom object for simplicity
    public class TaskInput
    {
       public TaskInput(){}
       public TaskInput(string url, string srl)
        {
            Url = url;
            Srl = srl;
        }
        public string Srl { get; set; }
        public string Url { get; set; }
    }
}
  

Комментарии:

1. Спасибо, но это не то, что я пытался сделать (я не хочу загружать файлы).

2. @agam360: загрузка была просто для имитации вашего сценария, а не для решения вашей проблемы. Мой ответ на вашу проблему можно найти в обработчике события button1_Click, из которого видно, как дождаться завершения нескольких задач перед получением результата.