巴别塔上的雇工


Browser: F5 vs Ctrl+F5
11月 30, 2007, 5:05 上午
Filed under: 技术体会

在浏览器里中,按F5键或者点击Toobar上的Refresh/Reload图标(简称F5),和做F5同时按住Ctrl键(简称Ctrl+F5),效果是明显不一样的,通常Ctrl+F5明显要让网页Refresh慢一些,到底两者有什么区别呢?

上一篇技术文章中,说到了Expires、Last-Modified/If-Modified-Since和ETag/If-None-Match这些HTTP Headers,F5/Ctrl+F5和这些有莫大关系。

假如我第一次访问过http://www.example.com,这个网页是个动态网页,每次访问都会去访问Server,但是它包含一个一个静态资源http://www.example.com/logo.gif,浏览器在显示这个网页之前需要发HTTP请求获取这个logo.gif文件,返回的HTTP response包含这样的Headers:

Expires: Thu 27 Nov 2008 07:00:00 GMT
Last-Modified: Fri 30 Nov 2007 00:00:00 GMT

那么浏览器就会cache住这个logo.gif文件,直到2008年11月27日7点整,或者直到用户有意清空cache。

下次我再通过bookmark或者通过在URI输入栏直接敲字的方法访问http://www.example.com的时候,浏览器一看本地有个logo.gif,而且它还没过期呢,就不会发HTTP request给server,而是直接把本地cache中的logo.gif显示了。

F5的作用和直接在URI输入栏中输入然后回车是不一样的,F5会让浏览器无论如何都发一个HTTP Request给Server,即使先前的Response中有Expires Header。所以,当我在当前http://www.example.com网页中按F5的时候,浏览器会发送一个HTTP Request给Server,但是包含这样的Headers:

If-Modified-Since: Fri 30 Nov 2007 00:00:00 GMT

实际上Server没有修改这个logo.gif文件,所以返回一个304 (Not Modified),这样的Response很小,所以round-trip耗时不多,网页很快就刷新了。

上面的例子中没有考虑ETag,如同在上一篇技术文章中所说,最好就不要用ETag,但是如果Response中包含ETag,F5引发的Http Request中也是会包含If-None-Match的。

那么Ctrl+F5呢? Ctrl+F5要的是彻底的从Server拿一份新的资源过来,所以不光要发送HTTP request给Server,而且这个请求里面连If-Modified-Since/If-None-Match都没有,这样就逼着Server不能返回304,而是把整个资源原原本本地返回一份,这样,Ctrl+F5引发的传输时间变长了,自然网页Refresh的也慢一些。

实际上,为了保证拿到的是从Server上最新的,Ctrl+F5不只是去掉了If-Modified-Since/If-None-Match,还需要添加一些HTTP Headers。按照HTTP/1.1协议,Cache不光只是存在Browser终端,从Browser到Server之间的中间节点(比如Proxy)也可能扮演Cache的作用,为了防止获得的只是这些中间节点的Cache,需要告诉他们,别用自己的Cache敷衍我,往Upstream的节点要一个最新的copy吧。

在IE6中,Ctrl+F5会添加一个Header

Pragma: no-cache

在Firefox 2.0中,Ctrl+F5会添加两个

Pragma: no-cache
Cache-Control: max-age=0

作用就是让中间的Cache对这个请求失效,这样返回的绝对是新鲜的资源Smile

 Update: 你可能发现在某些浏览器(比如Chrome和IE7/IE8)中行为和此文中描述的不一致, 参看此文

 



彩笔没水了
11月 30, 2007, 1:28 上午
Filed under: 原创卡通

昨晚上发现彩笔没水了,真的需要白纸而不是白板了。

画多格漫画比画单幅困难,主要倒不是工作量大,而是要保持每格里面的同一人物都像是同一个人比较困难,如果是单幅就无所谓了,反正一个故事就在这一幅里面了,人物就这一个,画成什么样就什么样。

今天没什么故事作品,贴一张前几天胡乱画的Starcraft3版程序员形象凑数吧:)

图像063



金钱的魅力
11月 28, 2007, 1:39 下午
Filed under: 原创卡通

图像102

图像104

 

今天发现我的Mark Pen快没水了,有的线条画得非常模糊,看样子过几天需要在白纸上画出来然后拍照了。



Config HTTP Header For Better Client Performance
11月 28, 2007, 1:34 下午
Filed under: 技术体会

网站的性能(Performance)要考虑两方面,一方面是在Server端的运行效率,另一方面是在Client端感受到的效率。不要以为Server端效率高这个网站或者服务给用户的体验就是高效的,如果不考虑影响Client Performance的因素,Server端跑得再快用户也可能感觉慢得和牛一样。

Yahoo的哥们做的研究真不是盖的,他们还写了本书《High Performance Web Sites》详细介绍如果提高Client Performance。公司内部也有一个Team做这方面的事情,听过他们的培训,和Yahoo说的差不多,所谓英雄所见略同。

给静态资源(HTML文件,图片文件等)的Repsone加上Expires/Cache-Control Header是很有效的一招。如果HTTP Response中有Expires这样的Header的话,浏览器会Cache这个资源,理想状况下(注意,只是理想状况),在Expire Date之前,不会再发HTTP请求给Server要这个资源,不过Expires的值只能是一个固定日期,比如“Thu 27 Nov 2008 07:00:00 GMT”,不能是一个类似“从现在开始之后10年”这样一个随机浮动的值,如果要这样的效果,可以用Cache-Control这样的Header,如果HTTP Resposne中有这样的Header:“Cache-Control: max-age = 100”,表示这个资源在cache中的最大寿命是100秒。一般说来这种静态文件永远不应该过期,如果真的要给这个Cache加上一个期限,那我希望是——一万年,“Cache-Control: max-age = 315360000000”Smile

其实就应该给Expires设一个永远不会过期的时间,比如你现在有一个文件叫logo.gif,需要用一个新的logo的时候,你不要去覆盖原来的文件,而把新的logo存成logo_v2.gif,让相关网页引用新的logo_v2.gif,这样可以让新老网页同时工作,实在犯不上为了节省存储空间覆盖原有文件。

对Apache服务器,使用mod_expires,在httpd.conf或者.htaccess中加上

<FilesMatch "\.(ico|gif|jpg|html)$">
ExpiresDefault "access plus 10 years"
</FileMatch>

对于IIS 6(IIS 7还不清楚),通过IIS Manager可以通过GUI添加Expires/Cache-Control,通过命令行修改就麻烦一点了。 

首先要到IIS的AdminScripts目录下去找到adsutil.vbs文件。

cd C:InetpubAdminScripts

比如我们要给根目录下的imags目录添加Expires/Cache-Control,先要在metabase中给它加一个节点

cscript adsutil.vbs create W3SVC/1/root/images "IisWebVirtualDir"

如果希望访问images目录下的文件获得Cache-Control: max-age=60,就这样

csript adsutil.vbs set W3SVC/1/root/images/HttpExpires "D, 0x3c"

如果希望访问images目录下的文件获得“Expires: Thu 27 Nov 2008 07:00:00 GMT”,就这样

csript adsutil.vbs set W3SVC/1/root/images/HttpExpires "S, Thu 27 Nov 2008 07:00:00 GMT"

还有,同一个HTTP Response中可以同时有Expires和Cache-Control,但是Cache-Control权限比Expires大,会override它的。

HTTP的Response中还会有另外一个Header叫Last-Modified,比如“Last-Modified:  Thu, 06 Apr 2006 21:17:12 GMT”,浏览器访问一个URI得到这样的Resposne之后,就知道这个资源最后一次的修改时间,下次需要再次获得这个资源的时候,会发一个Request给Server,不过这个Request中有一条“If-Unmodified-Since: Thu, 06 Apr 2006 21:17:12 GMT”,如果在Server端在这个日期之后对这个资源进行了修改,就会照常返回这个资源给Client端,但是如果没有修改,就会返回一个304 (Not Modified) Response而不返回资源,告诉Client端:“这个资源从上次给你之来从来没改过,你放心用你Cache中的好了。” 一个304 Response比一个静态资源通常小多了,这样就节省了网络带宽。

图像099

让我们回过头来比较一下Expires和Last-Modified这两个东西,似乎Last-Modified比不上Expires,因为虽然它能够节省一点带宽,但是还是逃不掉发一个HTTP请求出去,而Expires却使得浏览器干脆连HTTP请求都不用发,岂不痛快!那还要Last-Modified这个物体干什么?理想状况的确是这样,不过当用户在IE或者Firefox里面按F5或者点击Refresh按钮的时候(不是在URL栏里重新输入一遍URL然后回车),就算对于有Expires的URI,一样也会发一个HTTP请求出去,所以,Last-Modified还是要用的,而且要和Expires一起用。

除了Last-Modified,HTTP Response中还可能有另外一个Header: ETag,使得Server上的静态资源有点“版本控制”的味道Smile 假如HTTP Response中包含“ETag: "abcdefg1234:0001"”,等于告诉Client端,你拿到的这个版本的资源有个ID,叫做abcdefg1234:0001,下次需要发Request索要同一个URI的时候,在Request里面加一条“If-None-Match: "abcdefg1234:0001"”,好,Server端做了一些修改,下次这个Client再来了一个请求,但是这时候资源已经改了,所以返回这个新资源,还有新的tag “ETag: "abcdefg4567:0001"”(这个etag我是胡写的),这样,Client端等于Cache了两份,在需要索要这个资源的时候,可以包含这样的Header: “If-None-Match: "abcdefg1234:0001" "abcdefg4567:0001"”,这样,即使Server端头脑发热,把这个资源Roll back回原来的版本,依然会返回304 (Not Modified) Response,因为它知道Client端Cache着以前的版本呢,这点功能是Last-Modifed/If-Not-Modified没法做到的。

不过ETag/If-None-Match这点功能实在是个鸡肋,首先,Server端的资源不大可能Roll Back,更重要的是,有可能造成Client Performance下降。对于只有一个Server的网站,没什么问题,但是现在稍微上点规模的网站都需要Scale Out,也就是说需要前端一个Load Balancer,后面接多台Server来处理请求,俗称Cluster,既然是Cluster,那么每个请求到底返回什么结果应该和分配到哪个Server无关,不过这个ETag可能就坏事了。假如用户的第一次请求分配给Server A,返回“ETag: "abcdefg1234:0001"”,但是第二次请求分配给了Server B,Server B上这个资源和Server A上的一模一样,但是计算出这个资源的ETag是"abcdefg1234:0002",这下麻烦了,虽然内容一样,但是ETag不匹配,还是浪费了带宽把资源发送了一遍,冤枉啊!而事实上,不同Server上的ETag很有可能不同,对于Apache,ETag的计算考虑了inode,对于IIS,ETag考虑了metabase的修改版本,要保证不同server上的这些信息一致,有点小难。不过不是有Last-Modified/If-Not-Modified吗?Server端看到If-Modified-Since,对照一下时间对得上,不管If-None-Match,可以直接发回304(Not Modified)呀,很不幸,RFC2616对这种情况做了规定,如果既有If-None-Match又有If-Modified-Since,除非两者不冲突,不然不会返回304。

图像101

所以说ETag就是一个害人精,按照Yahoo的建议,别费劲想办法同步不同Server上的ETag了,干脆就把ETag删除得了(缺省,Apache和IIS都是有ETag的),我Sniff了一下Yahoo的若干网页返回HTTP Response,的确没有ETag,人家的确是知行合一Smile 

对于Apache,在httpd.conf或者.htaccess中加一行就搞定了:

FileETag none

对于IIS 6,可就有点费劲了,首先,似乎没有办法通过Config来把ETag去掉,查了很多资料,问了很多人,似乎能够去掉ETag的办法只有写一个ISAPI Filter来弄,Sniff了一下Microsoft的几个网页的结果显示ETag都稳当当的存在,估计目前真的没有什么好方法。

只好退而取其次,保证不同Server上的ETag一致了。 IIS对Etag的计算算法是ETag = {Filetimestamp:ChangeNumber}, Filetimestamp保持一致没什么问题,ChangeNumber是metabase的change number,就有点难保证Cluster中每个Server都一样了,所以,干脆就把它设成固定值好了,这个连接告诉我们该怎么办,很可惜,没有找到彻底删除ETags的配置。



自我
11月 28, 2007, 12:54 上午
Filed under: 原创卡通

图像090

图像092

图像094

图像096



如果Microsoft收购了Google(If Microsoft Acquired Google)
11月 27, 2007, 1:25 上午
Filed under: 原创卡通
图像086

今天这幅漫画不仔细看可能会看不明白,对照Microsoft的Logo看可能容易看明白一点。

microsoft-logo 

这个漫画系列我尽量使用中文旁白,但是英文有“虚拟语气”,所以这里使用英文作图内旁白了。虚拟情景而已,希望Google的员工不要觉得这幅画有侵略性:)



螺丝钉
11月 26, 2007, 1:11 上午
Filed under: 原创卡通

图像070 图像072

图像076

 图像080



11/24/2007 Week Summary 一周总结
11月 24, 2007, 9:41 上午
Filed under: 原创卡通
这周开始画漫画并贴在Blog上,以后会继续。漫画不能用来证明道理,只能说明道理,但是更加清晰幽默,所以用这种方式比较容易让人接受,以前我长篇大论一大通每多少人看,现在画画Page View明显多了:)

要是闭门造车肯定憋不出什么有意思的主题来,不过作为一个程序员,可以说的事情实在太多了,问题只是怎么挑有意思的拿出来表达,只要有生活,总是有得故事来讲。

不过画画花的工夫不比使用文字少,何况我现在这点三脚猫的画功实在捉襟见肘,还好主题都还能给朋友们一点共鸣。需要留点时间训练基本绘画技巧,所以目前只能在工作日Post Cartoon,周末需要练习基本功。

总结一下一周的成果:

Today is Friday
http://morganchengmo.spaces.live.com/blog/cns!9950CE918939932E!2084.entry

梦蝶
http://morganchengmo.spaces.live.com/blog/cns!9950CE918939932E!2078.entry

Show (目前我自己最喜欢的一篇)
http://morganchengmo.spaces.live.com/blog/cns!9950CE918939932E!2054.entry

山顶洞人程序员在石壁上刻的第一个程序
http://morganchengmo.spaces.live.com/blog/cns!9950CE918939932E!2051.entry

清醒
http://morganchengmo.spaces.live.com/blog/cns!9950CE918939932E!2045.entry

第一篇home-made卡通
http://morganchengmo.spaces.live.com/blog/cns!9950CE918939932E!2034.entry



Today is Friday …
11月 23, 2007, 1:32 上午
Filed under: 原创卡通

图像064

图像065

周五是让人期待的,但是要是忙起来,周末和不是周末也没有什么区别。我还是希望生活和工作有计划性,不能瞎忙,到了周末就应该放松,把办公室内的东西全都抛在脑后。

“我喜欢每天有挑战性的工作/生活”这样的口号刚毕业的时候找工作唬唬雇主和自己可以,千万别太当真。我刚工作的时候一位资深前辈就说,别每天都想着“挑战性的”事情,要是真有这样的生活肯定把你给累死了。

按照计划做好每天的事情就好了,发生变化调整计划,然后按照新的计划做。

理想情况是

-Today is Monday

-Who cares?



梦蝶
11月 22, 2007, 12:57 上午
Filed under: 原创卡通

图像055

图像056

图像057

图像059