在内部网(INTRANET)上基于WEB构建MIS系统时,一个绕不开的难题就是:大批量数据录入怎么解决?这个问题如果不处理好,不仅会成为操作员的噩梦,还会给WEBSERVER和数据库带来沉重的负担。
为了解决这个问题,我们设计了一种多框架结构。简单说,就是把应用里的各种功能先拆开,然后交给不同的框架去各干各的。这种分工协作的方式,可以让操作界面上的数据做到“受控的部分刷新”——网络数据传输量降下来了,各个部分的处理时间缩短了,WEBSERVER和数据库的压力自然也轻了一大截。

在这个多框架方案中,与数据库的交互工作是由ASP(ActiveX Server Pages)和ADO(ActiveX Data Objects)来完成的。至于框架之间的协作问题,则用DOM技术来解决。
关键词:多框架
*注:本文中讨论的方案,WEB服务器为IIS4.0,客户端浏览器为IE4.0及以上版本。
一、问题的提出
最开始,我们在内部网上用ASP和ADO技术设计基于WEB的MIS系统时,惯性思维让我们沿用了设计普通WEB站点的那一套习惯。但随着项目越做越深,我们渐渐发现,这变钱成的系统结构根本扛不住大批量数据录入的压力——这就逼着我们必须从整体设计结构上重新构思。
MIS系统和普通WEB站点最大的不同,在于它们处理信息的方式。普通WEB站点主要是“发布”信息,采集信息只是它很小的一部分功能,而且那些采集功能都相对简单。但对于MIS系统来说,信息的采集和维护工作占了相当大的比例,其中不乏一些复杂且大批量的数据录入功能,这些恰恰成了系统设计中最头疼的难点。
二、问题的分析
当一个系统涉及复杂且大批量的数据录入时,响应速度和界面体验这两个问题就会立刻浮出水面。在以往的C/S模式下,客户端的录入速度基本由录入员自己掌控。一般来说,一旦录入员熟悉了操作,录入速度基本不受系统限制。但在WEB模式下,页面是全刷新的——每次交互操作,至少得重新加载一次整个页面。这个刷新动作不仅更新了数据,还把界面上那些固定的内容也重新加载了一遍。对普通用户来说,几秒钟的刷新可能感觉不到什么;但对于要长时间、高强度操作的录入员来说,每录入一条数据就得等半天(有时2-3秒,有时十几秒甚至几分钟),这绝对忍不了。就算网络带宽足够宽,页面重载也会产生那种一闪一闪的闪烁效果,录入员不得不重新识别页面上的每一个元素,这既拖慢了录入速度,也容易让眼睛快速疲劳。
三、解决方案
如果能在“不”刷新页面的前提下,实现页面数据的“快速更新”,问题不就解决了吗?而且由于页面不用刷新,那些必须由服务器保存的状态信息也可以留在客户端,服务器的负担也能减轻不少。那么,怎么才能做到呢?接下来我们详细聊聊。
1. 设计思路
首先,我们确立了用多框架来构建页面。其实框架(Frames)不是什么新鲜玩意儿,很多网站早就用它在页面上显示固定标题和菜单了。使用框架可以减少一些页面的重复访问。但如果结合DOM(Document Objects Model)来使用,框架就能完成很多更细粒度的工作。
按照DOM的定义,框架可以被当作一个对象。假设我们建了一个框架,取名叫A,那么对于生成这个框架的页面来说,A是Frames集合中的一个成员;而对于A里的页面来说,A又相当于window对象。所以,虽然框架之间不存在从属关系,但它们可以通过父页面(对象)建立彼此之间的联系。
如右图所示:框架之间是可以相互控制和传送数据的。
1)在框架A中,用的是最常用的框架控制方式——用 来控制B框架中的页面重载。
2)在框架B中,可以通过按钮的点击事件来控制框架C。这种控制是通过DOM来实现的(假设B中按钮Name值为"B1")。
控制C中的URL,在按钮的ONCLICK事件中加入这段代码(VBScript):
sub b1_onclick
set Bframe = parent.B
Bframe.location.href = "URL"
End sub
控制C中的文本框内容,在按钮的ONCLICK事件中加入这段代码(VBScript):
sub b1_onclick
set Bframe = parent.B
Bframe.document.all.txt1.value = "刘念"
'txt1是C框架中文本框的Value值
End sub
2. 新的框架结构
如上图,我们定义了一个新的框架结构。除了原先用来放置一、二级菜单的MENU1、MENU2,以及用来放置三级菜单及具体应用功能的Aapp之外,还新增了三个专门处理数据的框架(在上图中用虚线表示)。这三个框架不需要界面,在应用执行时是看不见的。它们与Aapp框架分工合作,完成具体的功能。
- Aapp: 针对具体功能的界面和专用控制脚本
- Bfun: 客户端公用函数和全局变量
- Cbuf: 数据集合存储缓冲区
- Dcom: 服务器端命令执行结果存储缓冲区
在系统中,根据生存周期按 Bfun → Aapp → Cbuf → Dcom 的顺序从大到小存放变量和数据对象。具体约定如下:
- Bfun: 系统级全局变量。如:用户的登录信息和操作记录。
- Aapp: 功能级全局变量。如:步骤状态参数、功能常数。
- Cbuf: 如果一个功能在操作上存在多个步骤,在其中不确定的连续几个步骤中会用到的公共数据就保存在这个框架中,比如一个缓冲表。
- Dcom: 针对Cbuf,此框架只保存在多个步骤中的一步里需要用到的数据。如:函数计算结果。
Cbuf及Dcom框架中保存的数据主要从服务器上取得。
3. 程序流程说明
在一个具体的功能中,Aapp负责控制整个程序流程。它通过对象关系来获取Bfun中的变量值或调用Bfun中的函数。而Cbuf和Dcom里会包含一个完整的服务器端处理流程,Aapp在适当的时候将业务流程控制权交给Cbuf或Dcom,Cbuf或Dcom在流程执行完毕后,必须把控制权还给Aapp。借助DOM中对象的方法与触发事件,Aapp可以实现部分数据更新,就像一个C/S模式下的客户端程序。
如上图,Cbuf与Dcom承担了与WEBSERVER及DATABASE的数据交换工作,这使得Aapp在第一次被装入后,只需要在客户端浏览器中运行。这样,Aapp里的主要界面就不需要再刷新了,页面刷新带来的延迟和闪烁问题也就避免了。而Cbuf与Dcom里可以只根据约定格式返回数据和一个事件触发脚本,数据传输量可以根据需要降到最小;同时,因为Cbuf与Dcom没有可视界面,它们在浏览器中的加载速度也是最快的。另外,Bfun中保存了大部分的函数和变量,即使Aapp的页面需要重载,也只需要重载该页面专用的一部分内容。
4. 数据存储格式约定
将数据写入Aapp界面中有两种方式:
- 一种是在Cbuf与Dcom中定制脚本,把数据直接写到Aapp里;
- 另一种是由Aapp中的脚本去读取Cbuf与Dcom中的数据,再写到自己的界面上。
两种方法最终都要保证Aapp能拿回程序流程控制权。
当从服务器取到的数据量较少时(比如只有一条出错提示信息),前一种方法是可行的。但当取回的是一个数据集合(比如多行的记录集)时,前一种方法会导致控制脚本过长,灵活性也不如后一种方法。而且按照各框架的分工,数据的控制功能本来就应该由Aapp来完成。所以,后一种方法是数据控制的主要手段。但采用后一种方法,必须在Cbuf与Dcom中定义好数据格式。
数据量少的时候,用变量来保存数据就行,变量名可以在提交URL时定义,也可以使用默认变量名。两种方式的性能差别不大,具体用哪种可以根据个人喜好来定。
当数据量比较大时(最常见的情况是从服务器取回一个包含若干行的记录集),可以用表格来保存数据。具体格式如下:
假设在提交ASP文件的URL时定义的表格对象名为rsTest,那么会返回两个表格对象:rsTest和rsTestStru。
- rsTestStru: 用来存放记录集的列属性数据。这个表由固定的五列组成:
- 1. ID: 列顺序号
- 2. NAME: 名称
- 3. TYPE: 数据类型
- 4. LENGTH: 长度
- 5. PREC: 小数位
rsTest: 用来存放记录集的各行数据。
在DOM中,表格对象的行和列都有对应的对象集合。通过指定行和列的序号,可以很准确地定位到任何一个数据元素,再结合innerText属性,就能取出想要的数据。不过,DOM并没有提供对表格元素进行排序和查找的方法,所以这方面的函数脚本得我们自己来写。
当然,对于实际的WEB-MIS,还需要考虑ASP及数据库方面的程序优化问题;一些额外的功能,比如打印控制,依然需要借助ActiveX或Ja va Applet来实现,这里就不展开讨论了。
四、应用实例
这个方案在“深圳市自来水公司管理信息系统(SW-MIS)”的“抄表收费分系统”中得到了实际应用。其中的“抄表数据录入”功能,在采用本方案优化后,在50个并发用户的测试环境下,达到了不低于10条数据/(用户·分钟)的录入速度。而且WEBSERVER与SQLSERVER的CPU占用率始终保持在10%左右,表现相当稳定。
