[Из песочницы] Как работает сжатие GZIP

imageВ жизни каждого мужчины наступает момент, когда трафик растёт и сервак умирает необходимо задуматься об оптимизации. В последнем дайджесте PHP (№ 40) была упомянута ссылкой статья «How GZIP Compression Works». Исходя из статистики, 56% веб-сайтов используют GZIP. Я надеюсь, эта статья раскроет перед читателем достоинства этой технологии.Даже в современном мире, со скоростным интернет соединением и неограниченными хранилищами информации, сжатие данных по-прежнему актуально, особенно для мобильных устройств и стран с медленным интернет-соединением. Этот пост описывает метод де-факто сжатия без потерь для сжатия текстовых данных на веб-сайтах: GZIP.GZIP compression GZIP обеспечивает сжатие без потерь, иными словами, исходные данные можно полностью восстановить при распаковке. Он основан на алгоритме DEFLATE, который использует комбинацию алгоритма LZ77 и алгоритма Хаффмана.Алгоритм LZ77 Алгоритм LZ77 заменяет повторные вхождения данных на «ссылки». Т.е. если в имеющихся данных какая-то цепочка элементов встречается более одного раза, то все последующие её вхождения заменяются «ссылками» на её первый экземпляр. Алгоритм прекрасно рассмотрен horror_x и описан здесь. Каждая такая ссылка имеет два значения: смещение и длина.Давайте рассмотрим пример:

Original text: «ServerGrove, the PHP hosting company, provides hosting solutions for PHP projects» (81 bytes)LZ77: «ServerGrove, the PHP hosting company, p<3,32>ides<9,26>solutions for<5,52><3,35>jects» (73 bytes, assuming that each reference is 3 bytes)

Как вы могли заметить, слова «hosting» и «PHP» повторяются, поэтому во второй раз, когда подстрока найдена, она будет заменена ссылкой. Есть и другие совпадения, такие как «er», но т.к. это незначительно (в данном случае — «er» отсутствует в других словах), остается оригинальный текст.

Кодирование Хаффмана Кодирование Хаффмана является методом кодирования с переменной длиной, которая назначает более короткие коды к более частым «символам». Проблема с переменной длиной кода, как правило в том, что нам нужен способ узнать, когда код закончился и начался новый, чтобы расшифровать его.Кодирование Хаффмана решает эту проблему, создав код префикса, где ни одно кодовое слово не является префиксом другого. Это может быть более понятно на примере:

>Original text: «ServerGrove«ASCII codification:»01010011 01100101 01110010 01110110 01100101 01110010 01000111 01110010 01101111 01110110 01100101» (88 bits)

ASCII представляет собой систему кодировки символов с фиксированной длиной, так что буква «е», которая повторяется три раза, а также является наиболее часто встречаемой буквой в английском языке, имеет такой же размер как буква «G», которая появляется только один раз. Используя эту статистическую информацию, Хаффман может создать наиболее оптимизированную систему Huffman:»1110 00 01 10 00 01 1111 01 110 10 00» (27 bits)

Метод Хаффмана позволяет нам получить более короткие коды для «e», «r» и «v», в то время как «S» и «G» получаются более длинными. Объяснения, как использовать метод Хаффмана, выходят за рамки этого поста, но если вы заинтересовались, я рекомендую вам ознакомиться с отличным видео на Computerphile (или статьей на Харбе).DEFLATE как алгоритм, который используется в GZIP сжатии, является комбинацией обоих этих алгоритмов.

Является ли GZIP лучшим метод сжатия? Ответ — нет. Есть другие методы, которые дают более высокие показатели сжатия, но существует несколько хороших причин использовать этот.Во-первых, даже при том что GZIP не самый лучший метод сжатия, он обеспечивает хороший компромисс между скоростью и степенью сжатия. Сжатие и распаковка у GZIP происходят быстро и степень сжатия на высоком уровне.Во-вторых, нелегко внедрить новый глобальный метод сжатия данных, который смогут использовать все. Браузерам потребуется обновление, что на сегодняшний день гораздо проще за счёт автообновления. Как бы то ни было, браузеры — не единственная проблема. Chromium пытался добавить поддержку BZIP2, более лучшего метода основанного на преобразовании Барроуза-Уилера, но от него пришлось отказаться, т.к. некоторые промежуточные прокси-серверы искажали данные, т.к. не могли распознать заголовки bzip2 и пытались обработать gzip контент. Баг-репорт доступен здесь.

GZIP + HTTP Процесс получения сжатого контента между клиентом (браузером) и сервером достаточно прост. Если у браузера есть поддержка GZIP/DEFLATE, он даёт серверу понять это благодаря заголовку «Accept-Encoding». Тогда, сервер может выбрать — отправлять содержимое в сжатом или оригинальном виде.54ee0a7284c53b8bd841551e189aad4d.png Реализация Спецификация DEFLATE обеспечивает некоторую свободу разработчикам реализовать алгоритм с использованием различных подходов, пока полученный поток совместим со спецификацией.GNU GZIP Реализация GNU является наиболее распространенной и была разработана, чтобы стать заменой для утилиты архивации, свободной от запатентованных алгоритмов. Чтобы сжать файл с помощью утилиты GNU GZIP: $ gzip -c file.txt > file.txt.gz

Существует 9 уровней сжатия, от »1» (самого быстрого с наименьшим коэффициентом сжатия) до »9» (самого медленного с лучшей степенью сжатия). По умолчанию, используется »6». Если вам необходимо максимальное сжатие за счёт использования большего объёма памяти и времени, используйте флаг »-9» (или »-best»): $ gzip -9 -c file.txt > file.txt.gz

7-zip 7-zip реализуется алгоритм DELFATE иначе и обычно архивирует с большим коэффициентом сжатия. Чтобы максимально сжать файл: 7z a -mx9 file.txt.gz file.txt

7-zip так же доступен для Windows и обеспечивает реализацию для других методов сжатия, таких как 7z, xz, bzip2, zip и прочих.Zopfli Zopfli идеально подходит для одноразового сжатия, например в ситуациях, когда файл единажды сжимается и многоразово используется. Он в 100 раз медленнее, но сжатие на 5% лучше, чем у других. Хабрапост.Включение GZIP Apache Модуль mod_deflate обеспечивает поддержку GZIP, так что ответ сервера сжимается на лету до его передачи клиенту через сеть. Чтобы включить сжатие текстовых файлов, необходимо дополнить .htaccess строками: AddOutputFilterByType DEFLATE text/plainAddOutputFilterByType DEFLATE text/htmlAddOutputFilterByType DEFLATE text/xmlAddOutputFilterByType DEFLATE text/cssAddOutputFilterByType DEFLATE application/xmlAddOutputFilterByType DEFLATE application/xhtml+xmlAddOutputFilterByType DEFLATE application/rss+xmlAddOutputFilterByType DEFLATE application/javascriptAddOutputFilterByType DEFLATE application/x-javascript

Существует несколько известных багов в некоторых версиях браузеров, по этому рекомендуется также добавить:

BrowserMatch ^Mozilla/4 gzip-only-text/htmlBrowserMatch ^Mozilla/4\.0[678] no-gzipBrowserMatch \bMSIE! no-gzip! gzip-only-text/htmlHeader append Vary User-Agent

Кроме того, можно использовать предварительно сжатые файлы вместо того, чтобы сжимать их каждый раз. Это особенно удобно для файлов, которые не меняются при каждом запросе, например CSS и JavaScript, которые могут быть сжаты с использованием медленных алгоритмов. Для этого:

RewriteEngine OnAddEncoding gzip .gzRewriteCond %{HTTP: Accept-encoding} gzipRewriteCond %{REQUEST_FILENAME}.gz -fRewriteRule ^(.*)$ $1.gz [QSA, L]

Это даёт Apache понять, что файлы с расширением .gz должны предоставляться сжатыми (линия 2), нужно проверить доступность принятия gzip браузером (линия 3), и если сжатый файл существует (линия 4), мы добавляет .gz для запрашиваемого файла.Nginx Модуль ngx_http_gzip_module позволяет сжимать файлы с помощью GZIP на лету, в то время как ngx_http_gzip_static_module позволяет отправлять предварительно сжатые файлы с ».gz» расширением вместо обычных.Пример конфигурации выглядит следующим образом: gzip on; gzip_min_length 1000; gzip_types text/plain application/xml;

GZIP + PHP Хотя обычно сжимать данные используя PHP не рекомендуется, так как это довольно медленно, сделать это можно, используя модуль zlib. Например, используем максимальное сжатие на библиотеке jQuery.min: $originalFile = __DIR__ . '/jquery-1.11.0.min.js'; $gzipFile = __DIR__ . '/jquery-1.11.0.min.js.gz'; $originalData = file_get_contents ($originalFile); $gzipData = gzencode ($originalData, 9); file_put_contents ($gzipFile, $gzipData); var_dump (filesize ($originalFile)); // int (96380) var_dump (filesize ($gzipFile)); // int (33305) Вместо вывода (примечание переводчика) Не смотря на то, как чесались руки добавить в статью автора собственные пояснения алгоритмов, статистику и результаты тестов сравнения, перевод осуществлён практически без вмешательств со стороны переводчика. Перевод статьи осуществлён с разрешения автора и портала ServerGrove.

© Habrahabr.ru