前端要給力之:URL應(yīng)該有多長? 收藏 此文于2010-12-17被推薦到CSDN首頁
如何被推薦?
URL到底應(yīng)該有多長?我為什么要提這個問題呢?有許多優(yōu)化指南里都寫著:要盡量減小COOKIE、縮短URL,以及盡可能地使用GET請求等等,以便優(yōu)化WEB頁面的請求和裝載。但是,這種所謂“盡可能”、“盡量”只是定性的描述,定量的來看,要縮短到多少個字節(jié)才算少呢?
就以我們某次首頁的改版中,通過http analyzers我看到幾個有趣的.js文件的URL,是這樣的:
https://static.alipay.net/build/js/app/tracker.js?v=083 https://static.alipay.net/build/js/home/home.js?t=20101012 https://static.alipay.net/build/js/pa/alieditcontrol-update.js?t=20101012 https://static.alipay.net/javascript/arale_v1.0.js https://static.alipay.net/min/?b=javascript&f=arale/lang/aspect.js,arale/lang/md5.js,arale/lang/uri.js,arale/lang/tmpl.js,arale/lang/date.js,arale/lang/number.js,arale/http/jsonp.js,arale/http/ajax.js,arale/http/core.js,arale/event/event-chain.js,arale/class/declare.js,arale/fx/animator.js,aralex/widget.js,aralex/tplwidget.js,aralex/view.js,aralex/tab/tab.js,aralex/dropdown/dropdown.js,aralex/slider/slider.js,aralex/slider/switchslider.js https://static.alipay.net/build/js/app/tracker.js?v=083https://static.alipay.net/build/js/home/home.js?t=20101012https://static.alipay.net/build/js/pa/alieditcontrol-update.js?t=20101012https://static.alipay.net/javascript/arale_v1.0.jshttps://static.alipay.net/min/?b=javascript&f=arale/lang/aspect.js,arale/lang/md5.js,arale/lang/uri.js,arale/lang/tmpl.js,arale/lang/date.js,arale/lang/number.js,arale/http/jsonp.js,arale/http/ajax.js,arale/http/core.js,arale/event/event-chain.js,arale/class/declare.js,arale/fx/animator.js,aralex/widget.js,aralex/tplwidget.js,aralex/view.js,aralex/tab/tab.js,aralex/dropdown/dropdown.js,aralex/slider/slider.js,aralex/slider/switchslider.js 注意最后一條。嗯,不要驚訝,的確是這樣長的URL,確切的長度是443bytes。但這是長了呢?還是不算長呢?
要知道以IE為例,可以處理的URL長度為2048 bytes,也就是說,反正~~無論如何,IE是能處理的。其實,一般瀏覽器也沒問題,所以,“正確性”是沒問題。所以,接下來我們要說的是效率。
一、TCP/IP協(xié)議中的包頭問題
在TCP/IP網(wǎng)絡(luò)中,底層協(xié)議是一回事,應(yīng)用層協(xié)議又是一回事。所以作為應(yīng)用層協(xié)議的HTTP,自身可以傳輸多大的內(nèi)容,以及如何傳輸(例如HTTP包一般以48K為界限,超過48K時會出現(xiàn)應(yīng)用層的分包,即所謂的multipart)這些都是由應(yīng)用層來約定的。而在底層協(xié)議中,鏈路層與傳輸層對“傳多大的包”有各自的約定。簡單的說,傳輸層約定了IP數(shù)據(jù)包的MSS(最大分段尺寸),鏈路層約定了MTU(最大傳輸單元)。如果一個IP數(shù)據(jù)包的大小超過MTU(即MSS+TCP報頭+IP報頭>MTU),則在鏈路層會將IP數(shù)據(jù)包拆成多個信息包傳輸。
MSS與不同的傳輸環(huán)境相關(guān),有兩個推薦值。一般來說,
- 目標(biāo)地址非本地地址(與源地址在不同一個網(wǎng)段)時,MSS默認(rèn)值通常是536;否則,
- MSS默認(rèn)值通常為1460。
MTU與網(wǎng)絡(luò)環(huán)境相關(guān),也有兩個推薦值。一般來說,
- 串口為576字節(jié);
- 以太網(wǎng)為1500字節(jié)。
MTU/MSS的兩種推薦值中都有40個字節(jié)的差異,即是(TCP報頭+IP報頭)的一般值,該值以120 bytes為上限(20+20字節(jié)的IP/TCP頭部;40+40字節(jié)IP/TCP可選頭部)。所以在復(fù)雜的網(wǎng)絡(luò)環(huán)境中,應(yīng)用層的網(wǎng)絡(luò)協(xié)議可用的單個數(shù)據(jù)包的大小,最佳值應(yīng)小于536-80=456字節(jié),盡量限制在1460-80 = 1380字節(jié)以內(nèi)。這樣的限制,是綜合考慮傳輸層與鏈路層協(xié)議的結(jié)果。不過一些常見的建議中,也會用536/1460這兩個值,與這里的討論沒有太本質(zhì)的差異。我只是強(qiáng)調(diào),如果我們要一個“足夠優(yōu)化的請求”,那么極限值應(yīng)該是多少?
二、HTTP協(xié)議中的包頭問題
那么,現(xiàn)在來到HTTP這個應(yīng)用層協(xié)議。一個HTTP請求由頭部與數(shù)據(jù)區(qū)構(gòu)成,對于HTTP GET請求來說,可以只有頭部而沒有數(shù)據(jù)區(qū),原因是HTTP頭部的內(nèi)容如下(頭部需要以2個連續(xù)回車換行結(jié)束):
---------
GET (...) HTTP/1.1
Accept:*/*
Referer:http://www.alipay.net/
Accept-Language:zh-cn
User-Agent: (...)
Accept-Encoding:gzip, deflate
Host:static.alipay.net
Connection:Keep-Alive
Cookie: (...)
---------
---------
GET (...) HTTP/1.1
Accept:*/*
Referer:http://www.alipay.net/
Accept-Language:zh-cn
User-Agent: (...)
Accept-Encoding:gzip, deflate
Host:static.alipay.net
Connection:Keep-Alive
Cookie: (...)
---------
這里的GET (...)可以跟著一個完整的GET請求的URL,而GET請求的參數(shù)也都放在這個URL上,因此可以不需要有單獨的數(shù)據(jù)區(qū)。在上述的這個HTTP請求中,某些特定的客戶端可能會多幾個或少幾個http head field,但通常字段都會比較短。我們僅以這個例子來說明,那么這個“缺省的(不完整的)HTTP頭”用掉了多少字節(jié)呢?
答案是184字節(jié)。不過還需要強(qiáng)調(diào),Referer與當(dāng)前正在瀏覽的網(wǎng)址直接相關(guān),例如當(dāng)前正在瀏覽的頁面是500字節(jié)長的URL,那么當(dāng)前網(wǎng)頁上的超鏈接點擊時Referer字段都會填上這個500字節(jié)的URL,網(wǎng)頁中過長的URL會使得點擊超鏈接時消耗更多的傳輸,這里也是一例了。
那么不討論Referer字段的影響,僅以上述為例,我們能用的最佳值,就只剩下了456-184=272字節(jié)了。這272字節(jié)會有三個地方使用,就是上面標(biāo)為(...)的三個地方:GET、User-Agent和Cookie。User-Agent這個字段與瀏覽器相關(guān),不同的瀏覽器以及該瀏覽器處理不同的操作系統(tǒng)環(huán)境時,都會出現(xiàn)不同。在JS以及服務(wù)器上的統(tǒng)計軟件中,也常常使用這個字段來判斷瀏覽器環(huán)境,例如OS、版本等。這個字段的值有時候會比較長,以我當(dāng)前的機(jī)器為例,該值為:
---------
Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; QQWubi 108; EmbeddedWB 14.52 from:
http://www.bsalsa.com/ EmbeddedWB 14.52; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET CLR 1.1.4322; .NET CLR 3.5.21022; .NET4.0C; .NET4.0E)
---------
占用了274字節(jié)。也就是說,事實上理想環(huán)境下的使用456字節(jié)就已經(jīng)不夠用了。按此前討論的,我們可以退而求其次:
- 使用536字節(jié)的邊界值,即不考慮80字節(jié)的tcp/ip可選頭部。
此外,需要強(qiáng)調(diào)的是User-Agent長度的可變性,例如上面的“EmbeddedWB……”等64字節(jié)在一般的電腦中可能就沒有,這是一個第三方組件。同樣的,也可能因為其它的瀏覽器環(huán)境(例如傲游)導(dǎo)致這個字段更長?;谶@個事實,我仍以本例中的這一特殊情況來做分析。
以536字節(jié)為例,我們事實上還有78字節(jié)可用,因此在這里我們將優(yōu)化的第一等級設(shè)為:70字節(jié)。建議公司可以根據(jù)服務(wù)器端收集的數(shù)據(jù)取一個平衡值。
三、COOKIE耗用可以降到0
現(xiàn)在,Cookie是消耗最大的地方,以我當(dāng)前的機(jī)器為例,該值有幾種情況(對于不同的協(xié)議與域,是不一樣的):
(2) 對于
http://*.alipay.net/,值有171字節(jié):
ali_apache_id=12.1.11.70.1275978936200.5; ali_apache_sid=12.1.46.46.128998714836.4|1289988948; ALIPAYJSESSIONID=bYWcn4Wq0Z5FBCoHzfpn2f1XxDAmBepay; ali_apache_tracktmp=uid=
(3) 對于
https://static.alipay.net/,值有307字節(jié):
cna=AKaaAhYBhU0BAeMdAHlnHNcd; ali_apache_id=169.17.198.19.1272623861747.7; payMethod=directPay; _tb_order=38016166656317; defaultBank=ICBC; __utma=22931947.260433774.1277279158.1277279158.1282287558.2; __utmz=22931947.1282287558.2.2.utmcsr=life.alipay.net|utmccn=(referral)|utmcmd=referral|utmcct=/index.php
(4) 對于http(s)://img.alipay.net/,值有379字節(jié):
apay_id=159588238.127262386236866.128979461890689.1289969142342368.137; cna=AKaaAhYBhU0BAeMdAHlnHNcd; ali_apache_id=169.17.198.19.1272623861747.7; payMethod=directPay; _tb_order=38016166656317; defaultBank=ICBC; __utma=22931947.260433774.1277279158.1277279158.1282287558.2; __utmz=22931947.1282287558.2.2.utmcsr=life.alipay.net|utmccn=(referral)|utmcmd=referral|utmcct=/index.php
(5) 其它情況。
為什么在2、3、4情況下出現(xiàn)了cookie使用的暴增呢?事實上,3、4兩種情況雖然略有差異,但產(chǎn)生問題的根源與情況2是完全一致的。所以后文僅以情況2為例。跟蹤其http request過程可知:
- 請求首頁時,服務(wù)器端返回了四個set-cookie應(yīng)答。
這四個應(yīng)答(http response head)如下:
--------
Set-Cookie:ali_apache_sid=10.2.46.46.128998714836.4|1289988948; path=/; domain=.alipay.net
Set-Cookie:JSESSIONID=A8CE523AEA03E2C990D6796D6BAEC81E; Path=/
Set-Cookie:ALIPAYJSESSIONID=bYWcn4Wq0Z5FBCoHzfpn2f1XxDAmBepay; Domain=.alipay.net; Path=/
Set-Cookie:ali_apache_tracktmp=uid=; Domain=.alipay.net; Path=/
--------
所以在此后的所有http請求中,都將使用如前例(3)中的171字節(jié)的cookie。但是,顯然的,至少有以下幾種情況這些cookie是無意義的:
- 如果訪問的是重定向頁面,包括返回Status Code:302的重定向,以及html頁面中使用http-meta的重定向;
- 如果訪問的頁面是被緩存的,例如返回Status Code:304的“Not Modified”;
- 如果訪問的頁面是靜態(tài)的、無需識別cookie的,例如static.alipay.net中的.img、.js和.css文件等。
顯然,我們在img、static中的圖片或其它靜態(tài)資源是可以被緩存的,而且無論是緩存還是第一次存取,cookie值都完全沒有意義。對于靜態(tài)頁面(.html)來說,如果我們不是要通過http server來統(tǒng)計分析靜態(tài)頁面的訪問情況,那么這些cookie也是不需要的。所以,對于這些資源、內(nèi)容,我們應(yīng)該強(qiáng)調(diào)的使這些cookie不被發(fā)送,或盡量少的使用(對于部分的.html靜態(tài)頁,我們可能僅僅需要用于分析用戶訪問鏈的session id)。
優(yōu)化cookie的方式很簡單:將這些靜態(tài)資源部署在不以.alipay.net為domain的服務(wù)器/組里,或使用其它的獨立域名。這種情況下,對于特定的——當(dāng)然也是最大量的一部分——資源,COOKIE耗用可以降到0。
四、縮短url
總算來到我們的正題:URL可以有多長?通過前面的分析,我們?nèi)匀贿€有70字書可以用,即使在特定條件下,我們需要給一些頁面訪問留下track數(shù)據(jù)(例如session),那么我們?nèi)匀挥?0~50個字節(jié)可以用。不過,僅此而已,我們離本文最開始提到的443 bytes仍然有相當(dāng)相當(dāng)長的距離。
但我們真的需要這么長的URL嗎?
答案是不需要,我們完全可以縮短URL。例如前面的例子,我們原始的URL的get部分是:
---------
/min?b=javascript&f=arale/lang/aspect.js,arale/lang/md5.js,arale/lang/uri.js,arale/lang/tmpl.js,arale/lang/date.js,arale/lang/number.js,arale/http/jsonp.js,arale/http/ajax.js,arale/http/core.js,arale/event/event-chain.js,arale/class/declare.js,arale/fx/animator.js,aralex/widget.js,aralex/tplwidget.js,aralex/view.js,aralex/tab/tab.js,aralex/dropdown/dropdown.js,aralex/slider/slider.js,aralex/slider/switchslider.js
---------
---------
/min?b=javascript&f=arale/lang/aspect.js,arale/lang/md5.js,arale/lang/uri.js,arale/lang/tmpl.js,arale/lang/date.js,arale/lang/number.js,arale/http/jsonp.js,arale/http/ajax.js,arale/http/core.js,arale/event/event-chain.js,arale/class/declare.js,arale/fx/animator.js,aralex/widget.js,aralex/tplwidget.js,aralex/view.js,aralex/tab/tab.js,aralex/dropdown/dropdown.js,aralex/slider/slider.js,aralex/slider/switchslider.js
---------
仔細(xì)觀察,它的意思其實是
---------
/min?b=javascript&f=...
---------
字段f后面的,其實是arale這個腳本項目中的一些靜態(tài)資源的拼接。在服務(wù)器端,min這個程序根據(jù)參數(shù)"b=javascript&f=..."將一些腳本碎片拼接成一個單獨.js文件并返回到瀏覽器,如果沒有變化,則直接返回Status Code:304。
那么,事實上我們每次請求的“f=...”字段后面的參數(shù)塊將會是完全一樣的。或者,即使在不同的情況下要求拼接的文件列表不一樣,也僅有相當(dāng)有限的組合。這使我們自然的想到一個東西:求和。用這種方式對上述的字符串求一個key(例如hash、md5、crc),然后我們就可以用這個唯一key來查找拼接后的.js內(nèi)容——這也意味著min程序不需要每次都拼接文本。這樣一來,上面的URL可以變成(以對f字段后的396字節(jié)求crc32為例):
---------
/min?b=javascript&f=313466DB
---------
考慮到不同的版本管理:
---------
/min?b=javascript&v=0.9b&f=313466DB
---------
現(xiàn)在,我們將URL控制在了一個相當(dāng)小的規(guī)模,而且加上了版本管理和內(nèi)容的有效性驗證,需要的情況下,服務(wù)器端的min程序也可以做動態(tài)生成以及緩存。這些改造與我們原始的需求并沒有任何的沖突。重要的是,我們成功地將get請求控制在了35字節(jié),余留的空間完全滿足我們
整體優(yōu)化的需要:第一等級的優(yōu)化,70字節(jié)!
五、技術(shù)成熟性與價值
1、twritter中早就使用這樣的技術(shù)了。
3、微軟還“傻傻說不清楚”,所以你看他們的官網(wǎng)很慢。^^.
4、當(dāng)我們有條件將http頭部減小到456字節(jié)以下時,應(yīng)盡力為之。例如旺旺因為有獨立客戶端,所以可以定制http request head,以縮減User-Agent等字段。
5、當(dāng)我們總是從瀏覽器端發(fā)出最小化的HTTP請求時,網(wǎng)絡(luò)總是可以最快速的將請求提交到服務(wù)器,無需等待多個包并組合。這在慢速網(wǎng)絡(luò),以及存在大量丟包的網(wǎng)絡(luò)中效果將為極為明顯。簡單地說,如果有人在局域網(wǎng)中用迅雷或BT,那么最小化HTTP請求將會使網(wǎng)頁的瀏覽體驗提升得相當(dāng)相當(dāng)明顯。
6、我們應(yīng)該做腳本等靜態(tài)資源的版本管理了。