Несколько способов ускорить приложение на ASP.NET WebForms

    Существует распространенное мнение, что приложения на ASP.NET WebForms очень медленные, тяжеловесные, подходят только для корпоративных порталов и в основном работают в интрасетях. Однако есть несколько способов существенно повысить скорость работы вашего приложения. Большинство этих способов не новы, применяются многими разработчиками. Здесь мы собрали те из них, которые хорошо себя зарекомендовали при разработке движка AdVantShop.NET Ultimate.

    1.Отказываемся от UpdatePanel


    Одним из самых главных факторов, тормозящих работу вэб-сайта на ASP.NET WebForms – использование контрола UpdatePanel. Для его работы требуется разместить на странице ScriptManager. В этом и заключается первая проблема.
    Попробуем создать aspx-страничку и поместим простой Label внутри UpatePanel
    <asp:ScriptManager runat="server" />
    <asp:UpdatePanel ID="Updatepanel1" runat="server">
        <ContentTemplate>
            <asp:Label runat="server" Text="Hello world!" />
        </ContentTemplate>
    </asp:UpdatePanel>
    


    Запустим проект, посмотрим, во что это превратилось:
    <script src="/WebSite1/WebResource.axd?d=ddsVPpuyrEEfoBbqjFYV9oHZ-YB8armGl03nreWb8sxbdd0MXlSne0kVoo7mheRQ1cxLip1L_44SDmzKsEIHZW930AWZ2Zs_ae9a9NTgNck1&t=634790730663322540" type="text/javascript"></script>
    


    <script src="/WebSite1/ScriptResource.axd?d=x2K70A_qh5LLRPvqydlLAwONxKroPWgAGdmlhYHXFoXfUrRwcjMDwh9AgTRQkLVd0gEj8C6MhbyA_hq2RLgNpJxsoak2SQ5Dsi8RIcHR-k72-HxC0Vc2GfaaM_NqOWGS6pN4O31eNf9cYeLVlOWAro2fu6lKP46_8vn-OGH5mmN_8TmV6BQKdue5UaNBp45o0&t=ffffffff940d030f" type="text/javascript"></script>
    <script type="text/javascript">
    //<![CDATA[
    if (typeof(Sys) === 'undefined') throw new Error('ASP.NET Ajax client-side framework failed to load.');
    //]]>
    </script>
    


    <script src="/WebSite1/ScriptResource.axd?d=JmrJ_HQ4TGD_jchBEWNRb8OenR4k6OQ7VRyH_toBPQT3JUiqiQs3R0jW7v7zXJ2kEQZHsBtgkthfErRPRvwOKI-uadU-pRHAfl5iwxP1hhqLZX5DmrmatAe9-DtrY8UpvLZcFvWHClUe3sQQzRVLcMCbnpjYQ_AnnrUMVfpTPXHnstGsM4ZnlODzOKmhM5LU0&t=ffffffff940d030f" type="text/javascript"></script>
         <script type="text/javascript">
    //<![CDATA[
    Sys.WebForms.PageRequestManager._initialize('ctl02', 'form1', ['tUpdatepanel1','Updatepanel1'], [], [], 90, '');
    //]]>
    </script>
    


    <div id="Updatepanel1">
        <span>Hello world!</span>
    </div>
    


    image

    Видим, что для вывода простого текста внутри UpdatePanel потребовалось 2 кб html и еще почти 100 кб скриптов. Тоже самое без UpdatePanel занимает примерно 500 байт.
    Вторая же проблема – жизненный цикл страницы. То, что полностью отсутствует в ASP.NET MVC, и то с чем приходится уживаться в ASP.NET WebForms. Подробнее отличия есть здесь.
    При любом событии внутри UpdatePanel происходит PostBack и выполняются все события страницы, в которых могут происходить трудные вычисления или запросы к базе данных. Если не использовать кругом конструкцию вроде:
     if(!Page.IsPostBack)
     {
         Do something… 
     }
    


    то приложение будет работать не так быстро, как хотелось бы.

    Простой пример:

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

    image

    Как это можно сделать, не используя UpdatePanel.
    Создаем класс обработчик http-запросов
    public class CheckOrder : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
    	context.Response.ContentType = "application/json";
           Order order = OrderService.GetOrder(context["number"]);
            if(order != null)
            {
                var statusResult = new
                                   {
                                       Status = order.Status,
                                       Comment = order.Comment
                                   };
                context.Response.Write(JsonConvert.SerializeObject(
    statusResult));
            }   
        }
        
        public bool IsReusable
        {
            get { return true;}
        }
    }
    


    Тут еще пригодится библиотека Newtonsoft.Json для сериализации объектов в JSON. Но можно обойтись и без нее возвращая просто текст.
    Далее создаем js функцию, которая при помощи jquery при нажатии на кнопку «Проверить» будет отправлять через ajax get запрос на получение статуса.

    function CheckOrder(ordernum) {
        $.ajax({
            async: false,
            dataType: "json",
            cache: false,
            url: "checkorder.ashx",
            data: { number: ordernum },
            success: function (status) {
                $("#orderStatus").text(status.Status);
         $("#orderComment").text(status.Comment);
            },
            error: function () {
    $("#orderStatus").text("Ошибка получения статуса"));
            }
        });
    }
    


    Все очень просто, но эффективно. Также вместо httphandler можно обойтись статическим методом в классе страницы. Для этого достаточно пометить его атрибутом [WebMethod].

    2. Избавляемся от ViewState



    ViewState – еще одна особенность WebForms. У нее есть свои плюсы и минусы, которые хорошо описаны в этой статье. Однако в большинстве случаев без него можно обойтись. В старых версиях AdvantShop.Net ViewState на разных страницах был от 15 до 30 кб, что было очень неприятным, поскольку эти килобайты пересылались на каждом обращении к страничке. Как можно этого если не избежать, то хотя бы минимизировать риски?
    Достаточно всего лишь прописать на Мастер-Странице

    <%@ Master Language="C#" CodeFile="MasterPage.master.cs" Inherits="MasterPage" EnableViewState="false" ViewStateMode="Disabled" %>
    
    


    Однако начнутся проблемы с такими контролами, которым необходим ViewState для хранения своего состояния – например, DropDownList или ListView. В таких случаях придется ViewState все же включить, но его можно будет отключить для каждого контрола, который его не использует. Ссылка для заинтересовавшихся.

    3. Уходим от стандартных контролов и AjaxControlToolkit



    Так сложилось, что в Microsoft при разработке контролов для ASP.NET не особо задумывались о чистоте и оптимальности верстки, количестве запросов и скорости работы этих самых контролов. GridView — очень медленный, TreeView генерирует ужасную верстку, Calendar труден в настройке и захламляет html. Те, кто ими пользовались, могли сами в этом убедиться.
    Простой выход использовать jquery плагины или любые другие js фреймворки.

    4. Минимизируем контент страниц



    Чтобы повысить скорость работы сайта необходимо уменьшить количество http запросов к серверу и максимально сжать статический контент страниц: изображения, css и js файлы. С изображениями тут все понятно – используем выше степень сжатия и спрайты. Css и js файлы можно сжать с помощью специальных минимизаторов. Однако это неудобно, приходится хранить две версии: полную — для редактирования и дебага, и минимизированную — для продакшена.
    Другой способ – использовать библиотеку SquishIt. Как это делать, можно почитать тут. В результате комбинируем и минимизируем все js и css в два файла, уменьшаем количество запросов и увеличиваем время загрузки страницы примерно на две секунды.
    До применения:

    image
    После:

    image

    5. Сжимаем HTML


    Большинство браузеров поддерживает сжатие контента GZip либо Deflate. Создаем класс, который будет выполнять сжатие.

    namespace AdvantShop.Core.Compress
    {
        public class CompressContent : IHttpModule
        {
            public void Dispose()
            {
                // Nothing to dispose; 
            }
            public void Init(HttpApplication context)
            {
                context.PreRequestHandlerExecute += PreRequestHandlerExecute;
            }
    
            private static void PreRequestHandlerExecute(object sender, EventArgs e)
            {
                var app = (HttpApplication)sender;
                HttpRequest request = app.Context.Request;
                HttpResponse response = app.Context.Response;
    
                if (request.FilePath.Contains(".aspx") || request.FilePath.Contains(".js") || request.FilePath.Contains(".css"))
                {
                    if (!(request.Browser.IsBrowser("IE") && request.Browser.MajorVersion <= 6)) // IE6 does not support compression 
                    {
                        string acceptEncoding = request.Headers[HttpConstants.HttpAcceptEncoding];
                        if (!string.IsNullOrEmpty(acceptEncoding))
                        {
                            acceptEncoding = acceptEncoding.ToLower(CultureInfo.InvariantCulture);
                            if (acceptEncoding.Contains(HttpConstants.HttpContentEncodingGzip))
                            {
                                response.Filter = new HttpCompressStream(response.Filter, CompressionMode.Compress, HttpCompressStream.CompressionType.GZip);
                                response.AddHeader(HttpConstants.HttpContentEncoding, HttpConstants.HttpContentEncodingGzip);
                            }
                            else if (acceptEncoding.Contains(HttpConstants.HttpContentEncodingDeflate))
                            {
                                response.Filter = new HttpCompressStream(response.Filter, CompressionMode.Compress, HttpCompressStream.CompressionType.Deflate);
                                response.AddHeader(HttpConstants.HttpContentEncoding, HttpConstants.HttpContentEncodingDeflate);
                            }
                        }
                    }
                    else
                    {
                        response.Filter = new HttpCompressStream(response.Filter, CompressionMode.Compress, HttpCompressStream.CompressionType.None);
                    }
                }
            }
        }
    }
    
    

    Затем, чтобы подключить этот модуль в свое приложение просто добавим в web.config строчки:
    <modules>
    <add name="CompressContent" type="AdvantShop.Core.Compress.CompressContent"/>
      </modules>
    


    Получаем очень хороший результат – сжатие текста в четыре раза. Причем сжимается не только html но также js и css.
    До:
    image
    После:
    image

    6. Кешируем UserControls


    Чтобы закешировать User Control, достаточно поместить в заголовок контрола строку вида:
    <%@ OutputCache Duration="60" VaryByParam="CategoryID" %>
    

    Таким образом, мы указываем, что контрол будет храниться в кеше 60 секунд и создастся своя версия кеша в зависимости от параметра из Request[“CategoryID”].
    Вместо заключения
    Это только основные методы ускорения, которые в большей степени применимы к WebForms. Однако не стоит забывать и про другие методы, особенно те, на которые указывает эта полезная тулза — PageSpeed.
    AdvantShop
    Company
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 16

      +5
      А почему бы сразу не перейти на использование ASP.NET MVC, нежели допиливать веб-формы до преимуществ MVC? Тем более для плавного перехода вы могли бы скомбинировать Forms и MVC.
        +2
        Это советы могут помоч тем, у кого уже есть готовый рабочий проект на WebForms и требуется за 2-3 дня его оптимизировать, а не тратить еще существенное количество времени на то, чтоб перевести на MVC
          0
          Попробую высказать «за»:
          — если уметь правильно готовить webforms, то можно достичь хорошей производительности;
          — часто разработчики переключаются с winforms на webforms и наоборот, что позволяет экономить время на освоение технологии (конечно, при этом первый пункт будет хромать без хорошего техлида);
          — большая библиотека контролов;
          — положительное мнение о технологии со стороны крупных заказчиков (как известно, мир enterprise внедряет новинки с некоторым запозданием);
          — проблемы с производительностью Razor.
            +2
            А можете подсказать какие существуют «проблемы с производительностью Razor»?
              0
              Посмотрел дату источника — сравнивали перформанс бета версии, в релизе подкрутили. Последний пункт из моего списка можно убрать.
            0
            Потому что mvc это не следующая, более крутая версия asp.net worms. Это взаимодополняющие инструменты под разные задачи. Хотя и микроскопом можно гвозди забивать…
            +8
            Отключите ViewState, не используйте стандартные контролы, не используйте ScriptManager и UpdatePanel — это конечно хорошо. А что остается в итоге? зачем тогда вообще WebForms здесь?

            Если у вас старое приложение, то можно подключить MVC и делать новые страницы на нем.
              +2
              Действительно, статья скорее по теме «Как не использовать webforms». Все фишки, которые мы отключаем призваны упростить работу девелопера в ущерб скорости и объема. Пишете Highload — не используется вебформы.
                0
                Статья для тех, у кого есть решение на Web Forms и перевод на MVC пока не планируется либо намечен на будущее. Думаю в разных компаниях могут быть абсолютно разные сложности с быстрым переходом на MVC. Думаю для Web Forms разработчиков самое оно, чтобы лишний раз увидеть как другие разработчики добились хорошего результата, не выкидывая половину что писали и не начиная все заново.
                  0
                  Не находите, что 4 уровня if-ов как-бы слишком? Сложно было обойтись 2-мя?
                    0
                    Насколько я помню, в последнем фреймворке можно пользоваться Bundle, который сожмет вам все необходимые ресурсы в релизе. А что касается собственного модуля — чем он лучше стандартного динамического сжатия IIS? Тем, что можно использовать на shared хостингах?
                      0
                      Да, с шаредами как раз и была проблема, не везде настроено сжатие по-умолчанию
                      +1
                      Видим, что для вывода простого текста внутри UpdatePanel потребовалось 2 кб html и еще почти 100 кб скриптов.

                      И насколько это много? На серьезных проектах обычно полно разметки и скриптов целая пачка. Решается клиентским кешированим и сжатием скриптов.

                      ViewState. Однако в большинстве случаев без него можно обойтись.

                      Это стандартный механизм для работы с состоянием клиентского вью. Лучше там нет. Какой выигрыш дает отказ от него? Эта игра действительно стоит свеч?

                      при разработке контролов для ASP.NET не особо задумывались о чистоте и оптимальности верстки, количестве запросов и скорости работы этих самых контролов.

                      Еще как задумывались. Да — генеренная верстка там не оптимальна — но механизм позволяет абстрагироваться от от html и рассматривать его в ряде случаев как некий веб ассемблер. Стандартные контролы удобны в использовании если уметь их готовить. Проблема только в производительности — но она решается отдельными механизмами. Уходить от них опасно — придется смешивать разные стили разработки, подружить разные инструменты — дополнительные риски.

                      По минимизации javascript и сжатию html снимаю шляпу. Это правильно — и это стандарт разработки под asp.net.

                      Хотелось бы увидеть результаты тестирования производительности продукта. Без цифр невозможно оценить стоит ли использовать значительную часть из приведенных вами механизмов.
                        0
                        При любом событии внутри UpdatePanel происходит PostBack и выполняются все события страницы, в которых могут происходить трудные вычисления или запросы к базе данных.

                        Это решается серверным кешированием в сессию по моему. Вроде так и задумывался механизм — нет?
                          0
                          Кеширование в сессию вызывает лишнюю сериализацию и при большом количестве пользователей сессия будет очень разрастаться. Да еще придется делать механизм поддержки актуальной информации в сессии. И вообще, кеширование — это последнее средство по оптимизации, лучше разработать правильную архитектуру, чем пытаться дыры в производительности затыкать кешем.

                          0
                          Это стандартный механизм для работы с состоянием клиентского вью. Лучше там нет. Какой выигрыш дает отказ от него?

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

                        Only users with full accounts can post comments. Log in, please.