巴别塔上的雇工


我的2008
12月 31, 2008, 11:40 上午
Filed under: 八卦杂谈

2008年很快就要过去了,现在要总结一下过去,为明年规划。

先回顾,对比一下去年的总结

  • 我所属产品组虽然只有一个Release,但是发布之后运行相当稳定,可以说是慢工出了细活;
  • 依然坚持去健身房,体重没有增加,但是年终体检报告说我体重指数显示我应改控制体重了,我想这个体重指数没有考虑肌肉:-) 不管怎么样,以后还是要继续锻炼;
  • 今年没有出去旅游,很遗憾;
  • 画了一些漫画,但是因为工作紧,没有时间持续;
  • 包括这一篇,写了总共286篇博客文章,并把我的博客的PageRank分数推到了2分

还有一些新的内容

  • 作为观众,和家人一起参与了北京奥运会,这是一辈子的美好回忆;
  • 有幸被挑选参加公司一项长期的内部训练计划,在这个训练计划中学到很多Soft Skills;
  • 自主策划为所属部门拍摄了一个短片I’m APC,在同事们的帮助下此片可以说是相当成功,部门年终晚会的开始就是播放此片,我们一起看着大屏幕,可以自豪的说,看,这就是我们努力的结果。

规划一下2009年,有三件事要做

  • 坚持锻炼身体,保持体重,燃烧掉脂肪;
  • 学习Web Development技术;
  • 用DV拍摄一部有故事情节的“电影”

 

2009年到了,希望大家都够总结过去,展望未来,努力!



“少活20年” 换 “打败XXX”
12月 29, 2008, 11:50 上午
Filed under: 八卦杂谈

看到这个新闻,原文写的一堆语病,但是意思大概是,某某IT业界老大问另外两个老大,是否愿意用“少活20年”来换取“打败XXX”,后者都愿意接受这个“交易”。我得说,这个问题和回答不光无聊,而且无耻。

做事情要做好,就需要先保证做的事情有意义,你做的产品要能给用户带来好处才能生存发展,竞争只是促进参与各方努力的一个形式,但是做事绝对不是就为了“打败XXX”,如果你把“打败XXX”放到第一位,我敢说你最后肯定得不到你想要的东西。

“My naive and romantic belief is that … if you make meaning, you probably make money; but if you set out to make money, you’ll probably NOT make meaning and YOU WON’T MAKE MONEY.”

——Guy Kawasaki

  

就我的观察,很多出去创业的同龄人,似乎就没有摆正"make meaning"和"make money"的主次关系,他么创业的动机,更多的是为了赚个亿万身家,而不要去创造一个真正对用户有意义的产品或者服务,这样创业的结果,要么是彻底失败,要么是毫无新意地重复一个现有的产品。

虽然说,每个人的道路,都要自己去选择,但是人总是很容易被所谓“成功人士”影响,被认为是“成功人士”的人士,应该知道自己说的话会影响一批人,应该负责任。也许这几个江湖大佬自己都是无神论者,知道没有拿阳寿换胜仗这会事,但是他们还是用这种方式表达这种病态的“决心”,所以我说他们不光无聊,而且无耻。



比赛,到了最后一秒钟也不要放弃
12月 28, 2008, 9:55 上午
Filed under: 八卦杂谈

今天上午看CCTV-5直播NBA常规赛火箭对爵士,这场到最后关头真称得上惊心动魄,连续两个加时赛才分出胜负。火箭从一直领先到几乎被爵士反败为胜,打完第一个加时,演播室里面三位动嘴皮子得先生也已经放弃了,说出“即使火箭下一个加时赢了,其实也已经输了”这样得丧气话。就是在这个时候,阿泰斯特(Artest,最艺术!)站了出来,连续创造进攻机会,终于把比分拉开,最后终于挽救了麦蒂确阵姚明强弩之末的火箭队。比赛到了最后关头,双反都已经精疲力竭了,就看谁能够坚持住,不要放弃争胜的信念,一切皆有可能!同样,对手爵士也表现出即使到了最后一秒也要拼的斗志,值得敬佩,这是一场值得纪念的比赛!

这一年,还有一场同样充分体现“坚持才能胜利”的比赛,就是奥运会上佟文夺取女子柔道78公斤级冠军的那一场,在比赛结束前十几秒反败为胜。内心的强壮才是真的强壮,这才是体育的目的。



How C# Implements Closure
12月 27, 2008, 9:39 上午
Filed under: 技术体会

从2.0版本开始,C#开始支持Closure的概念,但是可能很多C#程序员还不知道何为Closure,即使曾经用过。了解事情就要了解事情的本质,看看C#到底怎么实现对Closure的支持的。

Wikipedia上,Clousre是这么定义的:

“In computer science, a closure is a function that is evaluated in an environment containing one or more bound variables. When called, the function can access these variables. ”

我觉得这样的定义中用了“function”这个词不大合适,用"code block"也许更恰当。特定于C#来说,Closure就是一段code block,这段code block是一个anonymous method或者lambda expression,而且,这段code block还可以访问它所在block的变量。奇妙的地方是,Closure不一定在所在block中运行,但是依然能够访问所在block中的变量。看代码例子最清楚了

class Program
{
    public static Action GetAction(int arg)
    {
        int x = arg;
        Action action =
            delegate()
            { // this is a closure
                Console.WriteLine(x);
            };
 
        return action;
    }
 
    public static void Main(string[] args)
    {
        Action action = GetAction(13);
        action(); // this is where the closure actually get executed
    }
}

上面的Code编译运行正常,输出13。在GetAction方法中有一段anonymous method,这就是一个Closure,你看它访问了GetAction中的“局部变量”x(这个“局部变量”需要打上引号,下面会介绍为什么),而这段Closure实际上不是在GetAction中执行的,GetAction将这个Closure作为一个Action返回了,在Main函数中,这个Closure才得以执行,也就是说在Main函数中,GetAction中的“局部变量”x才得到访问。

我相信大部分C#程序员都写过类似的code,但是不知道是否都想过,为什么一个“局部变量”会变得不“局部”了,按照常理,x既然是GetAction中的局部变量,是不可能在GetAction函数之外被访问的。这是因为C#支持Closure这个概念,为了支持这个概念,C#编译器会自动产生一些你看不到的code,要看到这些code,用Red Gate’s Reflector是不行的,因为这个Reflector足够聪明,把自动产生的code变回和我们写的code一模一样,要了解C#编译器搞了什么鬼,需要动用ILDASM.exe,虽然看到的是IL代码,但是我们还是能过推出C#编译器做的工作。C#把上面的code实际上变成了类似下面的样子再编译。

class Program
{
    private sealed class <c>__DisplayClass1
    {
        public int x;
 
        public void <GetAction>b__0()
        {
            Console.WriteLine(x);
        }
    }
 
    public static Action GetAction(int arg)
    {
        var captured = new <c>__DisplayClass1();
        captured.x = arg;
        Action action = captured.<GetAction>b__0;
        return action;
    }
 
    public static void Main(string[] args)
    {
        Action action = GetAction(13);
        action(); 
    }
}

可以看到,C#编译器产生了一个新的nested class叫<c>__DisplayClass1,这个nested class中有一个就是Closure内容的方法叫<GetAction>b__0,光看这两个古玲精怪的标识符就知道这肯定是C#编译自自动产生的名字了(实际上上面的code是没法编译的,因为标识符中不能包含"<"和">"字符,这么写是因为C#编译器产生的就是这两个名字),<c>__DisplayClass1还有一个public的data member叫x。

现在,C#如何实现Closure就一目了然了,每一个被Closure使用的“局部变量”,因为Closure“抓住”了,所以叫Captured variable,C#必须保证captured variable的life time必须不能比closure短,所以他们实际上不再是局部变量,而是nested class的成员变量,Closure也不能简单地处理为一个static method,而是变成了nested class的一个非静态成员方法,这样它总能访问同属一个class的data member,这样Captured Variable的life time问题就被解决了。



Browser: F5 vs Ctrl+F5 Revisited
12月 22, 2008, 5:11 上午
Filed under: 技术体会

在实验Microsoft的Fiddler网络嗅探工具的时候,意外地发现,在Google Chrome里面按Ctrl + F5居然一样会发送If-Modified-Since header,这和我一直理解的浏览器行为不一致,之前我在IE6和FireFox 2.X中做过实验,按F5(Refresh)会在发送的HTTP请求中包含If-Modified-Since,但是Ctrl+F5不会。

image

试验了一下Firefox 3.X,行为和Firefox 2.X还是一致的,但是IE8 Beta的行为和Chrome一样,这样的效果就是Ctrl + F5下网页的刷新时间可能要快一些,难不成这两个浏览器为了提高效率修改了Ctrl + F5的行为?

研究了一下这个现象,有人也发现了这个问题,原来在IE7和IE8中,如果焦点(focus)在网页中,Ctrl+F5还是行为如往常的,但是如果focus在URL输入栏中,Ctrl键时被忽略的,所以Ctrl+F5就和F5的行为一样了(这是一个bug还是一个feature?)。我一开始的实验就是因为focus在URL栏中所以Ctrl+F5也发送If-Modified-Since。

但是对Chrome,就没有定义Ctrl+F5的行为,只定义了F5就是Reload,不管焦点在哪里,Ctrl+F5就是和F5行为一样,至少对于我现在使用的1.0.0154.36版本还是这样的。也许Chrome是有意去掉了Ctrl + F5的功能,这样虽然行为和其他浏览器不一致,但是有时候会让用户感觉它要快一些:-)

相关文章: Browser: F5 vs Ctrl + F5



Where to Put Javascript in Web Page?
12月 20, 2008, 9:48 上午
Filed under: 技术体会
前几天写完“Slow Web Site Won’t Win”之后,有朋友和我讨论,说不大理解Steve Souders的Best Practices中的一条“Put Scripts at the bottom”,因为通常都是把javascript放在<header></header>中的。后来我看了一下Google Maps和VE的那两个例子,其实都没有遵从这条规则,所有的script都是写在了header部分,我再看了一下Yahoo!的主页,也是这样,但是YSlow还是给这些网页打出了A级的高分,我想YSlow并没有严格检测这个规则的遵从程度。

问题说回来,到底应该把Javascript放在哪里?是放在header部分,还是放在bottom(但是必须在</body>之前)。如果从只考虑效率的角度,的确应该将script尽量往后放,但是前提不要比依赖他们的元素还要往后,不然就有可能造成功能上的问题,比如有一个button的点击会引发调用一个javascript函数Button_OnClick,如果你把这个函数的javascript定义放得比这个button还靠后,那么就有可能浏览器在已经Render出这个button的时候,这个函数还没有load,这时候用户去点这个button就会出现我们不想出现的问题。

毫无疑问,应该尽量将javascript放在.js文件中让HTML去引用,这样的好处是将问题分割开了(Separation-of-Concerns),而且可以利用Cache,如果我们将.js文件的HTTP响应Header设对,就可以让浏览器Cache这个.js文件,如果javascript很庞大,那么PLT 1.5和PLT 2.0的提高就很可观了。好,这样的话.js文件中的内容就成了library一样的东西,library就应该是时刻可用的,所以最好还是将其放在Header部分。总之,从纯粹效率的角度出发,我们应该将javascript尽量往后放,但是从功能的角度出发,我们又必须将javascript放在header中,只要合理的设置HTTP Caching,这样做并不会影响网页效率,我想这也是YSlow依然给了这些网页A分的原因。



暗室:Visual Studio
12月 19, 2008, 4:51 上午
Filed under: 工作心情

Mac上有个应用程序叫Dark Room,没有花哨的外观,没有菜单,没有Ribbon,就是一个全屏的文本编辑器,就是让用户在暗室一样的环境中专注于写作。我觉得写Code也可以这样,Visual Studio的编程环境功能很强大,大大小小窗口太多,有时候我想专心写一段code,除了Text Editor窗口之外的就显得多余。

image

Visual Studio提供了全屏编辑功能,但是使用这种Dark Room模式,最好就要熟悉Visual Studio中常用功能的键盘快捷方式(short-cut),不然没起到Dark Room的作用。应该熟悉的第一个Short-cut就是进入和退出Full-Screen模式的命令:Alt + Shift + Enter。

image

这样,屏幕上只你需要写的code,你就专心编辑当前文件,不要关系其他的部分。

当然,如果要做到真正的“暗室”,也许应该把背景色调成黑色。据微软的研究结果,亮色的界面要比暗色的界面给用户更加快速的感觉,也许这也是大家抱怨Windows Vista比较慢的部分原因:-) 我也不希望去修改Visual Studio的缺省颜色配置,因为我知道要达到这样的“暗室”效果,还有一个选择,就是使用VisVim,很简单,在Visual Studio中呼出Vim来编辑文件,虽然没有了VS的Intellisense等功能,但是拥有了Vim的所有功能。

image



Slow Web Site Won’t Win !!!
12月 16, 2008, 2:27 下午
Filed under: 技术体会

Slow Web Site Won’t Win !!!

Slow Web Site Won’t Win !!!

Slow Web Site Won’t Win !!!

这句话应该再重复一万遍,直到这个观念彻底植入我们的脑袋里,彻底融入我们的血液里面,彻底嵌入我们的DNA里面,因为我们做事情是为了Win,不是Lose,Slow Web Site Can’t Win !!!

传统软件开发中,习惯上把Performance(效率)和Feature(功能)分开考虑;在网络服务中,performance就是最重要的一项feature。你的网络服务再花哨,如果用户感觉慢的话,就会转向其他的服务提供商。看看最近Redfin放弃Microsoft的Virtual Earth(VE)完全使用Google Maps,你就知道这是事实。

“In the end, it was speed, speed, speed that convinced us to switch. In our worst case scenario of 500 pushpins on the map in IE6, GMaps is 385% faster.”

Redfin也承认,VE能够提供比Google Maps更多的feature,但是速度(Speed)让他们作出这个决定。

道理大家都应该明白,现在让我们来看看为什么VE比Google Map慢。

就Redfin和其他Map API的使用者而言,使用VE或者Google Maps,其实就是在自己的网页里面嵌入VE或者Google Maps提供的javascript,网页在被访问的时候,这些javascript从VE或者Google Maps的服务器获取图片等信息,让后把地图绘制在网页上。

Redfin称Google Maps有时候能够比VE快385%,我敢断定,这不可能主要是因为服务器端的差距,这么大的差异肯定主要还是客户端的差异。因为Redfin已经不使用VE API了,所以我只能自己制作两个网页,分别使用VE和Google Maps来显示同一地区的地图,然后来分析一下二者的差距。

根据VEGoogle Maps的API介绍,我写了两个网页,一个是使用VE API,内容如下

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
   <head>
      <title></title>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
      <script type="text/javascript" 
        src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script>
   1:  
   2:       <script type="text/javascript">
   3:       var map = null;
   4:             
   5:       function GetMap()
   6:       {
   7:          map = new VEMap('myMap');
   8:          map.LoadMap(new VELatLong(37.4419, -122.1419), 13); 
   9:      
  10:       }   
  11:       

</script>

   </head>
   <body onload="GetMap();">
      <div id='myMap' style="position:relative; width:500px; height:300px;"></div>
   </body>
</html>
      

另一个是使用Google Maps API

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <title>Google Maps JavaScript API Example</title>
    <script 
      src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAmc7cFR5N2xuW1oVcfSwUZRTxMn_a2DsXeSJHJaVdHSoqU_SH6xRpWZ3jGCmH67BN_Er0NLwhihDqsw"
      type="text/javascript"></script>
   1:  
   2:     <script type="text/javascript">
   3:  
   4:     //<![CDATA[
   5:  
   6:     function load() {
   7:       if (GBrowserIsCompatible()) {
   8:         var map = new GMap2(document.getElementById("map"));
   9:         map.setCenter(new GLatLng(37.4419, -122.1419), 13);
  10:       }
  11:     }
  12:  
  13:     //]]>
  14:     

</script>

  </head>
  <body onload="load()" onunload="GUnload()">
    <div id="map" style="width:500px;height:300px"></div>
  </body>
</html>

这两个网页都只是用了最简单的API功能,用同样的Zoom显示中心坐标为(37.4419,-122.1419)位置的地方,那里就是著名的的Palo Alto市,你要是不知道Palo Alto就该补补计算机科学的历史课了:-)

通过浏览器分别访问这两个网页,VE的显示如下

image

Google Maps显示如下

image

两个API都是用缺省参数,看起来VE提供的内容比Google Maps要多啊,但是不是这些内容导致了效率的差异,因为这些多出来的道路名称都是在Server端就render进图片里面去了。

有一个分析网页效率的利器FireBug,听这名字你就该猜到这使一个FireFox的插件,再配合使用YSlow,我们的家伙什就齐备了。顺便说一下,YSlow的作者Steve Sounders在创造YSlow并提出一系列提高网站效率建议的时候,还供职于Yahoo!(要不然怎么叫YSlow呢),现在他已经加入Google了,但是他提出的这些建议是很浅显易懂而且天下人皆知的,这样一个Web Site Performance的专家加入了Google不能成为我们的performance就可以比别人差的理由,更不能成为不使用他提出来的Best Practices的借口

我们通过FireBug来看看这两个网页的效率,也就是PLT(Page Load Time),有三个方面,一个是PLT 1.0,也就是用户首次访问一个网页的时间;一个是PLT 2.0,这个是用户第二访问网页的时间;还有一个PLT 1.5,是用户在浏览器中按F5(注意,不是Control+F5)做Refreshi的PLT。因为存在浏览器缓存(caching)的存在,所以基本上PLT1.0 >= PLT1.5 >= PLT2.0。我重复测试了几遍,平均下来是这样的一个结果:

 

VE

Google Maps

PLT 1.0

~5.5 seconds

~3.5 seconds

PLT 1.5

~2.2 seconds

~0.6 seconds

PLT 2.0

~0.4 seconds

~0.6 seconds

似乎各方面VE均落后于Google Maps,尤其是PLT 1.5。

YSlow可以给网页打分,这个打分和实际的PLT无关,只看网页结构是否有利于更快的PLT。YSlow给VE打出86分B级

image

YSlow给Google Maps打出95分A级

image

VE和Google Maps的结果差9分,差了一个级别,主要就差在这几个方面

  • Add an Expires header
  • Gzip Components
  • Put CSS at the top
  • Configure ETags

第一条"Add an Expires header"和第四条"Configure ETags",是直接导致VE的PLT 1.5和Google Maps相比不是一个数量级的原因,之前我写过两篇文章解释这个问题:

Config HTTP Header For Better Client Performance

Browser: F5 vs Ctrl+F5

第二条"Gzip Components",是因为VE使用了一个javascript文件http://dev.virtualearth.net/mapcontrol/v6.2/js/atlascompat.js,这个文件没有被压缩来提供下载,这个文件18.7K,不小了,应该gzip。

第三条"Put CSS at the top",这点没做到倒不会影响下载速度,但是会影响网页的显示速度

我做的实验只涉及最简单的例子,但我相信在这两个Map API的更复杂的使用中,这样的差距依然存在,因为明显双方对Best Practices的使用有差距。如果按照已知的Best Practices来做,网站的效率在无需改变Server响应速度的情况下,给用户感觉的Performance也可以大大提高。

“Slow Web Site Won’t Win !!! ” 明白这一点,任何一个网站服务开发者都该好好考虑一下如何提高自己网站的效率,要么在快速中生存,要么在慢速中死掉,没有别的选择。



Don’t Be Vulnerable To Cross Site Scripting Attack
12月 15, 2008, 6:37 上午
Filed under: 技术体会

Cross Site Scripting(XSS)是一种很危险的攻击模式,不过坏人们之所以能够发动XSS攻击,是因为某些网站开发者的错误。

很明显,这个问题还不是说有的程序员都意识到了。Microsoft最近发布了一个Open Source的基于ASP.NET MVC的Blogging/CMS系统Oxite,我觉得算是使用ASP.NET的一个范本,下载下来试用了一下,感觉很有可称道之处,但是居然有明显的XSS缺陷。

启动Oxite,用一个用户名登录进去,创建一个新的Blog,在文本部分,我们输入这样一段

<script>alert("All your bases belong to us!")</script>

如下图所示,然后发布。

image

再访问这个Oxite首页的话,就会弹出一个对话框。

image

在这里,这个对话框只是吓唬吓唬你,你没有真的丢掉任何一个基地,但是既然这个弹出对话框的javascript可以被执行,那所有的javascript都可以执行,包括把你的authenticate cookie发送到某个坏人的email或者网站上去,这样,坏人就盗窃了你的身份。

试想,在这个bug没有fix的情况下,Oxite被微软来部署来作为blogging system,某个坏人注册了一个用户,在微软的网站上发布了一个利用XSS盗窃cookie的文章,使用email或者spam的方式诱使其他良民用户去访问这个恶意文章,就将用户在微软网站的cookie盗窃了。出现这样的问题,是微软网站的责任,因为存在让XSS利用的漏洞,我们不希望这样的事情发生。

出现XSS的根源,在于代码(code)和数据(data)混杂在一起了。在一个网页中,可以包含code(比如javascript),也可以包含data(html tag中要显示的文本),在上面的例子中,我们本意是在Blog Post中显示“<script>alert("All your bases belong to us</script>”这句话,把它当data,但实际上它成为了code,被浏览器当作嵌入的javascript执行了。

要对付XSS,方法很简单,那就是使用

Html.Encode

将文本做Html.Encode之后,它就绝对不会是code,只可能是data了,比如上面的的javascript被Encode之后就变成

&lt;script&gt;alert("All your bases belong to us!")&lt;/script&gt;

这样的字符串不会被浏览器当作javascript来执行。

以往在ASP.NET开发中,这种错误不容易犯,是因为微软提供的的可拖拽的Web Control在rendering的时候都考虑了这个问题,但是很多这些Control都是依赖于ASP.NET的ViewState和Postback功能,在ASP.NET MVC中,ViewState和Postback功能不能再用了,也就是说很多以前的Web Form Control在MVC中没法用了,ASP.NET MVC让开发者需要直接和HTML打交道,这时候就尤其要注意不要让开发的Web Application可以被XSS利用,对每个输出到网页的data做一次Html.Encode,就能不被XSS攻击



Scrum in Under 10 Minutes
12月 15, 2008, 1:55 上午
Filed under: 工作心情

这是Axosfot的CEO制作的一个视频,用了8分钟时间,把SCRUM讲的很清楚。

有一点值得hightlight,做任何估计(Esitmate),要利用至少3个头脑,精确的估计来自于多方面的考虑问题。一个人也许也许能够偶尔精确估计一个工作,但是不可能每次都对,而且,不到事情结束,我们不知道这个估计准确与否,所以,不要冒险让一个头脑进行估计,要用一个Team的头脑去估计。