软件项目实训及课程设计指导——如何优化Web应用数据访问实现方式以提高软件应用系统的响应性能

在软件应用系统中离不开数据访问和数据处理两个方面的功能,而数据处理之前首先要进行数据访问,也就是只有快速地获得了数据,才能进行下一步的数据处理。因此,如果数据访问比较耗时,尽管有高效的数据处理,但软件应用系统的最终性能也有可能仍然是低下的。

怎么进行数据访问?怎么能够高效地获得数据?怎么能够以最经济的技术手段高效地获得数据?作者在下文中为读者介绍如何优化软件应用系统中两种最常见的数据访问方式——文件读写和数据库表数据存取。

1、在软件应用系统中应用轻量级的XML文件IO访问技术

在J2EE Web应用系统的开发实现中,设计和开发实现人员可以将应用系统中不需要频繁修改的数据(如软件系统的配置数据、运行环境有关的参数、汇总统计查询中用户查询频繁但数据变动并不大等应用状况下的数据)保存到XML格式的配置文件,而不要保存到物理数据库系统的数据库表中。如下示例图显示Struts2应用框架中的struts.xml文件中的配置定义数据。




程序频繁读取request的body会导致cpu过高么_数据库连接池


因为对数据库表中的数据频繁地访问会加重软件应用系统的负担——访问物理数据库系统的数据库表中的数据是重量级的IO(Input/Output,输入输出)操作,而且物理数据库系统一般都在远程数据库服务器主机中,频繁地进行远程数据访问的性能更加低下;而访问XML格式文件中的数据是轻量级的IO访问操作,对软件应用系统的资源消耗相对比较小、不太可能会出现性能问题。

因此,可将软件应用系统中的比如业务查询结果生成XML格式文件,并保存在Web服务器主机上,使客户端能够直接和XML格式文件进行交互,以节省访问物理数据库的系统性能开销。这样的性能优化设计方案是最经济、也是最有效的性能优化方法。

在示例项目银行账户信息管理系统中,作者将Web应用系统中的公告信息存放在XML格式的配置文件中——请见下图所示及如下的示例代码。

<?xml version="1.0" encoding="gb2312" ?>              系统公告:系统升级给大家带来的不便还请原谅,本系统正在招聘前台工作人员


程序频繁读取request的body会导致cpu过高么_android 频繁访问数据库_02


因为这些"公告"和"通知"等类型的信息,本身并不会频繁地被修改,在一段时间内基本上是处于稳定不变化的状况。但对它的查询访问却是频繁的、高频次的——每个用户都有可能会查询和访问以了解具体的内容。

然后在程序中再利用SAX(Simple API for XML)的XML解析技术编程读取该XML配置文件中的数据、并在页面显示输出。如下为对应的SAX解析的程序代码示例,并请注意其中黑体标识的语句。

package com.bluedream.webbank.util;import org.xml.sax.helpers.DefaultHandler;import com.bluedream.webbank.exception.WebBankException;import java.io.*;import org.xml.sax.Attributes;import org.xml.sax.SAXException;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;public class SAXInformationConfig extends DefaultHandler implements XMLInformationConfig{      private String marqueeText = "";      public String getMarqueeText(){      return marqueeText;      }      public SAXInformationConfig(){      }      /**      * 读取配置文件信息,并设置相关参数。      * @param configFileName String 配置文件路径及文件名。      */      public void xmlInit(String configFilePathAndName) throws WebBankException {            SAXParserFactory saxParserFactory = null;            SAXParser saxParser = null;            saxParserFactory = SAXParserFactory.newInstance(); //获取SAX工厂对象            saxParserFactory.setNamespaceAware(false);            saxParserFactory.setValidating(false);            try{                  saxParser = saxParserFactory.newSAXParser(); //创建出SAX解析                  /** 将解析器和解析对象XMLDOMData.xml联系起来,同时指定事件回调方法的对象开始解析*/                  saxParser.parse(new File(configFilePathAndName), this);            }            catch (javax.xml.parsers.ParserConfigurationException pe){            throw new WebBankException("在SAXInformationConfig类中的xmlInit方法中出现ParserConfigurationException");            }            catch (SAXException se) {              throw new WebBankException("在SAXInformationConfig类中的xmlInit方法中出现SAXException");            }            catch (java.io.IOException ioe) {            throw new WebBankException("在SAXInformationConfig类中的xmlInit方法中出现IOException");            }            catch (Exception ex){            throw new WebBankException("在SAXInformationConfig类中的xmlInit方法中出现Exception");            }            finally{                  saxParserFactory = null;                  saxParser = null;            }      }      private String tagElementName = null;      /** 定义开始解析元素的方法. 这里是将中的名称xxx提取出来。*/      public void startElement(String uri, String localName, String qName, Attributes attributes)      throws SAXException {      this.tagElementName = qName; //获得该标签的名称      }      /** 这里是将之间的标签体的值加入到currentValue */      public void characters(char[] ch, int start, int length) throws SAXException{            String tagBodyText = new String(ch, start, length); //获得标签体的文字串内容            if (this.tagElementName.equals("marquee-text") && !tagBodyText.trim().equals("")) {            marqueeText = tagBodyText;            }      }}

考虑到本文的篇幅关系,作者在此文中不能详细地介绍SAX的XML解析技术编程。感兴趣的读者可以参考作者的《J2EE Web核心技术——XHTML与XML应用开发》的教材。


程序频繁读取request的body会导致cpu过高么_数据库连接_03


2、在软件应用系统中应用数据库连接池技术

(1)JDBC2.0中的DataSource接口

javax.sql包中的DataSource接口可以采用三种不同的方式实现——简单的实现(只提供Connection对象的创建)、数据库连接池的实现和分布式事务支持的数据库连接实现。使用DataSource 接口获得数据库连接Connection对象,不仅提高了项目的数据源的可移植性,也还能够提高数据访问的性能。

如下示例图为JDK API技术帮助文档中对javax.sql包中的DataSource 接口的定义及功能介绍的局部截图。


程序频繁读取request的body会导致cpu过高么_数据库连接_04


(2)建立数据库连接和释放数据库连接对象都是需要消耗系统资源的

软件应用系统程序建立与物理数据库系统之间的TCP连接时,数据库管理系统需要分配多种系统资源以完成连接的创建过程;而释放数据库连接时,数据库管理系统也需要释放掉这些被占用的系统资源,"分配"和"释放"系统资源其实都是比较耗时的工作。

因此,如果软件应用系统中存在反复建立数据库连接和释放与数据库连接的应用状况,势必会影响整个软件应用系统的总体性能。而基于javax.sql包中的DataSource 接口的数据库连接池技术能够重用已经存在的数据库连接对象,而不是每次请求都重新再建立新的数据库连接对象。如下示例图显示JDBC2.0版本中的javax.sql.DataSource接口的常见的实现形式。


程序频繁读取request的body会导致cpu过高么_数据库连接_05


(3)利用数据库连接池技术以提高软件应用系统的数据访问性能

当在软件应用系统的持久层数据访问组件中应用数据库连接池技术实现方案时,Web服务器在启动时首先会创建出一定数量的数据库连接对象并缓存在内存中(也就是连接池中)。如果应用系统的持久层中的数据访问组件(DAO)需要建立数据库连接时,只须从内存中获取一个由数据库连接池管理对象预先创建好的数据库连接对象,而不用重新创建新的数据库连接对象。

当然,在DAO程序中的数据访问操作完毕后,只需放回到数据库连接池的内存中——数据库连接的建立、关闭都由连接池管理程序来完成。

通过应用数据库连接池技术能够有效地减少建立和释放数据库连接对象时的系统消耗,并允许软件应用程序重复地使用一个现有的数据库连接对象;数据库连接池管理器程序能够释放空闲时间超过最大空闲时间的数据库连接对象来避免因为没有释放数据库连接而引起的数据库连接遗漏——这能明显地提高对数据库操作的性能和系统程序的稳定性。因此,该设计方案也是最经济和最有效的性能调优方法。

作者在下文中以Apache DBCP数据库连接池组件为示例,为读者介绍如何在Web项目中应用数据库连接池技术以提高软件应用系统中的数据库访问和数据库连接的性能。DBCP(DataBase Connection Pool)数据库连接池是Apache基金会上的一个Java连接池项目,它也是目前在J2EE系统平台中广泛应用的开源数据库连接池组件。

如下示图为Apache DBCP数据库连接池组件官方网站的页面局部截图,读者可以在其官方网站中下载DBCP的系统库文件,浏览技术参考文档和API系统库帮助文档等资料。


程序频繁读取request的body会导致cpu过高么_数据库连接池_06


3、在软件应用系统中如何应用Apache DBCP数据库连接池组件

(1)在Web项目中添加DBCP连接池相关的系统库的*.jar包文件

DBCP组件所依赖的JAR程序包主要为:commons-dbcp-1.2.2.jar、commons-pool-1.2.jar和commons-collections.jar 三个文件。最终的配置结果请参考如下示例图所示的配置结果示例图。


程序频繁读取request的body会导致cpu过高么_android 频繁访问数据库_07


(2)为Web项目中实现数据库连接的ConnectDBInterface接口提供一个新的实现类

该实现类名称为DBCPConnectDBBean,程序包名称为com.px1987.webbbs.dao,该程序类主要实现初始化DBCP数据库连接池,然后根据需要从数据库连接池中获得数据库连接对象实例,并返送给数据访问类(DAO)中相关的数据访问方法。该程序类的创建过程请参看如下示例图所示。


程序频繁读取request的body会导致cpu过高么_数据库连接_08


(3)编程实现类DBCPConnectDBBean中的程序代码

如下程序代码示例为DBCPConnectDBBean程序类的最终程序代码,请注意其中黑体所标识的语句,这些程序代码实现数据库连接池的初始化,并获得数据库连接池中缓存的数据库连接对象。

package com.px1987.webbbs.dao;import org.apache.commons.dbcp.BasicDataSource;import java.sql.*;import com.px1987.webbbs.exception.WebBBSException;import com.px1987.webbbs.config.*;import java.util.logging.*;public class DBCPConnectDBBean implements ConnectDBInterface {      String JDBC_DBDriver= null;      String JDBC_URL = null;      String dbUserName=null;      String dbUserPassWord=null;      String dbcp_maxActive=null;      private java.sql.Connection con = null;      BasicDataSource oneDataSourceImple=null;      private Logger logger = Logger.getLogger(this.getClass().getName());      public DBCPConnectDBBean() throws WebBBSException{            JDBC_DBDriver = ClassNameConfig.getProperty("JDBC_DBDriver");            JDBC_URL = ClassNameConfig.getProperty("JDBC_URL");            dbUserName = ClassNameConfig.getProperty("dbUserName");            dbUserPassWord = ClassNameConfig.getProperty("dbUserPassWord");            dbcp_maxActive = ClassNameConfig.getProperty("dbcp_maxActive");            oneDataSourceImple=new BasicDataSource();            oneDataSourceImple.setDriverClassName(JDBC_DBDriver);            oneDataSourceImple.setUrl(JDBC_URL);            oneDataSourceImple.setUsername(dbUserName);            oneDataSourceImple.setPassword(dbUserPassWord);            //最大的连接数目            oneDataSourceImple.setMaxActive(Integer.parseInt(dbcp_maxActive));            oneDataSourceImple.setDefaultAutoCommit(true);            try {            con=oneDataSourceImple.getConnection();            }            catch (java.sql.SQLException e) {                logger.log(Level.INFO, e.getMessage());                throw new WebBBSException("不能正确地连接数据库并且出现SQLException");            }      }      public void closeDBCon() throws WebBBSException {            try{                  con.close();                  con = null;            }            catch (SQLException e){                  logger.log(Level.INFO, e.getMessage());                  throw new WebBBSException("不能正确地关闭数据库连接");            }      }      public Connection getConnection() throws WebBBSException {      return con;      }}

4、测试应用Apache DBCP数据库连接池组件的功能正确性

(1)修改Web项目中的classNameConfig.properties属性配置文件中的下面的属性项目"connectDBBean.className"的类名称为DBCPConnectDBBean(该程序类获得数据库连接池中的数据库连接对象实例)

connectDBBean.className=com.px1987.webbbs.dao.DBCPConnectDBBean

为了能够在项目中动态加载相关的程序类从而实现利用Java发射技术动态创建类的对象实例,需要修改项目中程序类名称的属性配置文件中的数据库连接类名称。修改后的结果可以参看如下示例图所示。


程序频繁读取request的body会导致cpu过高么_android 频繁访问数据库_09


(2)再次执行在JUnit单元测试项目中的TestConnectDBBean测试用例类

在MyEclipse开发工具中启动并执行TestConnectDBBean测试用例,该测试用例的执行结果参看如下示例图所示。从测试用例的执行结果可以获知本Web项目的数据库连接是正确的,也就是正确地获得了从数据库连接池中缓存的数据库连接对象实例。


程序频繁读取request的body会导致cpu过高么_数据库连接池_10