山姆鍋曾經提到 , 影化身網站是放置在 Amazon S3 上 , 也利用 CloudFront 來減少存取的延遲 。 但是 , 好還可以更好 , 山姆鍋在本文會分享如何減少 HTTP 要求數量 , 壓縮網頁內容來加快存取速度 。 Amazon S3 雖然方便 , 但因為不是設計來支援動態內容 , 有幾個限制會影響到存取效率的最佳化 ( 優化 ), 像是不支援執行時期 GZip encoding。 雖然不支援動態壓縮 , 但是我們可以預先把內容做 GZip 壓縮來達到同樣效果 。 底下將說明如何透過 s3cmd 這個工具來達到減少 HTTP 請求數量以及資料量的目的 。 對了 ! 請注意山姆鍋用的工作環境是 Mac OS X。

準備工具 s3cmd

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

安裝 s3cmd

到官方網站下載 s3cmd 並解壓縮後 , 執行下列指令來安裝 :

$ python setup.py install

設定 s3cmd

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

$ s3cmd --configure

驗證 s3cmd 安裝完成

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

$ 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 中 , 加入下列腳本 :

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 任務看起來應該像 :

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 檔案做不同的配置 。

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 整合 , 讓它仍舊是相當好的選擇 。

參考資料