山姆鍋曾經提到, 影化身網站是放置在 Amazon S3 上,也利用 CloudFront 來減少存取的延遲。 但是,好還可以更好,山姆鍋在本文會分享如何減少 HTTP 要求數量,壓縮網頁內容來加快存取速度。

Amazon S3 雖然方便,但因為不是設計來支援動態內容,有幾個限制會影響到存取效率的最佳化 (優化),像是不支援執行時期 GZip encoding。雖然不支援動態壓縮,但是我們可以預先把內容做 GZip 壓縮來達到同樣效果。底下將說明如何透過 s3cmd 這個工具來達到減少 HTTP 請求數量以及資料量的目的。對了!請注意山姆鍋用的工作環境是 Mac OS X。

準備工具 s3cmd

本文使用的 s3cmd 版本需要是 1.1 以上。

安裝 s3cmd

到官方網站下載 < a href="http://s3tools.org/download" target="_blank" rel="noopener">s3cmd 並解壓縮後,執行下列指令來安裝:

1
$ python setup.py install

設定 s3cmd

要讓 s3cmd 能夠正確連到 Amazon S3 服務,需要做些設定。在命令列執行下列指令並安照提示輸入您 S3 的認證資訊:

1
$ s3cmd --configure

驗證 s3cmd 安裝完成

執行下列指令,確定 s3cmd 已經可以正確連到 Amazon S3:

1
2
3
$ s3cmd ls
2013-05-22 10:24 s3://aaa.eavatar.com
2013-06-05 02:30 s3://blog.eavatar.com

如果有看到之前建好的 bucket 就表示安裝設定正常。

預先對 HTML, JS, 以及 CSS 檔案做 GZip 壓縮

之前提過 Amazon S3 不支援動態做檔案的 GZip 壓縮,但我們可以預先壓好檔案在上傳到 S3。除了預先壓縮外,還要告訴 S3,這些壓縮過的檔案該使用的 HTTP 標頭。

設定壓縮腳本

在 Rakefile 中,加入下列腳本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
desc "GZip HTML"
task :gzip_html do
puts "## GZipping HTML"
system 'find public/ -type f -name \*.html -exec gzip -9 {} \;'
# Batch rename .html.gz to .html
Dir['**/*.html.gz'].each do |f|
test(?f, f) and File.rename(f, f.gsub(/\.html\.gz/, '.html'))
end
end

desc "GZip CSS"
task :gzip_css do
puts "## GZipping CSS"
styles_dir = "#{public_dir}/assets"
system 'find public/assets -maxdepth 1 -type f -name \*.css -exec gzip -9 {} \;'
# Batch rename .css.gz to .css
Dir['public/assets/*.css.gz'].each do |f|
test(?f, f) and File.rename(f, f.gsub(/\.css\.gz/, '.css'))
end
end

desc "GZip JS"
task :gzip_js do
puts "## GZipping JS"
styles_dir = "#{public_dir}/assets"
system 'find public/assets -maxdepth 1 -type f -name \*.js -exec gzip -9 {} \;'
# Batch rename .js.gz to .js
Dir['public/assets/*.js.gz'].each do |f|
test(?f, f) and File.rename(f, f.gsub(/\.js\.gz/, '.js'))
end
end

desc "GZip All"
task :gzip => [:gzip_html, :gzip_css, :gzip_js] do
end

同樣是 Rakefile,修改 generate 任務確保每次產生檔案時同時作壓縮。修改後的 generate 任務看起來應該像:

1
2
3
4
5
6
7
8
9
10
desc "Generate jekyll site"
task :generate do
raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
puts "## Generating Site with Jekyll"
system "compass compile --css-dir #{source_dir}/assets"
system "jekyll"
Rake::Task[:gzip_html].execute
Rake::Task[:gzip_css].execute
Rake::Task[:gzip_js].execute
end

注意,上述腳本山姆鍋只在 Mac OS X 上測試過,不過理論上,Linux 系統應該也可以執行。基本上,這個腳本會把 HTML, CSS 以及 JS 檔案使用 gzip 壓縮,並將’js’從檔名中移除。另外,您要根據您 JS/CSS 輸出的路徑,修改’public/assets’成實際放置 JS/CSS 的路徑。

修改部署腳本來設定適當 HTTP 標頭

修改 Rakefile 中,S3 的部署腳本,針對 HTML, JS 以及 CSS 檔案做不同的配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
desc "Deploy staging website via s3cmd"
task :s3_dev do
puts "## Deploying staging website via s3cmd"
# sync gzipped html files
# NOTE: Setting charset in header for faster browser rendering
ok_failed system("s3cmd sync --acl-public -P --reduced-redundancy public/* s3://#{s3_bucket_dev}/ --mime-type='text/html; charset=utf-8' --add-header 'Content-Encoding: gzip' --exclude '*.*' --include '*.html'")
# sync non gzipped, non js/css/image files
ok_failed system("s3cmd sync --guess-mime-type --acl-public -P --reduced-redundancy public/* s3://#{s3_bucket_dev}/ --exclude 'images/' --exclude '*.css' --exclude '*.js' --exclude '*.html'")
# sync gzipped css and js
ok_failed system("s3cmd sync --guess-mime-type --acl-public -P --reduced-redundancy public/* s3://#{s3_bucket_dev}/ --add-header 'Content-Encoding: gzip' --add-header 'Cache-Control: public, max-age=31600000' --exclude '*.*' --include '*.js' --include '*.css'")
# sync all images
ok_failed system("s3cmd sync --guess-mime-type --acl-public -P --reduced-redundancy --add-header 'Cache-Control: public, max-age=31600000' public/images/* s3://#{s3_bucket_dev}/images/")
end

其中,請把 #{s3_bucket_dev} 換成您使用的 S3 bucket 名稱。 底下是幾個重要的 s3cmd 參數說明:

  • –add-header 參數告訴 s3cmd,當那些檔案被下載時,要設定相對應的 HTTP 標題。
  • –mime-type 指定該檔案要回傳給瀏覽器的 Mime 型別。Amazon S3 對於 HTML 檔案預設並不會有 utf-8 這個編碼設定,但沒有這個設定,瀏覽器要等比較久的時間才能決定網頁內容的正確編碼,甚至使用了錯誤的編碼。

除了設定’Content-Encoding: gzip’這個標頭讓瀏覽器知道回傳的內容經過 gzip 壓縮外,同時也設定’Cache-Control’標頭來讓瀏覽器或者 CloudFront 知道這些內容該緩存的時間。針對 JS、圖檔、或者 CSS 這類靜態檔案,我們希望緩存的時間越長越好。但是 HTML 或者XML檔案,因為,會需要比較快更新,山姆鍋在這裡設為緩存 1 小時 (),您可以根據需要自行調整。

小結

作為靜態網站服務器,Amazon S3 不支援動態 GZip,的確是不方便。不過它提供的額外特性以及跟 CloudFront 整合,讓它仍舊是相當好的選擇。

參考資料