asp.net: gzip, несколько вариантов включения

    Сжатие web-содержимого посредством gzip (GNU zip) – это довольно старая технология. Суть ее сводится к тому, что web-содержимое перед отправкой пользователю сжимается по известному всем алгоритму zip. Сама спецификация gzip описана в RFC1952, версия 4.2 которой датируется маем 1996 года. На сегодняшний день все популярные браузеры и веб-серверы поддерживают сжатие web-содержимого посредством gzip. В этой статье я постараюсь рассказать о нескольких способах включения в проекты asp.net поддержки gzip-сжатия.

    Включение HTTP-compression на сервере IIS


    Первый способ самый простой. Вы просто нажимаете пару кнопок в настройках IIS, и вот уже web-сервер обеспечивает вам автоматическое сжатие для клиентов, которые gzip поддерживают.
    Для тех, кто не знает, как в IIS включить сжатие, рассказываю: необходимо в диспетчере служб IIS зайти в свойства элемента «Веб узлы» и перейти во вкладку «Служба».



    Данный способ самый простой, но вместе с тем не самый гибкий.
    Плюсы такого включения сжатия:
    • простота;
    • поддержка сжатия IIS статических файлов;
    • поддержка кэширования сжатых файлов;
    • не требует написания кода.

    Минусы включения поддержки сжатия на сервере IIS:
    • сервер решает: вы не будете знать что, когда и как сжимается;
    • глобальное включение: сжатие включается для всей службы разом и будет влиять на все узлы или виртуальные каталоги вашего сервера (по крайне мере, через gui отключить сжатие у конкретного узла нельзя);
    • проблемы: лично я столкнулся с проблемой, когда один из пользователей пожаловался на то, что у него стала отображаться пустая главная страница как раз после включения сжатия через IIS, после выключения все вернулось в норму. Данный момент скорее из разряда частных и к общему случаю не подходит, но я привел его, как пример того, почему глобальное включение сжатие в IIS может вас не устроить.

    Application_BeginRequest


    Суть этого метода, описанного в интеренете во многих источниках, например, тут

    www.stardeveloper.com/articles/display.html?article=2007110401&page=1

    состоит в том, что вы, используя, появившиеся в .net framework 2.0 классы System.IO.Compression.GZipStream и System.IO.Compression.DeflateStream определяете свой собственный фильтр содержимого http-запроса, который перед отправкой клиенту, сжимает данные посредством методов GZipStream. Особенность этого метода в том, что все действия по сжатию содержимого производятся в global.asax в методе Application_BeginRequest, который вызывается перед запросом пользователя. Тем самым можно создать фильтр на любой запрос пользователя для отправки всего содержимого Response в сжатом виде. Вот как этот метод выглядит в исходном виде (взято с сайта указанного выше):
    <%@ Application Language=«C#» %>
    <%@ Import Namespace=«System.IO» %>
    <%@ Import Namespace=«System.IO.Compression» %>

    <script runat=«server»>
    void Application_BeginRequest(object sender, EventArgs e)
    {
      HttpApplication app = (HttpApplication)sender;
      string acceptEncoding = app.Request.Headers[«Accept-Encoding»];
      Stream prevUncompressedStream = app.Response.Filter;

      if (acceptEncoding == null || acceptEncoding.Length == 0)
        return;

      acceptEncoding = acceptEncoding.ToLower();

      if (acceptEncoding.Contains(«gzip»))
      {
        // gzip
        app.Response.Filter = new GZipStream(prevUncompressedStream,
          CompressionMode.Compress);
        app.Response.AppendHeader(«Content-Encoding»,
          «gzip»);
      }
      else if (acceptEncoding.Contains(«deflate»))
      {
        // defalte
        app.Response.Filter = new DeflateStream(prevUncompressedStream,
          CompressionMode.Compress);
        app.Response.AppendHeader(«Content-Encoding»,
          «deflate»);
      }
    }
    </script>
    * This source code was highlighted with Source Code Highlighter.


    Основные плюсы данного метода:
    • расположение в global.asax позволяет решить проблему централизованно, разом разрешив сжатие для всего потока данных в контексте одного web-приложения asp.net;
    • сжимается практически весь контент;
    • немного переделав этот код, можно гибко фильтровать содержимое для сжатия.
    Минусы данного метода:
    • способ определяет правило для всего приложения, нет возможности отключить сжатие для ряда страниц;
    • данный метод некорректно работает с ajax.net, что вообще не позволяет использовать его в ajax-ориентированном приложении;
    • по сравнению с первым методом через IIS, здесь приходится писать код и сопровождать его.

    Надо заметить, что проблема с ajax.net возможно имеет решение, но мне на момент написания статьи такое решение не известно. Я буду благодарен, если кто-нибудь опишет реализацию данного способа включения gzip в ajax.net проектах.

    GZipEncodePage


    Отличный специалист Rick Strahl на своем блоге

    west-wind.com/WebLog/default.aspx

    приводит еще один, альтернативный способ сжатия web-страниц посредство gzip. Рассмотрим пару функций которые он написал, а я сделал статическими:
        public static bool IsGZipSupported()
        {
          string AcceptEncoding = HttpContext.Current.Request.Headers[«Accept-Encoding»];
          if (!string.IsNullOrEmpty(AcceptEncoding) &&
             (AcceptEncoding.Contains(«gzip») || AcceptEncoding.Contains(«deflate»)))
            return true;
          return false;
        }

        public static void GZipEncodePage()
        {
          if (IsGZipSupported())
          {
            HttpResponse Response = HttpContext.Current.Response;

            string AcceptEncoding = HttpContext.Current.Request.Headers[«Accept-Encoding»];
            if (AcceptEncoding.Contains(«gzip»))
            {
              Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
                           System.IO.Compression.CompressionMode.Compress);
              Response.AppendHeader(«Content-Encoding», «gzip»);
            }
            else
            {
              Response.Filter = new System.IO.Compression.DeflateStream(Response.Filter,
                           System.IO.Compression.CompressionMode.Compress);
              Response.AppendHeader(«Content-Encoding», «deflate»);
            }
          }
        }
    * This source code was highlighted with Source Code Highlighter.


    Первый метод просто проверяет поддержку клиентом технологии сжатия, а вот второй очевидно практически равносилен методу из примера о Application_BeginRequest, только в данном случае вынесен в отдельную функцию. Что это дает? Ну, во-первых, рассмотрим использование:
    protected void Page_Load(object sender, EventArgs e)
    {
      HtmlUtil.GZipEncodePage();
    }
    * This source code was highlighted with Source Code Highlighter.


    Очевидно, что данный метод работает на уровне пользовательских страниц. Работает только с телом страницы и не обрабатывает js,css и любые другие файлы.

    Плюсы этого подхода:
    • работает на уровне одной страницы, позволяет включать сжатие только на тех страницах, где это требуется;
    • не сжимает ничего кроме тела страницы, тем самым не конфликтуя с ajax.net.
    Минусы:
    • нет возможности разом включить сжатие везде на сайте;
    • не сжимает js, css и другие ресурсы;
    • необходимо писать код и поддерживать его.

    Очень важно, при использовании этого метода вызывать его до любой первой записи в Response.

    Послесловие


    Лично я остановился на третьем варианте. Во-первых, часть моего проекта написана на ajax.net и второй вариант у меня не заработал. Во-вторых, я столкнулся с проблемой при включении централизованного сжатия на уровне IIS, следовательно, и первый вариант мне не подошел. В-третьих, третий подход лично мне кажется элегантным и гибким. Для сжатия js и css можно поискать и другие способы, а сжатие одних только aspx уже дает заметный выигрыш в размере трафика.

    Приведу некоторые данные:
    • по моим измерениям размер данных страниц передаваемых клиенту при сжатии через gzip уменьшился в среднем в шесть раз;
    • размер ajax-ответов клиенту уменьшился в среднем в десять раз;
    • увеличение нагрузки на процессор на сервере не берусь оценить, но в интернете ее оценивают как ~5%, что для себя считаю вполне допустимым.

    Для себя делаю вывод, что сжатие одних только страниц позволяет значительно уменьшить размер передаваемых данных, а для ajax-ориентированного проекта бонусом пойдет еще и уменьшение размера всех ajax-данных передаваемых клиенту.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

      –9
      Все ваши проблемы решаются очень просто: используйте Apache. :) Тогда сами сможете выбирать, что и как сжимать. И даже выключать сжатие контента для посетителей со старыми браузерами, которые сжатие не поддерживают.
        0
        Вы немного неумный.
        0
        Спасибо огромное. Замечательная статья. К сожалению, не могу плюсануть.
          0
          У меня второй способ работал со странностями. На сервере для разработки все работало, но после переноса приложения на сервер хостера (Parking) сжатие само-собой отключилось. Причем, предположение, что сжатие не работает из-за того, что сервер съедает заголовки, отпало, так как после удаления условий проблема осталась. А их техподдержка внятного ответа так и не дала.
          Надо будет попробовать третий вариант (первый, сами понимаете, недоступен), хотя, по сути-то он ничем не отличается.

          Кстати, можно третий вариант реализовать по-другому: сделать какой-нибудь GZipCompressedPage : Page и переопределить в нем, скажем, метод ProcessRequest, где и будет подключаться сжатие. Так мы избавимся от:
          1). лишнего кода в Page_Load()
          2). гарантируем, что в Response никто ничего не запишет (ProcessRequest вызывается до всех событий)
          3). Можем подключать сжатие, просто изменяя в Codebehind класс-родитель, не меняя код.
            +1
            Хм. Единственное что могу предположить, что по каким-то причинам в вашем случае Application_BeginRequest просто не выполнялся. Или проверяли? Других вариантов пока даже не представляю...

            Да, хороший вариант, предлагаю попробывать и выложить сюда в виде статьи, заодно поделитесь заработало ли на паркинге?
              0
              проверял, в том-то и дело, что все выполнялось. Правда, код вызывался не по BeginRequest, а по PreSendRequestHeaders, но сомневаюсь, что это меняет дело.

              Статью может попробую на выходных написать, если получится. У меня сейчас одна большая статья под названием «диплом» пишется, плюс еще работать надо :)
            +1
            Использую сжатие первым способом, но ещё с правкой метабазы IIS. Настраивается все достаточно гибко, если знать, что крутить, так же можно и для отдельных сайтов задавать настройки. В общем отдал предпочтение IIS для этой задачи, пока не жалуюсь, да и при размещении новых сайтов не задумываешься о том, что нужно что-то ещё прикрутить.
              0
              Да, я наверняка тоже бы остановился бы на первом варианте и не искал бы ничего другого, но обстоятельства так сложились, что пришлось покопаться.
                +1
                Тоже хотел добавить, что можно еще пошаманить с настройками в IIS.
                Плюс ко всему еще есть неплохой уже реализованный модуль - одно время я им пользовался.
                http://www.codeproject.com/KB/aspnet/htt…
                  0
                  похоже, хорошая вещь, спасибо, обязательно покручу
                  а теперь чем пользуешься?
                0
                хорошо пишете, и тема злободневная)
                  0
                  Статья, как минимум, интересна, ну а максимум решает каждый для себя.
                    0
                    Извиняюсь, за дабл-коммент. Позже перешел на blowery (http://www.blowery.org/code/HttpCompress…).
                    Ну и пример реализации с помощью HttpModule (http://www.jigar.net/articles/viewhtmlco…)
                      0
                      а тебе не было бы интересно написать статейку по этим двум компонентам?
                      я бы сам с удовольствием почитал
                        0
                        В личке обсудим :)
                        0
                        У blowery ошибки есть, работу с хедерами переписать пришлось. А в общем, достаточно успешно используем модифицированную версию в нескольких проектах.
                          0
                          Спасибо большое за ссылки, мне понравился второй вариант - реализация через HttpModule. Возник один вопрос - сжатие CSS и JS файлов. В web.config добавил еще 2 ContentTypes: Value="application/x-javascript" и Value="text/css". При дебаге в студии сжатие происходит на ура, когда стараюсь работать через IIS, сжимает только text/html. Возникали ли такие проблемы у вас или нет?
                            0
                            Ну так конечно :)
                            ASP.NET обрабатывает только свои расширения - aspx, axd, asmx и прочее.
                            Надо в IIS выставить, чтобы js и css обрабатывались asp.net-ом. Вот только есть ли смысл?
                            IIS сам умеет сжимать статику.
                            А если у вас динамические скрипты, то их лучше отдавать через *.axd.
                        • НЛО прилетело и опубликовало эту надпись здесь
                            0
                            А Вы уверены, что под lighthttpd можно запустить asp.net?
                            • НЛО прилетело и опубликовало эту надпись здесь
                                0
                                Я уверен, что этого делать не нужно.
                                • НЛО прилетело и опубликовало эту надпись здесь
                              0
                              Вообще это пример «интеграции от которой невозможно отказаться» :) Выбирая отличный .Net, Вы вынуждены использовать Windows (некоторые не очень видят его на сервере, хотя бы из-за клиентский червей) и IIS (который сильно проигрывает другим веб-серверам).
                                0
                                Это к евангелистам .Net. Они обратят кого угодно.
                              0
                              Эм.. А в современном приложении вы много насчитаете статических элементов, которые "нада" сжимать? CSS + JS? Сколько на этом можно сэкономить, если эти элементы зачатую кешируются для всех страниц сайта и грузятся по сути вообще один раз.

                              Нормально динамический контент жмется, не у всех выделенки, поэтому это достаточно актуально. И на выделенке в общем-то оно все равно быстрее грузится сжатым. Да и на хабре уже несколько раз обсуждалось на реальных тестах полезность сжатия.
                              • НЛО прилетело и опубликовало эту надпись здесь
                                  +1
                                  Я не отрицаю, что CSS+JS нужно сжимать. IIS со статическим контентом поступает именно так как вы говорите, при первом обращении сжимает и ложит в директорию на диск.

                                  Хабр - это просто достаточно старая версия сайта. Возьмите ту же автокадабру. Там сжатие работает и очень даже никому не мешает, а только помогает?
                                  Для примера ещё несколько "посещаемых" ресурсов: google, корреспондент (поссылкам потыкайте, это не прямые на сайт, а на анализ использования компрессии в них).

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

                                  В общем как-то неубедительно это все звучит с вашей стороны. Больше чем уверен, что "раз в 10" — это только что придуманая цифра.
                              0
                              Кстати, я правильно понимаю, что если я хочу сжимать JS и CSS файлы, но только для одной папки, а не глобально, то я не смогу это сделать в IIS?
                                0
                                Вообще.. не совсем ясно, зачем так делать. Однако, можно копать в сторону сохранения таких файлов в ресурсах сборки и настройки сжатия обращений *.axd для приложения. Но я могу ошибаться.
                                  0
                                  Кеширование стоит как раз только CSS и JS, потому что они не изменяются. Но вообще вопрос бы конечно же про гибкость настроек ;)
                                    0
                                    Я понял про то, что надо только статику. Не понял, про ", но только для одной папки, "/ А если хочется особой гибкости, так что мешает редактировать метабазу вручную? Вот там, как раз, много чего можно изменить да подкрутить.
                                  0
                                  Да, всё правильно. Стандартно IIS этого не делает.
                                  Мы используем Blowery.HttpCompress, и дополнительно прописываем ему что сжимать, а что нет (это делается в web.config)
                                  0
                                  Вопрос можно? Тестировали вы третий случай в ситуации использования Microsoft ISA Server? У меня почему-то в этом случае он показал свою полную неработоспособность и даже падение в ошибку при HttpContext.Current.Request.Headers[«Accept-Encoding»] (Request был пуст).

                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                  Самое читаемое