
2.7.3 应用程序资源失衡
现代应用程序平台使用资源池(resource pooling)和数据库连接池(.NET、Java应用程序服务器等)来管理多个事务的负载。因此,软件厂商通常会推荐使用“迟开早关”的方法,从而尽可能使与应用程序其他层数据库的连接保持打开状态的时间最短。开发人员不及时关闭连接或者忘记关闭连接都违背了连接池的初衷,这占用了可用的连接,引起应用程序性能降低直到无法使用。一种强制执行迟开早关规则的方式是,强制所有开发人员在同样的方法中编写打开和关闭连接的代码,以防止连接蠕变。这个非功能性问题很难测试,最好的检测方式是分析应用程序中连接管理方式的结构。
当连接池中的数据库资源和来自应用程序的请求线程数配合不当时,资源连接会阻塞线程直到某个资源可用,等待的线程会占用CPU资源,并且应用程序响应时间会变慢(图2-7)。如果数据库开始抛出异常,应用程序代码必须被设计为能恰当地处理这些异常,否则会有锁死应用程序的风险。资源失衡很难在功能测试中检测到,如果环境不足以模拟重载条件,那也可能逃过压力测试。资源失衡应当通过评估应用程序的架构中请求线程和连接池规模之间潜在的负载失衡来检测。
当运行应用程序的服务器间使用点到点通信方案时,应用程序各层或组件间的通信可能超过负荷,因为每个服务器必须和每一个运行该应用程序的其他服务器都维护一个通信链路。当只有两个服务器通信时,负载很低。但是,如果多个服务器安装了应用程序,打开的通信链路数会呈指数增长,减慢甚至可能锁死应用程序。这种情况下,多播或者发布订阅模型可能更合适。这样的应用程序通信问题很难测试,最好的识别方式是评估应用程序的架构以及应用程序是如何管理服务器间通信的。
在某个环境下一切正常的应用程序在其他环境里可能会有性能或稳定性问题,由于其架构或代码没有专门针对这些环境进行设计。例如,考虑局域网环境下应用程序中的一个用户接口,它在填充屏幕的每个区域都单独访问数据库。因为局域网很快,所以众多的数据库访问几乎没有性能损失。然而,如果应用程序被扩展到跨越多个不同的地理区域,那么其响应会很差,因为这么多的数据库访问需要在局域网之外缓慢的通信线路上“爬行”。没有分布式应用程序经验的开发人员经常无法理解其组件与应用程序中其他层之间交互方式的性能含义,从而损害了其可扩展性和性能。
数据层和用户接口层或逻辑层之间的交互在最初的负载下运行良好,但随着数据量或者事务频率的快速增长,它可能会变得极其缓慢。例如,开发人员在循环中实例化一个对象去访问数据层的表,如果这个表增长的非常大,那么循环中这些重复的调用会降低性能。在SQL编程中,随着数据或处理频率的增加,嵌入在WHERE从句中的函数或没有索引的查询能引起重大的性能损失。这些由违反良好最佳实践造成的性能损失往往会增加一个量级甚至更多的处理时间。