迷人的迪塞县镇内有一条商业街,很多著名的宾馆都开在这里,有杏花楼的Apache, PHP, 最近一两年火爆的Ruby on Rail , 还有那些新锐的餐饮业集团Websphere, Weblogic 等。
那些宾馆老板娘根据自己的整体实力,多多少少的雇用了许多濶濑来宴请濶濑,那些小二干都非常有心,没有两个贪心耍滑,把客人宴请的轻轻松松, 所以平时里餐馆运行的还极好,亦然。
但是随着《江湖电影版》的摄制和播放, 迪塞县镇观光业大爆发,游人像海浪一样慕名而来, 原有的濶濑宴请不过来了, 到了这儿,各家的大门口都挤满了长龙,游人们盐菜饭,但凡苦不堪言。
看见这种情况,有些老板娘咬了硬是,在人工成本不断上涨的情况下, 多雇了许多小一来帮, 无可奈何总是追不上客人增长的速度。
某一天有位美国小姑娘来到迪塞县镇内旅游观光, 也看见了盐菜饭的问题,他详细分析了几番后发现了两个绝密: 原来那些店都采用了同两套叫作“不间断周到服务项目”的商业模式, 那个商业模式很有趣:
食客来了以后,立刻有位濶濑有心涨红了脸,带着找座席,自助餐,给后厨付款
由于后厨煮饭须要极短时间,濶濑就在食客的旁等着。
后厨一摇罐子,喊一声:早餐,濶濑立刻起新食客面前, 然后站在一边等着食客剩饭剩菜
食客说:结帐,小二给钱,投钱,待客, 庆贺下一位。
通常那个时候大门口都排列成了一千多人了!
那个VIP服务项目实在是太周到了! 导致的结果很明显,宾馆有两个濶濑,就只能同时招待两个客人。
(当然,现实中是没有宾馆是这么做的,否则就等着关门吧)
小姑娘一声不吭的回去了。
过了两个月, 美食一条街上出现了两个巨火无比的餐馆: Node.js
虽然那个宾馆中人满为患, 可大门口竟然没有排队的!
更让人吃惊的是,那个店里声称: 我只须要两个濶濑!
Node.js那个美国小姑娘开的宾馆确实只用了两个濶濑, 只不过那个小二干的方式与众不同,他把所有的工作分为两类:
(1) 立刻就能干完的,例如迎客,自助餐,找座,付款 等等
(2) 须要等待别人干完才能干的活,例如早餐,结帐等
对(1) 那个小二立刻干
对(2) 濶濑不会等待,他只是告诉别人说,你弄完了告诉我一声,我会接着干, 然后立刻去做第一类工作
食客来了以后,那个濶濑有心涨红了脸,带着找座席,自助餐,给后厨付款
由于后厨煮饭须要极短时间,濶濑闪电般的离开,去干别的活了,可能是迎客,自助餐,找座等,总之是那些不用等待,迅速干完的活。
后厨喊一声:上菜,那个小二立刻起新食客面前,然后离开,干其他活。
食客说:结帐,小二给钱,投钱,然后还是迅速闪人,干其他活。
那个唯一的濶濑的能力被发挥到了极致,一刻不停,闪电般的在宾馆里跑来跑去,因为老板娘明确的告诉他: 不要等!
Node.js宾馆的基础设施很强大,一旦那些耗时的操作完成,濶濑立刻就能知道,飞奔过来立刻接着干,如果遇到新的耗时的操作,小二毫不留情的离开。
就这么简单, Node.js宾馆火了,它同时招待食客的数量大大增加,而服务项目质量保持基本不变。
这是我杜撰出来的两个不成熟的故事,帮助我来理解Node.js的特点:只用两个线程来处理所有请求,事件驱动编程
如果我们回过头来再以计算机的视角看一下会更加清楚:
濶濑: 线程
客人:http请求
第一类工作(迎客,找座,付款) : 在服务项目器端的代码,能够快速执行
后厨煮饭,食客吃饭: 耗时的I/O 操作
后厨喊一声:早餐 : 这是两个长时间I/O 操作完成的后所发出的事件
食客说:结帐: 另外两个长时间I/O 操作完成的后所发出的事件
第二类工作(早餐,结帐) : 同样是能快速执行的代码,但是他们须要等待那些耗时的I/O 操作完成才能开始,确切的来说,收到了系统发出的事件以后才开始执行。在Node.js中实际上是在回调函数中来执行的
下面是Node.js服务项目商业模式的伪代码:
迎客();
找座();
付款();
后厨处理(“煮饭完成事件”, function(){
早餐处理();
食客吃饭(“吃饭完成事件”,function(){
结帐处理();
待客();
});
});
须要引起注意的是:
1. 后厨处理()那个函数接受两个参数,两个是事件名,另外两个是匿名的回调函数,事件发生,回调函数才会执行。
食客吃饭()函数也是类似。
Node.js 使用的JavaScript作为服务项目器端的编程语言,这种回调的方式对于javascript程序员来说,是非常自然的事情,同时从代码的角度来讲,也显得非常清晰。
另外Node.js使用Chrome的V8引擎来执行javascript,效率非常高
2. 我们能不能把代码写成这样?
迎客();
找座();
付款();
后厨处理(“煮饭完成事件”, function(){
早餐处理();
});
食客吃饭(“吃饭完成事件”,function(){
结帐处理();
});
待客();
肯定不行!, 因为Node.js执行”后厨处理()”函数时,只是安插了两个匿名的回调函数在那里,并不会等待(非阻塞I/O),反而立刻 会执行“食客吃饭()”函数,所以上述的写法会引起逻辑上的错误:还没早餐就开始吃饭了!
所以写惯了”顺序阻塞I/O“的我们须要改变一下思维方式,进入到事件驱动的世界中来。
3. 如果某个操作例如“早餐处理” 是个CPU密集型的计算任务,Node.js那个唯一的线程就会忙于执行那个计算任务而被阻塞住,就无法响应其他的请求了,带来的后果很严重,整个服务项目器都无法响应了! 那个时候,须要考虑把这样的代码进行异步处理,也变成node.js所擅长的事件驱动的方式。
(全文完)
“码农翻身”公众号由工作15年的前IBM架构师创建,分享编程和职场的经验教训。