Статьи

Добавление сжатия Gzip в веб-приложение Clojure за 30 секунд

Как вы, наверное, видели , я работаю над новым веб-проектом , который предполагает доставку метрической тонны контента в браузер каждого пользователя при посещении основной части сайта. Мы говорим о чем-то вроде 1,5 МБ HTML, Javascript и CSS, и это после минимизации с максимальными усилиями и тому подобное.

Ясно, что Gzipping весь беспорядок необходим. Я никогда не работал на сайтах большого объема, которые требовали таких мер, так что это новое требование для меня. Бэкэнд сайта реализован в Clojure, поэтому мой первый инстинкт был в Google «gzip ring clojure» ( Ring — совершенно впечатляющий веб-фреймворк Clojure), после чего я нашел проект ring-gzip-middleware Майкла Стивенса . Кажется достаточно простым: обработчики запросов вызова — это просто функции, и вы можете применять промежуточное ПО тривиально через композицию функций, поэтому ring-gzip-middleware предоставляет функцию, которая оборачивает ваши собранные обработчики вызова для надлежащего сжатия исходящих ответов.

К счастью, я остановился в самый последний момент: в то время как связующее ПО Ring — чертовски хороший молоток, сжатие ответов Gzip не должно быть гвоздем в этом сценарии. Я не хотел, чтобы впредь мне приходилось читать полностью несвязанные биты, связанные с инфраструктурой, в моей кодовой базе, независимо от того, насколько элегантно они сложились. В понятии разделения интересов остается значение .

К счастью, веб-приложения Clojure — это веб-приложения Java, которые универсально развернуты в контейнере сервлетов, таких как Tomcat, Jetty, Glassfish и т. Д. Итак, у меня есть довольно вкусное меню на выбор.

Сжатие Gzip в контейнере

Большинство, если не все контейнеры сервлетов Java, обеспечивают готовое сжатие ответов Gzip. Например, Tomcat требует простого добавления пары атрибутов к элементу Connector в его файле server.xml . Jetty легко обеспечивает сжатие Gzip статических ресурсов через сервлет по умолчанию ; просто установите для параметра gzip init значение true в вашем файле web.xml.1 файл, и все готово:

<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
    <init-param>
        <param-name>gzip</param-name>
        <param-value>true</param-value>
    </init-param>
</servlet>

Я думаю, что вы можете пропустить элемент <servlet-class> там; Я не много экспериментировал на этом пути, потому что:

  1. Мне нужно было иметь возможность сжать динамически сгенерированный контент, и
  2. Я использую Jetty в своей среде разработки, но развертываю в Tomcat на производстве, поэтому мне нужно универсальное решение.

Итак, что я считаю идеальным подходом:

Gzip Servlet Filters

Фильтры сервлетов — это независимые, компонуемые компоненты, которые могут динамически изменять запросы и ответы — следствие Java около 1999 года для промежуточного программного обеспечения Ring. Разница в значительной степени заключается в упаковке и контексте: хотя промежуточное ПО Ring — это просто функция, которую можно программно складывать в кодовую базу, фильтры сервлетов задаются статически как часть файла web.xml приложения и, как правило, не изменяются внутри сервлета. во время выполнения. (Все может стать интересным, когда вы внедрите фильтры сервлетов с помощью Clojure, но, возможно, это тема для другого поста.)

Существует множество реализаций фильтра сервлетов сжатия Gzip, в том числе один очень плохой пример, появившийся в каком-то журнале в 2004 году, с неоправданно большим количеством сока Google, связанного с ним по некоторым причинам. Используйте любой из них, и ваше веб-приложение Clojure будет готово к Gzip, независимо от того, в каком контейнере сервлета вы развертываете. За мои деньги лучший из них предоставлен проектом Jetty , просто потому, что невозможно спорить с его происхождением, учитывая, что Jetty используется повсеместно : если бы была проблема с его реализацией фильтра сервлетов Gzip, она, безусловно, была бы обнаружена. сейчас

Используя это торт; добавьте соответствующую зависимость в ваш файл pom.xml:

<dependency>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jetty-util</artifactId>
    <version>6.1.26</version>
</dependency>

и добавьте его в свой файл web.xml с соответствующим отображением URL:

<filter>
    <filter-name>jetty-gzip</filter-name>
    <filter-class>org.mortbay.servlet.GzipFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>jetty-gzip</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Готово.

Все мое содержимое теперь сжато Gzip, как динамически генерируемое, так и статическое (потому что я всегда использую сервлет контейнера сервлета по умолчанию для обслуживания статических ресурсов). Мне не нужно было вносить какие-либо изменения в мою кодовую базу, и мне ни разу не напомнят о сжатии Gzip, когда я возлюсь с обработчиками Ring.

Фильтр Jetty Gzip имеет различные опции для настройки того, какие типы и размеры MIME должны быть включены, и для исключения определенных пользовательских агентов от получения содержимого Gzipped, но пока я просто оставлю значения по умолчанию в одиночку (то есть сжимаю все для всех).

Постскриптум: подождите, а что со всем XML в этом «веб-приложении Clojure»?

Некоторые люди имеют аллергию на скобки; у некоторых аллергия на XML; Я решил найти мир в обоих случаях. 🙂

Этот фильтр Jetty Gzip хорошо выполняет то, о чем я не хочу думать. Так же как ядро против контекстаполезная структура в делах бизнеса , я думаю, что это удобно, когда вы думаете о том, как подходить к разработке программного обеспечения. Я не получаю баллы за то, что у меня есть «чистый Clojure» стек для моего веб-приложения, особенно если мне нужно отойти от разумного разделения интересов или разобраться с FUD, который всплывает в моей голове о том, является ли повторная реализация фундаментально товарных операций действительно соответствует спецификациям или нет — вполне может быть, но просто нет никакой выгоды, если этот выбор будет благоприятным, если есть более безопасные альтернативы для таких контекстных вопросов.

Таким образом, я выбрал ~ 5-летний Gzip-фильтр от Jetty, точно так же, как я часто использую скучно надежные библиотеки Java-land (например, Spring Security) и инструменты (например, Maven и Eclipse) для поддержки гораздо более интересных вещей, для которых Я использую передовой набор, как Clojure.

Сноски
  1. Вы знаете, что такое файл web.xml, верно? Каждое Java-приложение имеет одно, независимо от того, сгенерировано ли оно вашим процессом сборки или вы сами его создаете. Последнее, как правило, предпочтительнее IMO, просто потому, что вы можете воспользоваться всеми преимуществами, которые он открывает для вас. Вы можете прочитать общие сведения о файлах web.xml по всей сети; Здесь есть пример веб-проекта Clojure, который содержит простой пример, и я немного о нем расскажу в своем посте и скринкасте здесь .

 

С http://cemerick.com/2011/04/22/adding-gzip-compression-to-a-clojure-webapp-in-30-seconds/