Каков наилучший способ GZIP страниц веб-приложения JSF-Seam
Я занимаюсь разработкой веб-приложения JSF на Tomcat, планируя использовать Seam в ближайшем будущем, и я хочу добавить сжатие наших веб-страниц и ресурсов (например, файлов Javascript и CSS). Мне известны три метода ответов GZIP в сети Java:
Используйте фильтр Ehcache GZIP: он используется в Appfuse, поэтому он, вероятно, надежен и проверяет, поддерживает ли пользовательский агент GZIP, прежде чем применять его, но, похоже, у него есть проблемы с Seam, который мы будем использовать http://seamframework.org/Community/EHCacheGZipFilterIncompatibleWithSeam.
Используйте pjl-фильтр. Из вопроса stackru: Сжатие Tomcat не добавляет Content-Encoding: gzip в заголовок, похоже, что у него нет утечек памяти, но я не знаю, есть ли у него проблемы с Seam или нет.
Используйте встроенное сжатие Tomcat - хотя оно может не обеспечивать кодировку содержимого (кажется, что Tomcat 6.0.14 работает нормально, но вы можете предоставить только черный список того, к чему сжатие пользовательских агентов не должно применяться.
У кого-нибудь есть опыт работы с этими методами в среде JSF-Seam? Какое "лучшее" решение?
Спасибо Глен
8 ответов
Фильтр GZIP значительно сократит время начальной загрузки.
Вы можете дополнительно внедрить cacheFilter, чтобы повысить производительность ваших экранов по сравнению с пользовательским интерфейсом на основе JavaScript ( /questions/5768340/filtr-staticheskih-resursov-jsf-cache/5768370#5768370).
Для клиентских компонентов вы можете использовать Primefaces, который представляет собой пользовательский интерфейс на основе JQuery.
Включить фильтр GZIP в JSF
Просто добавьте это к вашему
web.xml
<filter>
<filter-name>gzipResponseFilter</filter-name>
<filter-class>org.omnifaces.filter.GzipResponseFilter</filter-class>
<init-param>
<description>The threshold size in bytes. Must be a number between 0 and 9999. Defaults to 150.</description>
<param-name>threshold</param-name>
<param-value>150</param-value>
</init-param>
<init-param>
<description>The mimetypes which needs to be compressed. Must be a commaseparated string. Defaults to the below values.</description>
<param-name>mimetypes</param-name>
<param-value>
text/plain, text/html, text/xml, text/css, text/javascript, text/csv, text/rtf,
application/xml, application/xhtml+xml, application/x-javascript, application/javascript, application/json,
image/svg+xml, image/gif, application/x-font-woff, application/font-woff2, image/png
</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>gzipResponseFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/</location>
</error-page>
И следующее к вашему
pom.xml
<dependency>
<groupId>org.omnifaces</groupId>
<artifactId>omnifaces</artifactId>
<version>1.11</version>
</dependency>
Как проверить, использует ли мой экран gzip
Чтобы увидеть, что ваш контент уже использует gzip и кеш, в браузере Google Chrome -> щелкните правой кнопкой мыши на экране -> осмотрите -> щелкните вкладку сети -> обновите экран. Нажмите на изображения, значки, таблицы стилей и посмотрите, видите ли вы следующее в заголовке ответа
Content-Encoding:gzip
если статус элемента 200
Мы используем JBoss Seam
на JBoss AS
прокси и балансировка нагрузки позади apache
+ mod_proxy_ajp
с mod_deflate
сжимать исходящий трафик.
Преимущества этой настройки
- много примеров в сети
- простая настройка / отладка с учетом особенностей различных пользовательских агентов
- изменение конфигурации во время выполнения (
apachectl graceful
вместо перезапуска webapp/tomcat, чтобы отразить изменения).
Используйте встроенное сжатие Tomcat - хотя оно может не обеспечивать кодировку содержимого (кажется, что Tomcat 6.0.14 работает нормально, но вы можете предоставить только черный список того, к чему сжатие пользовательских агентов не должно применяться.
Я думаю, что вы неверно истолковали проблему, обнаруженную в сжатии Tomcat, не добавляет кодировку содержимого: gzip в заголовок. Эта проблема вызвана использованием Apache HTTPD с mod_jk перед Tomcat, который, в свою очередь, плохо настроен, чтобы не отправлять Content-Encoding
заголовок обратно от Tomcat. Эта проблема не связана с самим Tomcat. Tomcat отлично справляется со своей работой.
Я бы сказал, просто продолжайте встроенное сжатие Tomcat. Это так же просто, как добавить compression="on"
приписать к соединителю HTTP в server.xml
, У вас есть рядом с noCompressionUserAgents
установка также compressableMimeType
установка. Прочитайте документацию по соединителю HTTP.
Как насчет добавления nginx
front-end и позволяющий ему делать сжатие (и кеширование)?
В этом случае принадлежит на серверефалут:)
Я доволен фильтром EhCache после некоторого взлома. Вот как это работает:
package myapp;
import net.sf.ehcache.constructs.web.GenericResponseWrapper;
import net.sf.ehcache.constructs.web.ResponseUtil;
import static org.jboss.seam.ScopeType.STATELESS;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.annotations.web.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPOutputStream;
/**
* Zip content before sending to the browser.
*
*
*/
@Name("gzipFilter")
@Scope(STATELESS)
@BypassInterceptors
@Filter(around = "org.jboss.seam.web.ajax4jsfFilterInstantiator")
public class GzipFilter extends net.sf.ehcache.constructs.web.filter.Filter
{
private static final Logger LOG = Logger.getLogger(GzipFilter.class.getName());
/**
* Performs initialisation.
*
* @param filterConfig config
*/
protected void doInit(FilterConfig filterConfig) throws Exception
{
//nothing required.
}
/**
* A template method that performs any Filter specific destruction tasks.
* Called from {@link #destroy()}
*/
protected void doDestroy()
{
//noop
}
/**
* Performs the filtering for a request.
*/
protected void doFilter(final HttpServletRequest request, final HttpServletResponse response,
final FilterChain chain) throws Exception
{
if (!isDocStore(request) && !isIncluded(request) && acceptsEncoding(request, "gzip"))
{
// Client accepts zipped content
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(request.getRequestURL() + ". Writing with gzip compression");
}
// Create a gzip stream
final ByteArrayOutputStream compressed = new ByteArrayOutputStream();
final GZIPOutputStream gzout = new GZIPOutputStream(compressed);
// Handle the request
final GenericResponseWrapper wrapper = new GenericResponseWrapper(response, gzout);
chain.doFilter(request, wrapper);
wrapper.flush();
gzout.close();
//return on error or redirect code, because response is already committed
int statusCode = wrapper.getStatus();
if (statusCode != HttpServletResponse.SC_OK)
{
return;
}
//Saneness checks
byte[] compressedBytes = compressed.toByteArray();
boolean shouldGzippedBodyBeZero = ResponseUtil.shouldGzippedBodyBeZero(compressedBytes, request);
boolean shouldBodyBeZero = ResponseUtil.shouldBodyBeZero(request, wrapper.getStatus());
if (shouldGzippedBodyBeZero || shouldBodyBeZero)
{
compressedBytes = new byte[0];
}
// Write the zipped body
//ResponseUtil.addGzipHeader(response);
response.setHeader("Content-Encoding", "gzip");
response.setContentLength(compressedBytes.length);
response.getOutputStream().write(compressedBytes);
} else
{
// Client does not accept zipped content - don't bother zipping
if (LOG.isLoggable(Level.FINE))
{
LOG.fine(request.getRequestURL()
+ ". Writing without gzip compression because the request does not accept gzip.");
}
chain.doFilter(request, response);
}
}
/**
* Checks if the request uri is an include.
* These cannot be gzipped.
*
* @param request the request
* @return true if included
*/
private boolean isIncluded(final HttpServletRequest request)
{
final String uri = (String) request.getAttribute("javax.servlet.include.request_uri");
final boolean includeRequest = !(uri == null);
if (includeRequest && LOG.isLoggable(Level.FINE))
{
LOG.fine(request.getRequestURL() + " resulted in an include request. This is unusable, because" +
"the response will be assembled into the overrall response. Not gzipping.");
}
return includeRequest;
}
private boolean isDocStore(final HttpServletRequest request)
{
return request.getRequestURI().indexOf("/docstore/") > 0;
}
/**
* Determine whether the user agent accepts GZIP encoding. This feature is part of HTTP1.1.
* If a browser accepts GZIP encoding it will advertise this by including in its HTTP header:
* <p/>
* <code>
* Accept-Encoding: gzip
* </code>
* <p/>
* Requests which do not accept GZIP encoding fall into the following categories:
* <ul>
* <li>Old browsers, notably IE 5 on Macintosh.
* <li>Internet Explorer through a proxy. By default HTTP1.1 is enabled but disabled when going
* through a proxy. 90% of non gzip requests seen on the Internet are caused by this.
* </ul>
* As of September 2004, about 34% of Internet requests do not accept GZIP encoding.
*
* @param request the request
* @return true, if the User Agent request accepts GZIP encoding
*/
protected boolean acceptsGzipEncoding(HttpServletRequest request)
{
return acceptsEncoding(request, "gzip");
}
}
Я попробовал фильтр сервлетов, чтобы добавить сжатие GZIP (но не Ehcache), и не смог заставить его работать должным образом. Я поставил Apache с mod_jk перед сервером приложений. Все, что мне потребовалось, - это несколько минут для настройки сжатия GIP, и я также чувствую себя намного более защищенным, так как открыто только одно приложение, а не весь сервер приложений.
Альтернативный фильтр сервлетов можно найти здесь:
http://onjava.com/pub/a/onjava/2003/11/19/filters.html
Как и Ehcache, он проверяет, поддерживает ли его браузер. Я не могу сказать категорически, хорошо ли это сочетается с Seam, но я использовал его в прошлом без проблем.