zoukankan      html  css  js  c++  java
  • Java 学习使用常见的开源连接池

    目录


    连接池介绍

    自定义连接池

    JDBC Tomcat Pool

    DBCP(DataBase Connection Pool)

    使用配置文件来设置DBCP

    C3P0

    Druid

     

     

     


    连接池介绍

      在说连接池之前,我们先想一个问题:程序要进行数据库操作,与数据库建立的是什么连接?开销怎么样?数据库是否可以同时支持上百万个连接?

      首先第一个问题:程序与数据库建立的是socket连接,走的是传输层,使用TCP。

      第二个问题:总开销 约等于 程序运行耗时 + 网络io + 数据库运行耗时。

      第三个问题:应该是不能同时支持上百万个连接,几千个应该就是上限了。

      看了上面三个问题,再提出一个问题:上面的问题中,我们可以优化哪一个环节?

      运维可以优化网络传输的问题;DBA可以优化数据库的运行性能。

      对于开发人员来说,我们可以优化第一个问题;首先,创建socket连接真的很耗时,主要的原因是因为建立TCP连接时有个3次握手,建立连接之后传输数据开销其实并不大。所以我们可以在这个角度上进行优化:尽量让程序与数据库建立几个连接,不要让程序频繁与数据库建立连接。

      为了业务的正常执行,线程需要与数据库进行交互,只建立几个数据库连接,好像不现实。

      我们可以换个方式来优化:创建固定数量的数据库的连接,这些数据库连接在Java中就是一个个对象而已,我们可以将这些对象存到容器中(比如List中),这个容器就叫做连接池。

      当有一个线程需要与数据库交互的时候,如果容器中还有数据库连接对象,那就从容器中取出一个连接对象,使用完之后,并不关闭数据库连接,而是将数据库连接对象放回容器,方便其他线程使用;如果线程需要与数据库交互时,容器中没有数据库连接对象了,那么这个线程他就阻塞一下,等待别的线程使用完连接之后归还,然后再使用别的线程归还的数据库连接。

       

    自定义连接池

      自定义连接池的有个规范,需要实现DataSource接口,并且重写多个方法,但是主要重写一个无参的getConnection方法:

    package cn.ganlixin.utils;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintWriter;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.Collections;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Properties;
    import java.util.logging.Logger;
    
    import javax.sql.DataSource;
    
    /**
     * 自定义的超简单的连接池,主要重写了DataSource接口中的getConnection方法,然后创建了归还连接的方法
     */
    public class MyConnectionPool implements DataSource {
    	
    	// 创建一个同步的容器来保存数据库连接
    	private static List<Connection> connectionList = 
    			Collections.synchronizedList(new LinkedList<Connection>());
    	
    	// 设置建立数据库连接的最大数
    	private static final int MAX_CONNECTIONS = 50;
    	
    	// 编写静态初始化块,读取数据库配置文件,建立数据库连接,放入容器
    	static {
    		InputStream _is = MyConnectionPool.class.getClassLoader().getResourceAsStream("database.properties");
    		try {
    			Properties props = new Properties();
    			props.load(_is);
    			String driver = props.getProperty("jdbc.driver");
    			String url = props.getProperty("jdbc.url");
    			String username = props.getProperty("jdbc.username");
    			String password = props.getProperty("jdbc.password");
    			
    			Class.forName(driver);
    			
    			// 创建多个连接,放入容器中
    			for (int i = 0; i < MAX_CONNECTIONS; i++) {
    				Connection conn = DriverManager.getConnection(url, username, password);
    				connectionList.add(conn);
    				System.out.println("创建第 " + (i+1) + " 个连接");
    			}
    			
    		} catch (IOException e) {
    			e.printStackTrace();
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    	}
    	
    	// 从容器中取出一个数据库连接对象
    	@Override
    	public Connection getConnection() throws SQLException {
    		
    		if (connectionList.size() > 0) {  
    			// 如果容器中还有数据库连接对象,就将取出第一个对象,并将该对象从容器中删除
    			Connection conn = connectionList.remove(0);
    			System.out.println("使用了一个数据库连接, 容器中还剩下 " + connectionList.size() + " 个数据库连接");
    			return conn;
    		}
    		
    		return null;
    	}
    	
    	/**
    	 * 归还数据库连接给容器,便于其他线程使用
    	 * @param conn 要归还的数据库连接对象
    	 */
    	public void releaseConnection(Connection conn) {
    		connectionList.add(conn);
    	}
    
    	@Override
    	public PrintWriter getLogWriter() throws SQLException { return null; }
    
    	@Override
    	public void setLogWriter(PrintWriter out) throws SQLException {	}
    
    	@Override
    	public void setLoginTimeout(int seconds) throws SQLException {	}
    
    	@Override
    	public int getLoginTimeout() throws SQLException { return 0; }
    
    	@Override
    	public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; }
    
    	@Override
    	public <T> T unwrap(Class<T> iface) throws SQLException { return null; }
    
    	@Override
    	public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; }
    
    	@Override
    	public Connection getConnection(String username, String password) throws SQLException { return null; }
    
    }
    

      

      测试

    package cn.ganlixin.test;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    public class TestPool {
    	public static void main(String[] args) throws SQLException {
    		MyConnectionPool connectionPool = new MyConnectionPool(); // 获取连接池
    		
    		for (int i = 0; i < 20; i++) {
    			// 获取连接
    			Connection connection = connectionPool.getConnection();
    			
    			if (i % 3 == 0) { // 归还连接
    				connectionPool.releaseConnection(connection);
    			}
    			
    			System.out.println(connection);
    		}
    	}
    }
    

      

    JDBC Tomcat Pool

      tomcat服务器可以提供数据库连接池。我们可以将数据库连接池的配置保存在一个文件名为context.xml的文件中。

      context.xml可以放在项目下的webRoot/META-INF目录下,只对于本项目有效。

      context.xml可以放在tomcat服务器安装路径的conf目录下,针对所有项目都有效。

    <?xml version="1.0" encoding="UTF-8"?>
    <Context>
        <WatchedResource>WEB-INF/web.xml</WatchedResource>
        <Resource
        	driverClassName="com.mysql.jdbc.Driver"
        	url="jdbc:mysql://localhost:3306/test"
        	user="root"
        	password="123456"
        	name="tomcat_supported_pool"
        	auth="Container"
        	maxActive="50"
        	maxIdle="20"
        	maxWait="10000"
        	type="javax.sql.DataSource"	
        ></Resource>
    </Context>
    

      测试:

    package lixin.gan.test;
    
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import javax.naming.Context;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.sql.DataSource;
    
    /**
     * Servlet implementation class TestPool
     */
    @WebServlet("/TestPool")
    public class TestPool extends HttpServlet {
    	@Override
    	public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
    		request.setCharacterEncoding("utf-8");
    		response.setContentType("text/html; charset=utf-8");
    		
    		try {
    			Context ctx = new InitialContext();
    			DataSource ds = (DataSource) ctx.lookup("java:comp/env/tomcat_supported_pool");
    			
    			Connection conn = ds.getConnection();
    			
    			/*
    			 
    			 进行数据库操作即可
    			 
    			 */
    			
    		} catch (NamingException e) {
    			e.printStackTrace();
    		} catch (SQLException e) {
    			e.printStackTrace();
    		}
    		
    	}
    }
    

        

    DBCP(DataBase Connection Pool)

      需要下载commons-dbcp.jar、commons-pool.jar、commons-logging.jar。另外仍旧需要导入mysql的驱动包。

      下载网址(下载xxx-bin.zip即可):

        http://commons.apache.org/proper/commons-dbcp/download_dbcp.cgi

        http://commons.apache.org/proper/commons-pool/download_pool.cgi

        http://commons.apache.org/proper/commons-logging/download_logging.cgi

        下载之后解压,将jar包添加到build path中。

      使用DBCP的示例:

    package cn.ganlixin.test;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    import org.apache.commons.dbcp2.BasicDataSource;
    
    public class TestDBCP {
    	public static void main(String[] args) throws SQLException {
    		
    		// 获取连接池
            BasicDataSource dataSource = new BasicDataSource();
            
            // 设置连接池相关信息
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/test");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
            dataSource.setInitialSize(5);
            dataSource.setMinIdle(2);
    
            // 获取数据库连接
            Connection conn = dataSource.getConnection();
            
            // 执行数据库操作
            Statement stmt  = conn.createStatement();
            stmt.executeUpdate("update stu set age = 30 where id < 4");
            
            // 这个close()方法被重写了,并不是关闭数据库连接,而是将连接归还连接池
            conn.close();
            
    	}
    }
    

      

    使用配置文件来设置DBCP

      前面使用DBCP时,是在程序中手动指定连接信息,同样的,我们可以使用配置文件保存DBCP的连接信息。

      操作:在src下创建一个dbcp.properties文件,内容如下:

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/test
    username=root
    password=root
    
    initialSize=20
    maxIdle=10
    minIdle=5
    maxWait=10000
    

      

      使用DBCP

    package cn.ganlixin.test;
    
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.Statement;
    import java.util.Properties;
    
    import org.apache.commons.dbcp2.BasicDataSource;
    import org.apache.commons.dbcp2.BasicDataSourceFactory;
    
    public class TestPool {
    	public static void main(String[] args) throws Exception {
    
    		BasicDataSourceFactory dataSourceFacoty = new BasicDataSourceFactory();
    
    		InputStream _is = TestPool.class.getClassLoader().getResourceAsStream("dbcp.properties");
    		Properties props = new Properties();
    		props.load(_is);
    
    		// 获取连接池
    		BasicDataSource dataSource = dataSourceFacoty.createDataSource(props);
    		Connection conn = dataSource.getConnection();
    		Statement stmt = conn.createStatement();
    		stmt.executeUpdate("update stu set age = 30 where id < 4");
    
    		conn.close();
    
    	}
    }
    

      

    DBCP返回的数据库连接是包装类对象

      当我们从DBCP连接池获取的连接其实是一个经过包装之后的数据库连接对象,而不是原生的jdbc数据库连接对象:

    package cn.ganlixin.test;
    
    import java.io.InputStream;
    import java.sql.Connection;
    import java.util.Properties;
    
    import org.apache.commons.dbcp2.BasicDataSource;
    import org.apache.commons.dbcp2.BasicDataSourceFactory;
    import org.apache.commons.dbcp2.DelegatingConnection;
    
    public class TestDBCP1 {
    	public static void main(String[] args) throws Exception {
    
    		BasicDataSourceFactory dataSourceFacoty = new BasicDataSourceFactory();
    
    		InputStream _is = TestPool.class.getClassLoader().getResourceAsStream("dbcp.properties");
    
    		Properties props = new Properties();
    		props.load(_is);
    
    		BasicDataSource dataSource = dataSourceFacoty.createDataSource(props);
    
    		// 获取数据库连接
    		Connection conn = dataSource.getConnection();
    		
    		System.out.println(conn.getClass().getName());
    		// org.apache.commons.dbcp2.PoolingDataSource$PoolGuardConnectionWrapper
    		// 通过DBCP连接池获得的是一个包装过后的连接池对象
    		
    		// 可以通过下面的步骤获取原生的Connection对象
    		DelegatingConnection dc = (DelegatingConnection) conn;
    		Connection connection = dc.getInnermostDelegateInternal();
    		System.out.println(connection.getClass().getName());
    		// com.mysql.jdbc.JDBC4Connection
    
    	}
    }
    

      

    C3P0

      C3P0也是一个数据库连接池的开源项目,他和DBCP功能类似,但是有一些区别:

      1、DBCP不会自动回收空闲连接的功能,而C3P0有这个功能。

      2、DBCP需要手动指定配置文件的路径以及文件明,而C3P0不需要(C3P0的配置文件指定路径和名称)。

      

      需要下载c3p0.jar,mchange-commons-java.jar,以及mysql的驱动包。

      http://central.maven.org/maven2/com/mchange/c3p0/0.9.5.4/c3p0-0.9.5.4.jar

      http://central.maven.org/maven2/com/mchange/mchange-commons-java/0.2.15/mchange-commons-java-0.2.15.jar

    创建C3P0的配置文件

      C3P0的配置文件名为c3p0-config.xml,放在src目录下即可,配置文件的内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <c3p0-config>
    	<default-config>
    		<property name="driverClass">com.mysql.jdbc.Driver</property>
    		<property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
    		<property name="user">root</property>
    		<property name="password">root</property>
    		<property name="initialPoolSize">30</property>
    		<property name="maxIdleTime">60</property>
    		<property name="maxPoolSize">60</property>
    		<property name="minPoolSize">15</property>
    	</default-config>
    	
    	<!-- 可以指定两个数据库配置,一个线上环境,一个开发环境 -->
    	<named-config name="dev">
    		<property name=""></property>
    		<property name=""></property>
    		<property name=""></property>
    		<property name=""></property>
    		<property name=""></property>
    		<property name=""></property>
    		<property name=""></property>
    		<property name=""></property>
    		<property name=""></property>
    	</named-config>
    </c3p0-config>
    

       

    测试C3P0

    package cn.ganlixin.test;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    public class TestC3P0 {
    	public static void main(String[] args) throws SQLException {
    		
    		// 获取连接池
    		// 加载默认的数据库连接配置
    		// ComboPooledDataSource dataSource = new ComboPooledDataSource();
    		// 使用c3p0配置文件中named-config里面name为dev的数据库配置
    		ComboPooledDataSource dataSource = new ComboPooledDataSource("dev");
    		
    		// 获取数据库连接
    		Connection connection = dataSource.getConnection();
    		
    		// 获取到数据库连接之后就可以进行各种操作了
    		// code
    		
    		// c3p0返回的数据库连接同样是包装类
    		System.out.println(connection);
    		// com.mchange.v2.c3p0.impl.NewProxyConnection@769c9116 
    		// [wrapping: com.mysql.jdbc.JDBC4Connection@6aceb1a5]
    
    		
    		// 将数据库连接归还连接池
    		connection.close();
    	}
    }
    

      

      

    Druid

      Druid是alibaba开源的一个连接池项目;

      使用Druid,需要下载druid.jar以及mysql的驱动包;

      druid.jar的下载地址:http://central.maven.org/maven2/com/alibaba/druid/1.1.15/druid-1.1.15.jar

      github地址:https://github.com/alibaba/druid

     使用方法设置连接池信息

    package cn.ganlixin.test;
    
    import java.sql.SQLException;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.pool.DruidPooledConnection;
    
    public class TestDruid {
    	public static void main(String[] args) throws SQLException {
    		// 获取连接池对象
    		DruidDataSource dataSource = new DruidDataSource();
    		
    		// 设置连接信息
    		dataSource.setDriverClassName("com.mysql.jdbc.Driver");
    		dataSource.setUrl("jdbc:mysql://localhost:3306/test");
    		dataSource.setUsername("root");
    		dataSource.setPassword("root");
    		
    		// 设置连接池的配置
    		dataSource.setInitialSize(20);
    		dataSource.setMaxActive(30);
    		dataSource.setMaxWait(1000);
    		dataSource.setMinIdle(10);
    		
    		// 获取连接(得到的不是原生的jdbc for mysql的连接对象)
    		DruidPooledConnection connection = dataSource.getConnection();
    		
    		// 释放连接到连接池中
    		connection.close();
    	}
    }
    

      

      使用properties配置文件的方式配置Druid

      配置Druid可以参考官方的示例:https://github.com/alibaba/druid/wiki/DruidDataSource%E9%85%8D%E7%BD%AE

       在src下创建druid-config.properties(文件名随意),内容如下:

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/test
    username=root
    password=root
    
    maxActive=10
    initialSize=5
    maxWait=10000
    minIdle=5
    

      配置项和DBCP几乎一样。

      进行测试:

    package cn.ganlixin.test;
    
    import java.io.InputStream;
    import java.sql.Connection;
    import java.util.Properties;
    
    import javax.sql.DataSource;
    
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    public class TestDruid2 {
    	public static void main(String[] args) throws Exception {
    		
    		InputStream _is = TestDruid2.class.getClassLoader().getResourceAsStream("druid-config.properties");
    		
    		Properties props = new Properties();
    		props.load(_is);
    		
    		/**
    		读取配置文件,手动调用setter进行设置连接池
    		DruidDataSource dataSource = new DruidDataSource();
    		dataSource.setDriverClassName(props.getProperty("driverClassName"));
    		.........
    		*/
    		
    		// 利用工厂加载配置文件来配置连接池
    		DruidDataSourceFactory factory = new DruidDataSourceFactory();
    		DataSource dataSource = factory.createDataSource(props);
    
    		// 获取数据库连接
    		Connection connection = dataSource.getConnection();
    		System.out.println(connection.getClass().getName());
    		
    		connection.close();
    	}
    }
    

      

  • 相关阅读:
    LINUX常见服务列表
    xinetd服务管理
    System V启动脚本启动的服务
    linux系统服务
    proc文件系统
    sar网络统计数据
    sar磁盘I/O统计数据
    sar-CPU统计数据
    linux性能监视工具sar
    考试认证
  • 原文地址:https://www.cnblogs.com/-beyond/p/9903867.html
Copyright © 2011-2022 走看看