基于事件的Web应用
传统的Web表单提交就是典型的基于事件的模式。换句话说,在Web表单里输入了很多数据(用户输入文本框,点选复选框,从列表中选中某些项等等),之后这些数据提交给服务器。这个场景中实际是一个单一的程序事件:使用POST方式将表单数据提交。这也是基于Ajax的Web应用的工作原理。
一次性发送大量数据
对于Ajax来说,是可以和基于事件编程扯上一点关系。客户端和服务器端之间有些交互可以认为是基于事件的。典型的场景是输入一个省市代码,发送请求到服务器获得城市和省的名称。这里通过XmlHttpRequest的Ajax并不需要将很多数据一次性扔给服务器。但这并不能改变大部分web应用都是基于页面刷新这种模式的现状。Ajax已经更广泛的用于很多有意思的视觉相关的交互,快速的作表单验证,无刷新提交数据,这样就可以避免重新载入页面。因此,尽管并未通过提交表单来发起一个真正的POST请求,通过Ajax可以模拟POST表单提交。
坦率的讲,这种传统的Ajax交互方式也阻碍了Ajax程序员的创新。每次发送一个请求时(不管请求的数据多么小),都会在网络里走一个来回。服务器必须针对这个请求作出响应,通常是开辟一个新的进程。因此,如果你真正置身于一个事件模型的环境中作开发,你可能需要通过发起10到15个单独的小请求来保持你的页面和服务器之间的联系,服务器也会为之创建10到15个线程(可能更少,这取决于服务器处理新请求时分配线程池的策略),当这个数量乘以1000或者10000或者100000时(译注:每个页面需要10个请求,那么越多用户访问这个页面,所发起的请求个数就会越来越多),就会出现内存溢出、逻辑交错带来的冲突、网络瘫痪、系统崩溃这些问题。
结果是,在大多数场景中,Web应用需要保持对事件的最小依赖。有一个折衷方案,就是服务器端程序的响应返回的不是一个微小的数据片段,而是带有更多冗余数据结构的数据包,通常是JSON数据,这时就又遇到了eval()的问题。问题当然出在eval()身上,但这也和Web本身和服务器线程控制、包括页面和服务器之间的HTTP请求和响应策略(至少在这个场景下)有密不可分的关系。
或许有些人对上文提到的问题不以为然,因为你知道有很多方法来规避直接eval()带来的问题,你会使用诸如JSON.parse()来代替eval()。同样有很多令人信服的论据鼓励我们小心的使用eval()。这些东东都是值得进一步讨论的。但不管怎样,看一看eval()带来了太多类似栈溢出(Stack Overflow)这类的问题吧,你会发现大部分程序员并未正确或者安全的使用eval()。这着实是一个问题。因为太多菜鸟程序员似乎根本没有意识到eval()的问题有多严重。
不断的发送少量的数据
Node带来了架构应用的新思路,我们可以基于Node采用事件模型来架构Web应用,或者说“小型的”事件模型。换句话说,你应当基于大量的事件发送大量的请求,每个请求的数据包都很小,或者根据需要从后台抓取少量数据,而不是发送很少的请求,每次请求都带有大量的数据。在很多场景中,大多数情况下你需要唤醒GUI程序(Java Swing程序员的GUI知识储备可以派上用场了)。因此,当用户输入姓氏和名字后,移步到下一个输入框,这时就已经发起了一个请求来验证输入的用户名是否已经存在。省市代码、地址和电话号码的验证也是同理。页面上每发生一个时间,都会产生一个请求和响应。
这有什么不同吗?为什么Node可以做到,并规避了已有的线程问题?其实Node并没有这么神秘,Node官网充分解释了其哲学:
Node的目标是提供一种构建可伸缩的网络应用的方案,在hello world例子中,服务器可以同时处理很多客户端连接。Node和操作系统有一种约定,如果创建了新的链接,操作系统就将通知Node,然后进入休眠。如果有人创建了新的链接,那么它(Node)执行一个回调,每一个链接只占用了非常小的(内存)堆栈开销。
Node是无阻塞的,不会出现同源竞争线程的情况(Node非常乐于处理即时的请求,发生了什么事情,那就让他发生吧),新请求到达服务器时,不需要为这个请求单独作什么事情。Node仅仅是悠闲的坐在那里等待(请求的发生),有请求就处理请求。用非常简单的代码就可以实现,而不用花费程序员宝贵的精力去实现一整套服务器端逻辑。
没错,混乱不可避免
值得一提的是,非阻塞系统带来的问题也会出现在这种编程模式中:一个进程(非线程)等待一个数据存储操作,这时产生了另外一个抓取与之无关的数据的操作,这个意外的操作会对现有的等待造成影响(译注:作者的意思是说多个操作同时发生或者没有按照预定顺序发生时,会产生混乱,也就是说,操作本身并不是原子性的)。但要注意,大多数基于事件的web编程模式都是“只读的”!你大概也没有遇到过通过“微请求”来修改数据的情况,或者说非常罕见。相反,通过这种请求来验证数据合法性、查询数据的情形则非常常见。这种情况下,最好直接根据请求作响应。数据库本身会作加锁操作,一般来讲,一个优秀的数据库完全可以高效的做到数据操作的加锁解锁,而不用服务器端的程序代码去多做什么。而Node又比操作系统处理线程的保持和释放更加高效,使得服务器不必单独为“web响应”开辟一个进程。
此外,Node也计划实现“进程分支”(process forking),HTML5 Web Workers API为更复杂的进程控制提供了引擎(规范)支持。同样,如果你采用基于事件的模型来架构web应用,你的程序可能至少有100多个场景需要线程的支持。最终你会发现,你的编程思路和思考问题的方式发生了改变,你的注意力将放在服务器端处理请求的逻辑上,而不必在乎Node如何工作。
出处:Taobao.com UED Team
责任编辑:bluehearts
上一页 什么是Node? [7] 下一页 什么是Node? [8]
◎进入论坛网页制作、WEB标准化版块参加讨论,我还想发表评论。
|