JNDI与JDBC比较以及连接池的技术

       Java Database Connectivity(JDBC)JDBC以一种统一的方式来对各种各样的数据库进行存取。和ODBC一样,JDBC为开发人员隐藏了不同数据库的不同特性。另外,由于JDBC建立在Java的基础上,因此还提供了数据库存取的平台独立性。JDBC定义了4种不同的驱动程序:JDBC-ODBCBridgeJDBC-native driverbridgeJDBC-networkbridgePureJavadriver。在一个多层的企业级应用中,更大的可能是在客户端和一个EJB进行通信,采用EJB建立数据库连接。为了实现和改进可伸缩性和系统性能, 也可以采用连接缓冲池(connection pool)优化数据库连接。

Java Naming and Directory Interface (JNDI)JNDI API被用于执行名字和目录服务。它提供了一致的模型来存取和操作企业级的资源如DNSLDAP,本地文件系统,后者在应用服务器中的对象。在JNDI中,在目录结构中的每一个结点称为context。每一个JNDI名字都是相对于context的。应用可以通过这个初始化的context经有这个目录树来定位它所需要的资源或对象。

JNDI相对与JDBC来说是他的灵活性,程序员不需要关心“具体的数据库后台是什么?JDBC驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理,程序员只需要对这些配置和管理进行引用即可。

理解连接池

连接池是创建和管理多个连接的一种技术,这些连接可被需要使用它们的任何线程使用。连接池技术基于下述事实:对于大多数应用程序,当它们正在处理通常需要数毫秒完成的事务时,仅需要能够访问JDBC连接的1个线程。未处理事务时,连接处于闲置状态。使用连接池,允许其他线程使用闲置连接来执行有用的任务。

事实上,当某一线程需要用JDBCMySQL或其他数据库上执行操作时,需要用到由连接池提供的连接。使用连接完成线程后,线程会将连接返回给连接池,以便该连接能够被其他需要使用连接的线程使用。

从连接池“借出”连接时,该连接仅供请求它的线程使用。从编程观点看,其效果等同于每次需要JDBC连接时调用DriverManager.getConnection(),但是,采用连接池技术,可通过使用新的或已有的连接结束线程。

连接池技术能显著增加Java应用程序的性能,同时还能降低资源使用率。连接池技术的主要优点包括:

·         缩短了连接创建时间

与其他数据库相比,MySQL提供了快速的连接设置功能,连接时间通常不是问题,但创建新的JDBC连接仍会导致联网操作和一定的IDBC驱动开销,如果这类连接是“循环”使用的,使用该方式,可避免这类不利因素。

·         简化的编程模型

使用连接池技术时,每个单独线程能够像创建了自己的JDBC连接那样进行操作,从而允许使用直接的JDBC编程技术。

·         受控的资源使用

如果不使用连接池技术,而是在每次需要时为线程创建新的连接,那么应用程序的资源使用将十分浪费,而且在负载较重的情况下会导致无法预期的结果。

注意,与MySQL的每个连接均会在客户端和服务器端造成一定的开销(每寸、CPU、关联转换等)。每个连接均会对应用程序和MySQL服务器的可用资源带来一定的限制。无论连接是否执行任何有用的任务,仍将使用这些资源中的相当一部分。

连接池能够使性能最大化,同时还能将资源利用控制在一定的水平之下,如果超过该水平,应用程序将崩溃而不仅仅是变慢。

幸运的是,Sun公司通过JDBC-2.0“可选”接口,完成了JDBC中连接池概念的标准化实施,所有主要应用服务器均实施了能够与MySQL Connector/J一起良好工作的这类API

通常,你可以在应用服务器的配置文件中配置连接池,并通过Java命名和目录接口(JNDI)访问它。在下面的代码中,介绍了在J2E应用服务器上运行的应用程序中使用连接池的方法:

示例26.12. J2EE应用服务器一起使用连接池

import java.sql.Connection;

import java.sql.SQLException;

import java.sql.Statement;

import javax.naming.InitialContext;

import javax.sql.DataSource;

public class MyServletJspOrEjb {

    public void doSomething() throws Exception {

        /*

         * Create a JNDI Initial context to be able to

         * lookup the DataSource

         *

         * In production-level code, this should be cached as

         * an instance or static variable, as it can

         * be quite expensive to create a JNDI context.

         *

         * Note: This code only works when you are using servlets

         * or EJBs in a J2EE application server. If you are

         * using connection pooling in standalone Java code, you

         * will have to create/configure datasources using whatever

         * mechanisms your particular connection pooling library

         * provides.

         */

        InitialContext ctx = new InitialContext();

         /*

          * Lookup the DataSource, which will be backed by a pool

          * that the application server provides. DataSource instances

          * are also a good candidate for caching as an instance

          * variable, as JNDI lookups can be expensive as well.

          */

        DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB");

        /*

         * The following code is what would actually be in your

         * Servlet, JSP or EJB 'service' method...where you need

         * to work with a JDBC connection.

         */

        Connection conn = null;

        Statement stmt = null;

        try {

            conn = ds.getConnection();

            /*

             * Now, use normal JDBC programming to work with

             * MySQL, making sure to close each resource when you're

             * finished with it, which allows the connection pool

             * resources to be recovered as quickly as possible

             */

            stmt = conn.createStatement();

            stmt.execute("SOME SQL QUERY");

            stmt.close();

            stmt = null;

            conn.close();

            conn = null;

        } finally {

            /*

             * close any jdbc instances here that weren't

             * explicitly closed during normal code path, so

             * that we don't 'leak' resources...

             */

            if (stmt != null) {

                try {

                    stmt.close();

                } catch (sqlexception sqlex) {

                    // ignore -- as we can't do anything about it here

                }

                stmt = null;

            }

            if (conn != null) {

                try {

                    conn.close();

                } catch (sqlexception sqlex) {

                    // ignore -- as we can't do anything about it here

                }

                conn = null;

            }

        }

    }

}

如上例所示,获得JNDI InitialContext并查找到数据库后,其余代码与过去在JDBC编程中使用的类似。

使用连接池时需要牢记的最重要事项是,无论在代码中出现了什么(异常、控制流等),连接以及由连接创建的任何部分(语句、结果集等)均应被关闭,以便能再次使用它们。如不然,它们将纠缠在一起,在最好的情况下,意味着它们所代表的MySQL服务器资源(缓冲区、锁定、套接字等)可能会捆绑一段时间,在最坏的情况下,可能会导致永久捆绑。

连接池的最佳大小是什么?

与所有其他配置经验规则一样,回答是“它取决于具体情况”。尽管最佳大小取决与预期的负载和平均的数据库事务时间,最佳的连接池大小小于你的预期。例如,如果使用的是Sun公司的Java Petstore Blueprint应用程序,对于包含1520个连接的连接池,使用MySQLTomcat,在可接受的相应时间下,可服务于中等程度的负载(600个并发用户)。

要想确定用于应用程序的连接池大小,应使用诸如Apache JmeterThe Grinder等工具创建负载测试脚本,并对应用程序进行负载测试。

确定出发点的一种简单方法是,将连接池的最大连接数配置为“无限”,运行负载测试,并测量最大的并发连接数。随后,应进行反向操作,确定出使应用程序具有最佳性能的连接池的最小和最大值。

posted on 2008-05-04 19:15  小顾问  阅读(27684)  评论(0编辑  收藏  举报