簡介: Web 發展的速度讓許多人歎為觀止,層出不窮的元件、技術,隻需要合理的組合、恰當的設定,就可以讓 Web 程式性能不斷飛躍。所有 Web 的思想都是通用的,它們也可以運用到 Java Web。這一系列的文章,将從各個角度,包括前端高性能、反向代理、資料庫高性能、負載均衡等等,以 Java Web 為背景進行講述,同時用實際的工具、實際的資料來對比被優化前後的 Java Web 程式。第一部分 , 主要講解網頁前端的性能優化,這一部分是最直接與使用者接觸的。事實證明,與其消耗大量時間在伺服器端,在前端進行的優化更易獲得使用者的肯定。
引言
前端的高性能部分,主要是指減少請求數、減少傳輸的資料以及提高使用者體驗,在這個部分,圖檔的優化顯得至關重要。許多網站的美化,都是靠絢麗的圖檔達到的,圖檔恰恰是占用帶寬的元兇。每個 img 标簽,浏覽器都會試圖發起一個下載下傳請求。本文就詳細介紹了圖檔優化的幾種方式,介紹了使用的工具以及優化後的結果。
圖檔壓縮
減少圖檔的大小,可以明顯的提高性能,而對于已有圖檔,要想減少圖檔的大小,隻能改變圖檔的格式,這裡推薦的是 PNG8 的格式,它可以在基本保持清晰度的情況下,減少圖檔的大小。知道這個原理以後,可以用 Windows 的畫圖工具、以及 PhotoShop 工具逐個的改變。但是這樣做的缺點是單張處理,效率太慢。本文推薦一個線上轉換工具 Smush.it,可以批量的進行壓縮與轉換。它的位址是:www.smushit.com/ysmush.it。打開後效果如下圖所示。
圖 1. Yahoo 提供的線上壓縮工具
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiInBnauMDMwU2Zh1WavwVMmJXZwlGaiV2dhZXYq1ybs1iavwVY2Fmavwlbj9CXztmcvdnclB3bsVmdlR2Lc12bj5SbilmL3d3dvw1LcpDc0RHaiojIsJye.jpg)
我們上傳了一張大小為 3790K 的圖檔,待線上程式處理完畢後,點選 Download Smushed Images 下載下傳檢視結果。下載下傳界面如下圖所示。
圖 2. 壓縮後的結果
打開下載下傳下來的壓縮包,檢視結果可以看到,圖檔從 3790 減少到了 3344,就如下圖所示。對于大批量的圖檔網站,這個方法會幫助快速實作批量圖檔壓縮。
圖 3. 壓縮後的結果
圖像合并實作 CSS Sprites
CSS Sprites 是一個吸引人的技術,它其實就是把網頁中一些背景圖檔整合到一張圖檔檔案中,再利用 CSS 的“background-image”,“background- repeat”,“background-position”的組合進行背景定位,background-position 可以用數字能精确的定位出背景圖檔的位置。利用 CSS Sprites 能很好地減少網頁的 HTTP 請求,進而大大的提高了頁面的性能,這也是 CSS Sprites 最大的優點,也是其被廣泛傳播和應用的主要原因。CSS Sprites 能減少圖檔的位元組,由于圖像合并後基本資訊不用重複,那麼多張圖檔合并成 1 張圖檔的位元組往往總是小于這些圖檔的位元組總和。同時 CSS Sprites 解決了網頁設計師在圖檔命名上的困擾,隻需對一張集合的圖檔上命名就可以了,不需要對每一個小元素進行命名,進而提高了網頁的制作效率。更換風格友善,隻需要在一張或少張圖檔上修改圖檔的顔色或樣式,整個網頁的風格就可以改變。維護起來更加友善。同時,由于将圖檔合并到一張圖檔,是以圖檔的請求數就被縮減到 1 個。其他的請求都可以用到本地緩存,不需要通路伺服器。下圖是一個合并以後的圖檔。它将很多小圖示都拼到了一起。
圖 4. 合并後的圖檔
這裡介紹一個小工具 ---“CSS Sprites 樣式生成工具 2.0”,可以從 這裡下載下傳。這是一個簡單免費的小工具,用該工具打開上面的圖檔,選中圖檔中的某塊。如下圖的“綠色大拇指”部分,工具會計算出這個部分的長、寬、距離左上角的距離。勾選複制類名、複制寬、複制高,再點選“複制目前樣式”按鈕。這樣生成的樣式會被複制到剪切闆上。
圖 5. 小工具的使用
生成的 CSS 代碼如清單 1 所示。
清單 1. 小工具生成的 CSS 代碼
|
将這段代碼運用在網頁上,它的代碼如下清單所示。
清單 2. 測試 CSS Sprites 代碼
|
打開測試網頁顯示結果如下圖所示。
圖 6. 測試網頁效果
可以看到,網頁隻顯示工具選擇的“綠色大拇指”部分,這樣的代碼可以運用在網頁的多個部分,而圖檔隻需要下載下傳一次,這就是該技術的最大優勢,減少了因為小圖檔引起的多個請求。
多域名請求
有時候,圖檔資料太多,一些公司的解決方法是将圖檔資料分到多個域名的伺服器上,這在一方面是将伺服器的請求壓力分到多個硬體伺服器上。另一方面,是利用了浏覽器的特性。一般來說,浏覽器對于相同域名的圖檔,最多用 2-4 個線程并行下載下傳。不同浏覽器的并發下載下傳數,都是不同的,并發數如下清單所示。
清單 3. 各浏覽器的并發下載下傳數
|
而相同域名的其他圖檔,則要等到其他圖檔下載下傳完後才會開始下載下傳。 這裡我做了一個測試,選擇了多個相同域名的圖檔在同一網頁上。代碼如清單 5 所示。
清單 4. 單域名的多圖檔下載下傳
|
接下來,使用 FireFox 的 Firebug 插件監控網絡。結果如下圖所示。
圖 7. 單域名多圖檔的監控效果
可以看到,相同域名的多張圖檔,它們下載下傳的起始點是存在延遲的。它們并不是并行下載下傳。當我們将其中的 3 張圖檔換成别的域名圖檔。如清單 6 所示。
清單 5. 多域名多圖檔下載下傳
|
再次檢視網絡監控,可以看到,這些圖檔是并行下載下傳的。
圖 8. 多域名多圖檔測試結果
多域名的下載下傳固然很好,但是太多域名并不太好,一般在 2-3 個域名下載下傳就差不多。
圖像的 BASE64 編碼
不管如何,圖檔的下載下傳始終都要向伺服器送出請求,要是圖檔的下載下傳不用向伺服器送出請求,而可以随着 HTML 的下載下傳同時下載下傳到本地那就太好了。而目前,浏覽器已經支援了該特性,我們可以将圖檔資料編碼成 BASE64 的字元串,使用該字元串代替圖像位址。假設用 S代表這個 BASE64 字元串,那麼就可以使用 <img src="data:image/png;base64,S"> 來顯示這個圖像。可以看出,圖像的資料包含在了 HTML 代碼裡,無需再次通路伺服器。那麼圖像要如何編碼成 BASE64 字元串呢?可以使用 線上的工具---“Base64 Online”,這個工具可以上傳圖檔将圖檔轉換為 BASE64 字元串。當然,如果讀者有興趣,完全可以自己實作一個 BASE64 編碼工具,比如使用 Java 開發,它的代碼就如清單 7 所示。
清單 6. BASE64 的 Java 代碼
|
本文編碼了一個圖像,并且将編碼獲得的 BASE64 字元串,寫到了 HTML 之中,如下清單 8 所示。
清單 7. 嵌入 BASE64 的測試 HTML 代碼
|
由于圖檔資料包含在了 BASE64 字元串中,是以無需向伺服器請求圖像資料,結果顯示如下圖所示。
圖 9. BASE64 顯示圖像
然而這種政策并不能濫用,它适用的情況是浏覽器連接配接伺服器的時間 > 圖檔下載下傳時間,也就是發起連接配接的代價要大于圖檔下載下傳,那麼這個時候将圖檔編碼為 BASE64 字元串,就可以避免連接配接的建立,提高效率。如果圖檔較大的話,使用 BASE64 編碼雖然可以避免連接配接建立,但是相對于圖像下載下傳,請求的建立隻占很小的比例,如果用 BASE64,對于動态網頁來說圖像緩存就會失效(靜态網頁可以緩存),而且 BASE64 字元串的總大小要大于純圖檔的大小,這樣一算就非常不合适了。是以,如果你的頁面已經靜态化,圖像又不是非常大,可以嘗試 BASE64 編碼,用戶端會将網頁内容和圖檔的 BASE64 編碼一起緩存;而如果你的頁面是動态頁面,圖像還較大,每次都要下載下傳 BASE64 字元串,那麼就不能用 BASE64 編碼圖像,而正常引用圖像,進而使用到浏覽器的圖像緩存,提高下載下傳速度。從現實我們接觸的角度看,如一些線上 HTML 編輯器,裡面的小圖示,如笑臉等,都使用到了 BASE64 編碼,因為它們非常小,數量多,BASE64 可以幫助網頁減少圖示的請求數,提高效率。
GZIP 壓縮
為了減少傳輸的資料,壓縮是一個不錯的選擇,而 HTTP 協定支援 GZIP 的壓縮格式,伺服器響應的報頭包含 Content-Encoding: gzip,它告訴浏覽器,這個響應的傳回資料,已經壓縮成 GZIP 格式,浏覽器獲得資料後要進行解壓縮操作。這在一定程度可以減少伺服器傳輸的資料,提高系統性能。那麼如何給伺服器響應添加 Content-Encoding: gzip 報頭,同時壓縮響應資料呢?如果你用的是 Tomcat 伺服器,打開 $tomcat_home$/conf/server.xml 檔案,對 Connector 進行配置,配置如清單 9 所示。
清單 8. TOMCAT 配置清單
|
我們為 Connector 添加了如下幾個屬性,他們意義分别是:
compression="on" 打開壓縮功能
compressionMinSize="2048" 啟用壓縮的輸出内容大小,這裡面預設為 2KB
noCompressionUserAgents="gozilla, traviata" 對于以下的浏覽器,不啟用壓縮
compressableMimeType="text/html,text/xml, image/png" 壓縮類型
有時候,我們無法配置 server.xml,比如如果我們隻是租用了别人的空間,但是它并沒有啟用 GZIP,那麼我們就要使用程式啟用 GZIP 功能。我們将需要壓縮的檔案,放到指定的檔案夾,使用一個過濾器,過濾對這個檔案夾裡檔案的請求。
清單 9. 自定義 Filter 壓縮 GZIP
|
該程式的主體思想,是在響應流寫回之前,對響應的位元組資料進行 GZIP 壓縮,因為并不是所有的浏覽器都支援 GZIP 解壓縮,如果浏覽器支援 GZIP 解壓縮,會在請求報頭的 Accept-Encoding 裡包含 gzip。這是告訴伺服器浏覽器支援 GZIP 解壓縮,是以如果用程式控制壓縮,為了保險起見,還需要判斷浏覽器是否發送 accept-encoding: gzip 報頭,如果包含了該報頭,才執行壓縮。為了驗證壓縮前後的情況,使用 Firebug 監控請求和響應報頭。
清單 10. 壓縮前請求
|
清單 11. 不壓縮的響應
|
清單 12. 壓縮後的響應
|
可以看到,壓縮後的資料比壓縮前資料小了很多。壓縮後的響應報頭包含 Content-Encoding: gzip。同時 Content-Length 包含了傳回資料的大小。GZIP 壓縮是一個重要的功能,前面提到的是對單一伺服器的壓縮優化,在高并發的情況,多個 Tomcat 伺服器之前,需要采用反向代理的技術,提高并發度,而目前比較火的反向代理是 Nginx(這在後續的文章會進行詳細的介紹)。對 Nginx 的 HTTP 配置部分裡增加如下配置。
清單 13. Nginx 的 GZIP 配置
|
由于 Nginx 具有更高的性能,利用該配置可以更好的提高性能。在高性能伺服器上該配置将非常有用。
懶加載與預加載
預加載和懶加載,是一種改善使用者體驗的政策,它實際上并不能提高程式性能,但是卻可以明顯改善使用者體驗或減輕伺服器壓力。
預加載原理是在使用者檢視一張圖檔時,就将下一張圖檔先下載下傳到本地,而當使用者真正通路下一張圖檔時,由于本地緩存的原因,無需從伺服器端下載下傳,進而達到提高使用者體驗的目的。為了實作預加載,我們可以實作如下的一個函數。
清單 14. 預加載函數
|
上面的代碼,首先定義了 Image 對象,并且聲明了需要預加載的圖像數組,然後逐一的開始加載(.src=images[i])。如果已經在緩存裡,則不做其他處理;如果不在緩存,監聽 onload 事件,它會在圖檔加載完畢時調用。
而懶加載則是在使用者需要的時候再加載。當一個網頁中可能同時有上百張圖檔,而大部分情況下,使用者隻看其中的一部分,如果同時顯示上百張,則浪費了大量帶寬資源,是以可以當使用者往下拉動滾動條時,才去請求下載下傳被檢視的圖像,這個原理與 word 的顯示政策非常類似。
在 JavaScript 中,它的基本原理是首先要有一個容器對象,容器裡面是 img 元素集合。用隐藏或替換等方法,停止 img 的加載,也就是停止它去下載下傳圖像。然後曆遍 img 元素,當元素在加載範圍内,再進行加載(也就是顯示或插入 img 标簽)。加載範圍一般是容器的視框範圍,即浏覽者的視覺範圍内。當容器滾動或大小改變時,再重新曆遍元素判斷。如此重複,直到所有元素都加載後就完成。當然對于開發來講,選擇已有的成熟元件,并不失為一個上策,Lazy Load Plugin for jQuery 是基于 JQuery 的懶加載元件,它有自己的 官方網站。這是一個不錯的免費插件。可以幫助程式員快速的開發懶加載應用。
小結
本文總結了前端圖檔高性能優化的幾種方式,将它們歸結起來,在讀者需要的時候,可以檢視本文的内容,相信按照本文的方法,可以輔助讀者進行前端的高性能優化。筆者将繼續寫後續的部分,包括資料庫的優化、負載均衡、反向代理等。包括由于筆者水準有限,如有錯誤,請聯系我批評指正。
接下來在第二部分文章中,我将介紹前端的 BigPipe 技術、Flush 機制、動靜分離、HTTP 持久連接配接、HTTP 協定靈活應用、靜态化與僞靜态化、頁面緩存(整體、部分)等,并且将它們應用到 Java Web 的開發中。使用這些技術可以幫助提高 Java Web 應用程式的性能。
原文位址:https://www.ibm.com/developerworks/cn/java/j-lo-javawebhiperf1/