Archive for January, 2008

友谊?

Saturday, January 12th, 2008

有一句流行的黑话,形容友谊:一起同过窗,一起扛过枪,(还有两句不太和谐,想知道的人自然有办法知道)

稍微花点时间回顾一下,作为具备一般性收入从事一般工作过着一般生活的普通人,与朋友、同事在一起的除工作外的生活是什么?KTV或者其他类似的娱乐项目?健身?吃饭?再回忆一下,你与同事朋友们之间的这些社交活动,你还记得多少?

我的回答是,那些活动在大脑里只能存在一个模糊的轮廓,具体的记忆几乎没有。我更记得的是与他们一起工作的日子。

作为我一贯的写作风格,还是说一段小故事。

去年7月开始的时候,由于种种原因,我重新开始玩魔兽世界。在三区找了一个人最少的服务器,开始了一个联盟的圣骑士。圣骑士在20级的时候有一个史诗级别的职业任务,这个职业任务有一步需要到影牙城堡去找点东西。当时我与一个20级不怎么会玩的战士,20级的新手法师从铁炉堡出发,一路穿过丹莫罗,萨尔多大桥,湿地,阿拉希高地,南海镇。稍微了解这个游戏地形的人应该知道有多远……他们俩人(战士是女生)陪着我从一开始就跑过去,一路上见到了湿地的阴晦,阿拉希瀑布的壮观,一边体会着游戏的景色,一边聊天。最后到达了影牙城堡。影牙城堡是适合18~23级的5人副本,里面的怪都是精英级别——血量以及各项攻击、防御属性比它同级的怪要高的多。我们三人死了很多次,每次都停下来商量怎么打,不知道多长时间后终于拿到了任务物品。

游戏中我不知道他们怎么想的,现实中我们从未谋面,后来战士在43级的时候AFK(注:离开),在以后的三个月里从未上过线;法师也AFK很久。但我对这个过程却一直铭记于心:有一段不是很容易的经历我们曾经一起度过,我们每个人都为此付出努力。虽然不是在现实——但游戏中的体验谁又能说不是一种现实呢。

类似的体验不可多得。后来我还有其他的帮忙,满级的角色来帮我完成,他们毫无难度的完成了任务,我也没有那种一起完成事情的激动;帮其他的低级别小号做任务,自己没有什么成就感,更不谈记得那些我帮助过的人了。

回到工作中吧。IT从业者是脑力劳动者。我们无法知道,一个人在看起来冥思苦想的时候,他究竟是不是真的如此。然而我们可以知道(或者明显感觉到)的是,花一段时间共事,这个人到底有没有如他看起来那样努力工作。每天上班,第一件事情是开浏览器看新浪,还是看前天的构建结果?是在构建的间隔扫一眼娱乐头条,还是与你身旁的同事讨论有没有更好的解决方案?一个人工作的时候,是做着与工作相关还是不相关的事情?这些问题涉及到拷问一个人的专业精神,然而我却觉得,他更影响到团队成员之间的友谊。成员之间交流的时候,更多的是看双方共同的体验。一个人在工作的时候他的同事在做别的事情,他们之间在同一件事情上不会产生共鸣。如果一段时间都两个人都不在一个共同的场景中,那么这段时间他们不会产生共鸣。如果这样的次数多了,勤奋工作的一方很快对不怎么勤奋的一方不信任。而信任,才是友谊的根本。

再来一个很简单的问题:在过去的从业经验中,你对你的同事的回忆,集中在哪些方面呢?技术上的争执,一起加班,……

毫无难度的共事会让记忆匮乏,毫无冲突的共同经历会让经历平淡。友谊是建立在一起处理那些高难度的工作上。所以,善待与同事一起的时间吧,若干年后,你们还有记忆一起分享。

构建基于慢速网络的实时应用

Thursday, January 3rd, 2008

HTTP连接往往被考虑为慢速的。通常不会考虑在之上构建所谓实时应用。然而玩过网络游戏的人都知道,除了类似于CS之类实时性要求非常高的外,延迟在500毫秒以下是可以继续游戏的,而750毫秒以下也可以勉强凑合,如果放到网页中,需要用户之间实时交互的应用延迟在1秒左右完全可以接受。据我的检测,国内绝大部分ISP到用户桌面的延迟远远小于这个数字。技术催生需求,这种情况下我们可以考虑利用这些特性来做一些有趣的应用。那么在这种情形下,我们该如何编写具备实时性的客户端代码?

在前面讨论的双向流模拟同步HTTP连接中,我提到了Bidirectional-streams Over Synchronous HTTP (BOSH)。然而经过验证,这种通用的设计在项目设计中并不吃香。原因之一是过于复杂,无论是服务器端还是客户端。如果有成熟的服务器端还好,否则要自行实现双通道HTTP连接的控制,这其中设计大量的低级Socket以及线程操作,相当需要技巧。在此之上,如果有更复杂的伸缩性需求,整个实现将会使梦魇。实现的复杂性同样会出现在客户端。客户端需要控制消息的拆包、封包,更高级些,能够精巧的控制Http连接(基本上是XMLHTTP的连接)个数。这些也需要相当的功底。

有一种更加简单,并且容易实现的方式,经过我的实验,能够完美的应用到新的应用中,能够满足实时性的要求。基本原理就是:

当上一个请求返回之前,不要发起下一个请求。一旦上一个请求返回,立即发起下一个请求。

这是一个非常简单的设计。但非常有效。写成代码就是:


function forever_request() {
new Ajax.Request('url', {onCompleted: function(response) {
//do your stuff
forever_request();
}})
}

这个确实很简单,但这不是构建实时应用的全部。实时意味着,大多数的用户操作需要广播到所有当前可见用户的桌面上。理想情况下,界面只是一个哑终端,只是如实的表现服务器端传递过来的消息。forever_request方法有两个作用:用来发送消息以及获取服务器响应。增强后的forever_request方法就是:


function forever_request() {
new Ajax.Request('url',
parameters: pendingMessages.pickAll().serialize();
{onCompleted: function(response) {
responseMessage(response.Text);
forever_request();
}})
}

responseMessage方法负责对从服务器端收到的消息进行展示。例如,某一次的请求中,他得到了类似于userA MOVE 3,4的消息,客户端只需要将userA移动到3,4的位置。

pendingMessages负责收集所有的用户操作。为了节约网络流量,并非每一次的用户操作都将发起一次网络请求,而是将其压入到pendingMessages中。例如,在3秒内,用户说了一句话,点击了另外一个用户查看详细信息,pendingMessages中就有了类似于如下的记录:

SAY 大家好啊
GETINFO userB

在下一次请求,这些消息被发送到服务器端,用户可以得到响应。

这是基本的实现了。可以看到这种实现非常简单,任何一个具备基本javascript技巧的人都可以在半天内写出实现来。然而这仍然不是全部。构建基于慢速网络的实时应用需要我们对流量、并发数非常在意。只要用户察觉不出来,尽可能少的数据,尽可能少的与服务器交互。现在带宽资源非常昂贵——共享10M与独享10M之间的价格差别可能让你咂舌。我们可以引入ConnectionManager对forever_request方法进行统筹调用,例如,当用户不怎么活跃(Idle)的时候降低更新量,尝试将更多的message pending并发送,等等。

2008 – 破冰之年

Thursday, January 3rd, 2008

不像gigix, 动不动来个许愿,我翻看了一下从2004年到现在的blog, 几乎没有任何关于许愿的帖子。这不是意味着自己没有计划,相反,而是将计划看得太重要,重要到以至于在新年到来的时候做回顾,都不曾记起曾经计划过什么。

2007年碌碌却无大作为。buffalo成功发布新版本,我却莫名奇妙的在后续的日子里做WPF富客户端应用开发到年底,看起来要做到明年;因为不想某人而开始玩魔兽世界,却有了做企业应用完全没有的新的技术体验。去软件开发2.0大会做关于混合语言开发的演讲,或多或少找到了技术传道者的感觉。

然而有太多的东西盘旋在脑海挥之不去:做一些用户群更广泛的东西是我一直的想法,然而这些想法在没有被天才般的同事打败之前,就被自己缺乏行动力的想法打败。太熟悉,太聪明并非都是好事。至少从外观看来,我除了工作本身,没有多少值得提及的东西。

另外郁闷的是,活动的圈子小了,转来转去,看来看去,都是熟悉的人名,熟悉的字眼。

2008对我而言,将是破冰的一年。让想法盘旋而去的最好做法是让它实现。无论是技术的或是非技术的,这里列举余下:

- 一个成熟到至少100人在线的应用 [0/1]
- 参加一次义工活动(组队) [0/1]
- 写一本非技术的书 [0/1]
- 每周至少一篇Blog [0/51]