none
Уменьшение нагрузки на сервер RRS feed

  • Вопрос

  • Здравствуйте

    Протестили утилитой: http://habrahabr.ru/post/65128/ для нагрузочного тестирования наш сайт.
    При одновременно работающих 50 пользователях сайт падает. 
    Сайт рукотворно написан - CSM не используется. - ASP.NET(WebForms)
    Если ли какие то простые способы. чтобы сайт не падал?

    18 июня 2012 г. 6:26

Ответы

  • Угу, не обратил внимание, на то что всего 50 пользователей.

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


    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        // Здесь ваша полезная работа 
    }

    • Изменено Алексей ЛосевEditor 19 июня 2012 г. 6:50
    • Предложено в качестве ответа PashaPash 19 июня 2012 г. 9:06
    • Помечено в качестве ответа ansi_str 21 июня 2012 г. 7:42
    19 июня 2012 г. 6:41
    Отвечающий
  • Да, а Вы говорите падает приложение. Открыли соединение в конструкторе и забыли про него:

    try
    {
      sqlConnection.Open();
    }
    catch (Exception ex)
    {
    }


    Так чего же вы хотите тогда. Открывайте соединени строго по тебованию, потом сразу же закрывайте его:

    public class DBWorker
      {
        private SqlConnection sqlConnection;
        private SqlCommand sqlCommand;
        private string connectionString;
        /// <summary>
        /// DBWorker constructor
        /// </summary>
        public DBWorker()
        {
          connectionString = ConfigurationManager.ConnectionStrings["ApplicationServices"].ToString();
        }
    
        public List<News> SelectAllNews()
        {
          List<News> newsList = new List<News>();
          sqlCommand = new SqlCommand();
          string selectNewsQuery = string.Format(@"SELECT Id, Header, ContentShort, ContentAll, DateCreateShow, PathToBigPicture, PathToSmallPicture, DateCreateTime, IsDeleted  FROM News WHERE IsDeleted = 0 ORDER BY [DateCreateTime] DESC");
          sqlCommand.CommandText = selectNewsQuery;
          using (SqlConnection connection = new SqlConnection(connectionString))
          {
            sqlCommand.Connection = connection;
            connection.Open();
            SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
            try
            {
              while (sqlDataReader.Read())
              {
                newsList.Add(new News(Convert.ToUInt64(sqlDataReader[0]), sqlDataReader[1].ToString(), sqlDataReader[2].ToString(), sqlDataReader[3].ToString(), sqlDataReader[4].ToString(), sqlDataReader[5].ToString(), sqlDataReader[6].ToString(),
                                                       Convert.ToDateTime(sqlDataReader[7]), sqlDataReader[8].ToString()));
              }
            }
            finally
            {
              sqlDataReader.Close();
            }
          }
          return newsList;
        }
    
        public void CloseConnection()
        {
          sqlConnection.Close();
        }
      }

    Код набирал быстро, если есть какие опечатки то извиняюсь заранее.

    • Помечено в качестве ответа ansi_str 21 июня 2012 г. 7:41
    19 июня 2012 г. 6:43
    Модератор

Все ответы

  • "Если ли какие то простые способы. чтобы сайт не падал?" - нет, нету. Тут всё очень относительно и условно. Вопрос класса: "У меня не заводится машина, можете по телефону объяснить, что и как сделать. Марку не знаю, капот не открывал, только умею ключ вставлять и поварачивать. Может ключ неправильно поварачиваю? ".  Это Ваше приложение, и Вы должны знать его архитектуру, то как оно построено и работает. Причин может быть 1000. Нужно анализировать и выявлять слабые места. Используйте нагрузочное тестирование, профилировщики. Посмотрите где, какой код медленно работает. Запросы к бд не оптимальны или методы выполняются долго или страница медленно рендерится или ещё что-то. Или архитектура изначально неправильная? Выявили узкие места, а потом только спрашивайте. Вот мол у меня этот метод медленно обрабатывает, из-за него всё приложение тормозит, как можно его оптимизировать и улучшить. А то на такой абстрактный вопрос ответить невозможно.
    18 июня 2012 г. 6:50
    Модератор
  • Да в том то и дело - что всё просто:

    Есть страница с новостями.
    Новости выводятся, используя обычный контрол.

      <asp:ListView ID="NewsList" runat="server" OnPagePropertiesChanging="NewsList_PagePropertiesChanging">
            <ItemTemplate>
                <div style="width: 800px; text-align: left;">
                    <div style="margin-bottom: 10px; padding-left: 20px;">
                        <asp:Label ID="DateCreateNews" Text='<%# Eval("DateCreateShow") %>' runat="server" />
                        <div style="display: inline-block;">
                            |
                        </div>
                        <asp:LinkButton ID="HeaderNews" Text='<%# Eval("Header") %>' Style="text-decoration:none; font-weight: bold; color:black;" CommandArgument='<%# Eval("Id") %>'
                            OnClick="ShowMoreAboutNewsOnHeader_Click" runat="server" />
                    </div>
                    <div style="display: inline-block; width: 120px; padding-left: 20px;">
                        <asp:ImageButton ID="ImageNews" ImageUrl='<%# Eval("PathToSmallPicture") %>' CommandArgument='<%# Eval("Id") %>'
                            OnClick="ShowMoreAboutNewsOnPicture_Click" Height="100" Width="100" runat="server" />
                    </div>
                    <div style="display: inline-block; width: 600px; text-align: left; vertical-align: top;
                        padding-right: 10px;">
                        <div style="margin-bottom: 10px; text-align: justify;">
                            <asp:Label ID="ContentNews" Text='<%# Eval("ContentShort") %>' runat="server" />
                        </div>
                </div>
                </div>
                <div style="padding-bottom: 20px; padding-top: 20px; width: 760px; padding-left: 20px;
                    padding-right: 20px;">
                    <hr style="border-style: dotted;" />
                </div>
            </ItemTemplate>

    Метод в контроле вызывает метод из DAL:

            private void FillNewsLine()
            {
                DBWorker worker = new DBWorker();
                NewsList.DataSource = worker.SelectAllNews();
                NewsList.DataBind();
                worker.CloseConnection();
            }

    Запрос к базе - обычный SELECT:

            public List<News> SelectAllNews()
            {
                List<News> newsList = new List<News>();
    
                string selectNewsQuery = string.Format(@"SELECT Id, Header, ContentShort, ContentAll, DateCreateShow, PathToBigPicture, PathToSmallPicture, DateCreateTime, IsDeleted  FROM News WHERE IsDeleted = 0 ORDER BY [DateCreateTime] DESC");
                sqlCommand.CommandText = selectNewsQuery;
                SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
                try
                {
                    while (sqlDataReader.Read())
                    {
                        newsList.Add(new News(Convert.ToUInt64(sqlDataReader[0]), sqlDataReader[1].ToString(), sqlDataReader[2].ToString(), sqlDataReader[3].ToString(), sqlDataReader[4].ToString(), sqlDataReader[5].ToString(), sqlDataReader[6].ToString(),
                                                               Convert.ToDateTime(sqlDataReader[7]), sqlDataReader[8].ToString()));
                    }
                }
                finally
                {
                    sqlDataReader.Close();
                }
                return newsList;
            }

    Всё просто :)
    Но при нагрузочном тестировании не работает


    18 июня 2012 г. 7:50
  • Да метод то, простой. Но откуда Вы знаете, что "бутылочное горлышко" именно он? Используйте инструменты нагрузочного тестирования Visual studio 2010, вот и вот для начала. Используйте Glimpse, маленький мануал по нему. Используйте Fiddler, справочка по нему. Используйте Firebug, для получения детальных данных о сгенерированной странице. Используйте SQL Server Profiler, для получения сведений о запросе. А потом только, из полученных данных, делайте выводы: скажем вот метод  List<news>SelectAllNews(), выполняется долго, целых 2 секунды, что тут можно сделать.

    18 июня 2012 г. 8:14
    Модератор
  • А как именно не работает? Падает с исключением, зависает?
    18 июня 2012 г. 11:02
  • Падает с исключением:

    Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
    19 июня 2012 г. 3:23
  • Увеличьте размер пула SqlConnection-ов. Про него можно почитать здесь.
    19 июня 2012 г. 3:32
    Отвечающий
  • Увеличение размера пула не лучший выбор, если приложение падает всего из-за 50 пользователей, значит проблема в коде. У меня есть подозрения, что Вы не закрываете открытые соединения и поэтому пул переполняется. Покажите более полный код вашего DAL класса. И ещё создайте нагрузочный тест в Visual Studio, запустите его, если там всё правильно насторить то показывается всё: сколько подключений открыто, количество запросов и т.д. Одновременно следите за этим и в Sql Server Profiler.
    19 июня 2012 г. 5:58
    Модератор
  • Увеличение не помогло.

    Есть тестовый сервер: SQL Server 2003 + IIS 6, на нём всё прекрасно работает
    Есть боевой сервер: SQL Server 2008 + IIS7, на нём сайт падает.

     public class DBWorker
        {
            private SqlConnection sqlConnection;
            private SqlCommand sqlCommand;
            public void CloseConnection()
            {
                sqlConnection.Close();
            }
    
            /// <summary>
            /// DBWorker constructor
            /// </summary>
            public DBWorker()
            {
                string connectionString = ConfigurationManager.ConnectionStrings["ApplicationServices"].ToString();
                sqlConnection = new SqlConnection(connectionString);
                sqlCommand = new SqlCommand();
                sqlCommand.Connection = sqlConnection;
    
                try
                {
                    sqlConnection.Open();
                }
                catch (Exception ex)
                {
                }
            }
    
    public List<News> SelectAllNews()
            {
                List<News> newsList = new List<News>();
    
                string selectNewsQuery = string.Format(@"SELECT Id, Header, ContentShort, ContentAll, DateCreateShow, PathToBigPicture, PathToSmallPicture, DateCreateTime, IsDeleted  FROM News WHERE IsDeleted = 0 ORDER BY [DateCreateTime] DESC");
                sqlCommand.CommandText = selectNewsQuery;
                SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
                try
                {
                    while (sqlDataReader.Read())
                    {
                        newsList.Add(new News(Convert.ToUInt64(sqlDataReader[0]), sqlDataReader[1].ToString(), sqlDataReader[2].ToString(), sqlDataReader[3].ToString(), sqlDataReader[4].ToString(), sqlDataReader[5].ToString(), sqlDataReader[6].ToString(),
                                                               Convert.ToDateTime(sqlDataReader[7]), sqlDataReader[8].ToString()));
                    }
                }
                finally
                {
                    sqlDataReader.Close();
                }
                return newsList;
            }
    
            public void CloseConnection()
            {
                sqlConnection.Close();
            }
    


    19 июня 2012 г. 6:31
  • Угу, не обратил внимание, на то что всего 50 пользователей.

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


    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();
        // Здесь ваша полезная работа 
    }

    • Изменено Алексей ЛосевEditor 19 июня 2012 г. 6:50
    • Предложено в качестве ответа PashaPash 19 июня 2012 г. 9:06
    • Помечено в качестве ответа ansi_str 21 июня 2012 г. 7:42
    19 июня 2012 г. 6:41
    Отвечающий
  • Да, а Вы говорите падает приложение. Открыли соединение в конструкторе и забыли про него:

    try
    {
      sqlConnection.Open();
    }
    catch (Exception ex)
    {
    }


    Так чего же вы хотите тогда. Открывайте соединени строго по тебованию, потом сразу же закрывайте его:

    public class DBWorker
      {
        private SqlConnection sqlConnection;
        private SqlCommand sqlCommand;
        private string connectionString;
        /// <summary>
        /// DBWorker constructor
        /// </summary>
        public DBWorker()
        {
          connectionString = ConfigurationManager.ConnectionStrings["ApplicationServices"].ToString();
        }
    
        public List<News> SelectAllNews()
        {
          List<News> newsList = new List<News>();
          sqlCommand = new SqlCommand();
          string selectNewsQuery = string.Format(@"SELECT Id, Header, ContentShort, ContentAll, DateCreateShow, PathToBigPicture, PathToSmallPicture, DateCreateTime, IsDeleted  FROM News WHERE IsDeleted = 0 ORDER BY [DateCreateTime] DESC");
          sqlCommand.CommandText = selectNewsQuery;
          using (SqlConnection connection = new SqlConnection(connectionString))
          {
            sqlCommand.Connection = connection;
            connection.Open();
            SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
            try
            {
              while (sqlDataReader.Read())
              {
                newsList.Add(new News(Convert.ToUInt64(sqlDataReader[0]), sqlDataReader[1].ToString(), sqlDataReader[2].ToString(), sqlDataReader[3].ToString(), sqlDataReader[4].ToString(), sqlDataReader[5].ToString(), sqlDataReader[6].ToString(),
                                                       Convert.ToDateTime(sqlDataReader[7]), sqlDataReader[8].ToString()));
              }
            }
            finally
            {
              sqlDataReader.Close();
            }
          }
          return newsList;
        }
    
        public void CloseConnection()
        {
          sqlConnection.Close();
        }
      }

    Код набирал быстро, если есть какие опечатки то извиняюсь заранее.

    • Помечено в качестве ответа ansi_str 21 июня 2012 г. 7:41
    19 июня 2012 г. 6:43
    Модератор
  • А разве я не закрываю соединение вот так:

    DbWorker worker = new DbWorker()
    List<news> news = worker.SlectAllNews();
    worker.CloseConnection();

    19 июня 2012 г. 7:33
  • Как повезет. Если метод SelectAllNew упадет с ошибкой, то нет.

    Воспользуйтесь схемой с using которую я показал выше. Это рекомендации MS. Такой пример показан прямо в топике про SqlConnection.

    19 июня 2012 г. 7:44
    Отвечающий
  • Если какие то из ответов помогли вам с решением вашей проблемы, не забудьте отметить их. Для этого есть соответствующая кнопка под каждым ответом.
    19 июня 2012 г. 8:03
    Отвечающий
  • Пока не помогло - не тестил предложенный вариант Ваш
    19 июня 2012 г. 8:07
  • Ок, как попробуете отпишитесь. Интересно ))
    19 июня 2012 г. 8:09
    Отвечающий
  • Как повезет. Если метод SelectAllNew упадет с ошибкой, то нет.

    Воспользуйтесь схемой с using которую я показал выше. Это рекомендации MS. Такой пример показан прямо в топике про SqlConnection.


    А метод SelevtAllNew у топик стартера может упасть только на строке
    SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();

    Остальная работа выделена в try-catch. ansi_str у вас код, который вызывает данный метод тоже обернут в try-catch? Не оставляйте пустым блок catch, выводите ошибку в лог, пусть даже самописный, это упростит отладку и поиск ошибки.

    Для связи [mail]

    20 июня 2012 г. 7:54
    Модератор
  • Нет, код вызова не обёрнут в try \ catch

    Раз уж разговор зашёл о try \ catch
    Стоит ли его вообще в жизни использовать?
    20 июня 2012 г. 8:09
  • "Стоит ли его вообще в жизни использовать?" - да, за редким исключением некоторых ситуаций, когда дальнейшая работа приложения невозможна.
    20 июня 2012 г. 8:17
    Модератор
  • Если разговаривать о try-catch - то этот разговор лучше вынести в отдельное обсуждение, а то будет много оффтопа и в теме будет сложно разобраться.

    Ждем результатов о попытки замены работы с подключением на блок using.


    Для связи [mail]

    • Помечено в качестве ответа ansi_str 21 июня 2012 г. 7:42
    • Снята пометка об ответе Abolmasov DmitryModerator 21 июня 2012 г. 10:33
    20 июня 2012 г. 8:32
    Модератор
  • Да, из-за не использования конструкции using сервере падал, соединения к базе не закрывались.
    Но странно - всё же, я же в методе закрывал, ну да ладно - впредь буду using использовать.

    Всем спасибо.
    21 июня 2012 г. 7:41