wro4jでリソースをminifyし、Jettyでレスポンスをgzip化して通信する。

スマホ向けのブラウザサービスは、モバイル回線で画像などのリソースを大量にやりとりするので、通信のもたつきを感じることが多いです。

ある時、さすがに読み込みが遅いと感じて、JavaScriptとCSSのリソースの容量を確認したら、11ファイルで742KBになっていました。

そこで、JavaScriptとCSSを圧縮することにしました。

wro4jで全てのファイルをminifyする。

まずは、よくminifyと呼ばれることをしました。

minifyとは、JavaScriptやCSSの動作に影響を与えない範囲で、不要なコメント文や空白文字を除去したり、変数名の文字数を減らしたりする処理です。

Javaには、wro4jと呼ばれるリソース圧縮ツールがあるので、これを使いました。

スクリーンショット 2014-04-01 22.31.17

wro4j – Web Resource Optimizer for Java – wro4j – Google Project Hosting

wro4jは、Mavenプラグインとして導入し、ビルド時にリソースの圧縮をかけられるようにしました。

<plugin>
	<groupId>ro.isdc.wro4j</groupId>
	<artifactId>wro4j-maven-plugin</artifactId>
	<version>1.7.0</version>
	<configuration>
		<minimize>true</minimize>
		<cssDestinationFolder>${project.basedir}/src/main/webapp/css/</cssDestinationFolder>
		<jsDestinationFolder>${project.basedir}/src/main/webapp/js/</jsDestinationFolder>
		<contextFolder>${project.basedir}/src/main/webapp/</contextFolder>
		<wroFile>${project.basedir}/src/main/webapp/WEB-INF/wro.xml</wroFile>
		<wroManagerFactory>ro.isdc.wro.extensions.manager.standalone.GoogleStandaloneManagerFactory</wroManagerFactory>
	</configuration>
	<executions>
		<execution>
			<phase>compile</phase>
			<goals>
				<goal>run</goal>
			</goals>
		</execution>
	</executions>
</plugin>

JavaScriptとCSSは、wro4jによって複数ファイルの結合とminifyをすることが可能です。どのファイルを圧縮するかの設定は、wro.xmlという設定ファイルに、たとえば次のように書きます。

<?xml version="1.0" encoding="UTF-8"?>
<groups xmlns="http://www.isdc.ro/wro" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.isdc.ro/wro wro.xsd">
	<group name="main">
		<js>/js/templates.js</js>
		<js>/js/app.js</js>
	</group>
</groups>

これだけでJavaScriptなどは、20〜30%くらいは容量を削減することができます。

さらにJettyの設定でレスポンスをgzip化する。

CSSは、minifyの効果があまり大きくありません。なぜなら、類似のスタイルをまとめる構文がないので、必然的に反復記述を減らせないためです。

そこで更に、HTTPの通信の際に、レスポンスボディを丸ごとgzipで圧縮します。

サーブレットコンテナにJettyを使っている場合の例です。

スクリーンショット 2014-04-01 22.32.42

Jetty – Servlet Engine and Http Server
jsファイル、cssファイルのマッピングを org.eclipse.jetty.servlet.DefaultServlet にし、gzipパラメータをtrueにすることでgzip転送を利用できるようになります。

	<servlet>
		<servlet-name>gzipServlet</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-mapping>
		<servlet-name>gzipServlet</servlet-name>
		<url-pattern>/js/*</url-pattern>
		<url-pattern>/css/*</url-pattern>
	</servlet-mapping>

意外と単純です。

事前にgzipファイルを用意しておく必要がある

このJettyのサーブレットによる、gzip転送は、HTTPのリクエストをされるごとにgzip圧縮をするわけではありません。

そのため事前に、非圧縮のファイルとgzip圧縮されたファイルを用意しておく必要があります。

$ ls ./js/
app.js		main.js		main.js.gz	templates.js

こんな風に、main.jsと並列にmain.js.gzを配置しておくことで、gzipされたファイルをレスポンスしてくれます。

ちなみに、両方置いておく必要がある理由は単純です。gzip圧縮による通信に対応していないブラウザが存在するためです。

HTTP通信の際には、gzip圧縮による通信ができるかをリクエストヘッダに添えるので、これを見て、main.js.gzを返すのか、main.jsを返すのかを判断します。

GET / HTTP/1.1
Host: example.com
...
Accept-Encoding: gzip, deflate
...

gzip圧縮されたファイルが用意されていない場合は、ヘッダに関わらず非圧縮のファイルが返されます。

結果、79%も削減できた。

minifyとgzip圧縮によって、容量を742KBから159KBに79%も削減することができました!

モバイルの低速回線だとかなりの体感の差になると思います。

About katty0324

Scroll To Top