巴别塔上的雇工


红螺寺
6月 30, 2007, 8:00 上午
Filed under: 山河好大

上上周做Team Building的时候去了一趟红螺寺。

正门的牌坊,”刹巨北京”,真是有气势啊!哦,应该是“京北巨刹”:)

100_1456

传说中,玉皇大帝的两个女儿下凡到此,化身为两个红螺,为名造福,所以当地人民造了一座庙来供奉她们。

100_1467

旁边的亭子里有铜钱卖,据说如果能够击中桥下大铜钱中间的铃铛,就会财源滚滚。

100_1464

不过路标上的翻译真有意思,Gold Money Hole:)

100_1500

路标还标示有一处景观叫“红螺塔”,我们还很是兴奋地前去观摩,一位有塔可爬了,哪知道就是这两座小塔。

100_1473

沿着山路往上,突然路被荆棘堵住,后面一条横幅上隐隐有字。

 100_1482

翻过去一看,原来是“地界纠纷,游客止步”。这个景点跨两个村,两村之间对门票收入没谈妥,导致这种状况。

100_1489 

还有一处“罗汉园”景观,倒是真的有500尊罗汉,不过都穿这披风很搞笑,让我想起《斯巴达300勇士》。

100_1495

环境还是比较清幽的,此行让人产生退出江湖的念头:)

100_1499



iDay
6月 29, 2007, 10:26 上午
Filed under: 工作心情

iPhone正式发布之日,虽然和中国市场没什么关系,但是通过Internet也可以感受到铺天盖地的宣传气氛。

Howstuffworks的主页是How the iPhone Works

 CNET的主页上,iPhone和Windows Mobile的广告交相辉映,共抢宝贵的网页地皮,不过,至少这次,如网页上一样,Windows Mobile在下风。

连Playboy也跟风了一吧。

 



Continue: The Art of ‘Ware
6月 26, 2007, 1:23 上午
Filed under: 技术体会
继续看《The Art of ‘Ware》,对孙子兵法的解读形式上有点牵强,但是也有那么点意思。

令半渡而击之利”,解读为,对手没有推出产品的时候(没有开始渡河),不要去攻击他;不要等到对手已经完全推出产品的时候(渡河完成)才去攻击他;要在对手产品刚刚发布,但还没有站稳脚跟的时候(渡了一半)去攻击他。攻击得太早显得自己惧怕对手,一个没有发布的产品都弄得自己人心惶惶,随对手吹去,必要的打击只是宣称对手光说不练,当对手的产品真的发布了,缺陷也就暴露出来了(这世界上还没有没有缺陷的产品吧),这时候攻击才是有的放矢。

数赏者,窘也;数罚者,困也;先暴而后畏其众者,不精之至也。”,解读为,如果对手不停的re-org,那说明对手自己都不知道该如何处理问题了。



用C#弄一个有timeout的crawler好难
6月 24, 2007, 8:33 上午
Filed under: 技术体会

.Net拥有强大的类库,其中包括HTTP的客户端实现,用HttpWebRequest和HttpWebResponse两个类,可以轻松实现一个Crawler,但是,离一个真正能够在丛林般Internet中生存的Crawler还差的很远。.Net的HTTP实现比较中规中举,对稍微不完全遵守协议的网站,就会出现问题,这些问题且不说,今天只说说弄一个有完整timeout功能的crawler有多困难。

弄一个有timeout的crawler难吗?应该很轻松,大概的code如下

        public void Crawl(int timeout, string uri)
       {

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);


            request.Timeout = timeout;

	   //other request settings
            ...

            HttpWebResponse response = null;

            try
            {
                response = (HttpWebResponse)request.GetResponse();
                Stream stream = response.GetResponseStream();
                
                // read bytes from stream
                ...
            }
            finally
            {
                if (response != null)
                {
                     response.Close();
                }
            }
       }

HttpWebRequest有一个Timeout属性,可以控制GetResponse的返回时间,当GetResponse返回的时候,HTTP的Headers已经接受到了,然后用GetResponseStream来获得HTTP response 除了Headers部分的内容流,为了达到timeout读取,用多线程控制,和从其他IO流中读数据没有什么差别,不多说了。问题出在GetResponse上,GetResponse是一个同步调用,没获得response headers是不会返回的,当然,有HttpWebRequest.Timeout控制,比如我们设timeout为5秒,5秒没有获得response headers,GetResponse会跑出一个Timeout的WebException,这样至少我们的程序不会block在GetResponse上。

真的是这样的?让我们看crawl一个uri的过程,建立一个TCP连接,发送文本形式的HTTP GET命令,然后等待server从这个TCP连接返回HTTP response,但是,输入的uri字符串很有可能host部分不是IP地址,而是一个域名,就和我们通常用的www.msn.comwww.google.com一样,所以在建立TCP连接之前,要做DNS解析,知道这个domain对应的IP地址。所以GetResponse()实际上要做两件工作,第一,DNS解析,第二,发送HTTP请求。

这个网页,有一段不大起眼的话:

A Domain Name System (DNS) query may take up to 15 seconds to return or time out. If your request contains a host name that requires resolution and you set Timeout to a value less than 15 seconds, it may take 15 seconds or more before a WebException is thrown to indicate a timeout on your request.

也就是说,自.Net 2.0开始,DNS解析部分的工作,可能不受HttpWebRequest.Timeout的影响了。什么样的URL会导致这样的问题呢?为什么是15秒呢?让我们看Windows的DNS解析方式,当然,对于本地没有cache的域名,需要找DNS Server问一问,这是一个UDP报文,发送出去之后,DNS Server可能没反应,也可能发回一个UDP包,说这个域名不认识,当然,不认识不表示这个域名肯定不存在,也许是因为这个DNS Server自己无知呢,所以Windows没有获得一个满意结果1秒钟之后,会再发送一个DNS解析请求,期望DNS Server在过去1秒钟中,和其他DNS Server联系找到了这个域名,如果还是没有结果,会等待2秒再试,这样重复尝试3次,每次的时间间隔是上一次的两倍,总共4次尝试都失败了,Windows就死心了,认为这个这个域名真得不存在,这样总共花费的时间是1+2+4+8=15秒,这也就是为什么上面说最多15秒。

但是如果Windows在一个域中(这个“域”是Windows的域),实际上还不止15秒,假如发动请求的Windows域是mycom,DNS请求的域是www.nosuchdomain.com,Windows在花了15秒处理www.nosuchdomain.com之后,还会尝试www.nosuchdomain.com.mycom, 这样又要化肥十几秒,所以花费了20到30秒之间的时间。

Windows native API gethostbyname/getaddrinfo用的就是Windows的DNS解析机制,很遗憾,这个机制没有可配置的timeout,gethostbyname和getaddrinfo也没有提供参数设置timeout,所以对于一个不存在的域名,就要花费很长时间才能返回。

在.Net 1.X中,HTTP实现通过interop去调用native的gethostbyname来做DNS解析,为了达到timeout的效果,把对gethostbyname的调用放到ThreadPool的线程中去做,timeout一到,不做等候,调用线程继续。这样的实现看似很好,但是如果短时间内有很多这样不存在域名的请求,会把ThreadPool中的线程很快耗光,所以到了.Net 2.0的时候,.Net team决定放弃这样一个hack,直接调用gethostbyname,所以有了上面的MSDN描述。

那怎么样获得一个真正拥有timeout的GetResponse呢?用异步调用HttpWebRequest.BeginGetResponse和HttpWebRequest.EndGetResponse,但是不行,按照Jeff Richter的说法,BeginGetResponse中还是同步调用了Dns.GetHostByName,察看了一下Rotor的code,果然如次,所以对于不存在的域名一样没有效果。

那我们就用Dns类的异步调用BeginGetHostByName和EndGetHostByName来获得域名的IP地址,如果timeout了,就不继续,如果成功了,把uri字符串作一番修改,用IP地质替换域名部分。这看起来漂亮,其实是错的,因为自HTTP1.1之后,容许多个域名拥有同一IP地址,用HTTP请求的Host Header来区分,好,那我们不光修改URI,还把Http的host header修改了不就成了嘛,可惜,request.Headers["Host"] = "xxx"抛出一个ArgumentException,"Host"是保留header,不容许直接修改,这条路也被堵死了。

治标但不治本的方法是,把GetResponse这样的操作放到ThreadPool的线程中去(和.Net 1.x中的做法一样),主线程等待timeout时间,过时不候。说这样治标是因为表面上看真的做到timeout功能了,说不治本是因为如果短时间有大量不存在域名请求,一样会把ThreadPool中的线程耗尽。

那我们就不要让ThreadPool中的线程做这样徒劳的事情了,timeout一到,就强行abort它,让他被recycle,但是,.Net的实现就是,如果某个managed线程在用interop执行native代码的时候,是不会立即被Abort的,必须在线程完成native部分返回managed部分的时候,ThreadAbortException才会产生。既然DNS最耗时的部分在native部分,所以Thread Abort的方法还实行不通。

要根本解决这个问题,只有自己实现DNS和Http的协议部分,完全控制timeout。在wikipedia上介绍crawler的文章中说,世界上几乎没有相同的crawler,我想主要原因就是internet就是一个杂乱的丛林,有众多协议,但是很多协议描述不清晰,又有众多不严格遵守协议的生物,而且每个crawler的需求有差异,所以进化成为了多样的crawler物种群。

 



光阴
6月 24, 2007, 5:16 上午
Filed under: 八卦杂谈
候耀文也走了,享年59岁,真是光阴似箭,一些记忆中都还年轻的公众人物,转眼就成古人了。

又有人拿老候的死因说事,真是没有口德,人都死了,让人家走好不成吗?



魔鬼赛制
6月 20, 2007, 4:01 下午
Filed under: 工作心情
起始赛制为一球制,输者下场。
下一队攻擂者失两球才算输,守擂方失一球即为输。
若守擂成功,下一队攻擂者失三球才算输,守擂方依然失一球即为输。
继续攻擂方赛点依次增加。
若守擂失败,攻擂方成为下一局的守擂方,一个赛点,攻方两个。

这样的赛制让选手守擂越久,压力越大,一球都不能失误。
坚决贯彻了打击球霸的精神,但是真正的球霸是不会被赛制影响的。
Frank和我坚持12球,依次1:0,2:0,3:0,4:0击败挑战者,最后终于1:2失手最后终于2:1失手。



秘闯金三角
6月 19, 2007, 1:04 上午
Filed under: 电影电视
童年的时候电视上就上映过《秘闯金三角》,映像中预告片是够酷的,可惜最后到底没看上,只能听看过的同学眉飞色舞的讨论,让我很是郁闷了一整子。昨晚CHC放了这个片,看了一下,神呐,真不是一般的烂!

套用《全职杀手》中的话:“再烂的电影,预告片都一样好看!”

也许是当年文化作品贫乏,也许是小孩子不懂,这片居然也很有吸引力,当年没看后悔一整子,现在看了后悔一辈子



狙击手(jt)
6月 17, 2007, 8:04 上午
Filed under: 电影电视

之前在BBS上看到不少表扬《生死狙击》(Shooter)的评论,但是看了之后我觉得情节还是比较老套。

开场字幕显示场景在埃塞俄比亚,一段航拍飞越山川、河流、输油管道直到飞到主角狙击手和他的观察员埋伏的山头,的确很酷,但是到这时候就可以肯定,这两个大兵肯定是执行一个项注定要被出卖的任务。果然,出事了之后,美军总部无情地将他们抛弃了,观察员阵亡,狙击手靠着神勇愣是突围而出。

狙击手虽然很有影响力,但也只是IC(Individual Contributor),而且是无名的IC,所以最有可能被出卖,真是一个高危行业,有没有比狙击手更加高危的行业呢?有,那就是狙击手的观察员,因为狙击手背负着回头报仇雪恨的任务,不能随随便便就死了,所以观察员就得死,给狙击手一个沉痛的打击,嘿,俗套俗套:)

接下来的情节更加自然了,主角深受打击,隐居山林,编剧当然不会让这么猛地猛人得一身功夫只用在打猎上,三年之后,有一帮穿黑西装打领自称国安人员的人找上门来,晓以大义,请猛人出山,说保护总统的任务非你不行。不出所料,这种请退休狙击手的任务基本上都是陷阱,不过电影中的主角拒绝了倒不是识破了阴谋,而是不想涉足这些政治破事,当然,受过英雄主义教育的大兵还是忍不住重处江湖了。

黑衣人的情报称,有人计划在总统巡游三个城市之际狙击行刺,主角的任务就是凭他的专业技能寻找可能的狙击地点。主角在三个城市总统露面的地点做了细致观察,最后确定凶手肯定会在费城下手,黑衣人决定布置狙击手狙杀凶手,又热情邀请主角做观察员。主角傻傻的答应了,从高危职业进入更高危职业,果不其然,最后凶手没抓着,自己反被栽赃陷害,身负重伤。

《Shooter》的情节等于是糅合了《Sniper2》的老兵出山和《The Most Wanted》的政治阴谋论,之后的情节就没什么可说得了,狙击手找到一个心地善良的人疗好伤,然后伙同一个政府部门内部的好人,以暴制暴,最终把坏蛋搞定了。

总结:

1. 千万别真把自己当回事,谁要是说什么事非你不可,那可要小心了,有可能就是一个陷阱;

2. 找人做替罪羊找个差不多的就行了,不要找太猛的人,不然很有可能反被蛇咬。



Prosumer, The New World
6月 16, 2007, 4:50 上午
Filed under: 技术体会
看这个很酷的未来世界预言性质的视频。

在还剩2分14秒的位置,“Google buys Microsoft”,提前宣判东家的死刑了:)

“…the world universal content leaders….”

在还剩2分03秒的位置,冷不丁的冒出来一句"…with CNN, BCC and CCTV"
也许视频制作人看好中国好歹有>1.3B人口,CCTV怎么着都有市场,连日新月异的信息时代人物都不敢小觑CCTV,真是牛口塞,不得不服!



连锁反应
6月 15, 2007, 3:19 下午
Filed under: 工作心情
这一周都没有在blog上写什么东西,是因为出了点紧急又棘手的状况要处理,忙了四天。

这个状况的根源在于Windows的一个功能没有提供configuration,调用这个功能的Windows API也不够友好,然后.NET的library需要这个功能的时候,也只好使用这个不友好的API,最后我的C#程序又使用.NET实现,层层传递,一个多年前OS的设计,给多年后的程序造成困难。

一个馒头也能引发一场血案,一个Bad Design会造成一个……