分享

Spring之防止连接泄露的处理办法

 WindySky 2017-08-01

连接泄漏
在开发过程中直接连接数据库获取连接,使用完之后不进行及时的关闭连接,连接会一直处于激活状态,就会造成连接泄露,对系统和数据库都会带来一定的压力和负担。
我们写了一个方法让主线程睡眠的形式来模拟多线程环境下的模拟

package com.zhu.service;

import java.sql.Connection;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Service;
import com.alibaba.druid.pool.DruidDataSource;

@Service
public class TestJdbcService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public static class UserServiceRunner extends Thread{
        private TestJdbcService testJdbcService;
        private String name;
        public UserServiceRunner(TestJdbcService testJdbcService,String name) {
            this.testJdbcService = testJdbcService;
            this.name = name;
        }
        @Override
        public void run() {
            testJdbcService.logon(name);
        }
        public long getThreadID() {
            return Thread.currentThread().getId();
        }
    }
    public void logon(String name){
        try {
            Connection connect = jdbcTemplate.getDataSource().getConnection();
            String sql = "update zhu_test set last_time = ? where name = ?";
            jdbcTemplate.update(sql, System.currentTimeMillis(),name);  
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void sleep(long time){
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void reportConn(DruidDataSource dataSource){
        System.out.println("连接数[active]-[" +
                dataSource.getActiveCount()+"]");
    }

    public static void asynclogin(TestJdbcService testJdbcService,String name){
        UserServiceRunner runner = new UserServiceRunner(testJdbcService,name);
        System.err.println(Thread.currentThread().getName()+"ID:"+runner.getThreadID());
        runner.start();
    }
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("file:src/main/resources/applicationContext.xml");
        TestJdbcService testJdbcService = (TestJdbcService) ctx.getBean("testJdbcService");
        DruidDataSource druidDataSource = (DruidDataSource) ctx.getBean("dataSource");

        testJdbcService.reportConn(druidDataSource);
        testJdbcService.asynclogin(testJdbcService,"tom");
        testJdbcService.sleep(500);
        testJdbcService.reportConn(druidDataSource);
        testJdbcService.sleep(2000);     
        testJdbcService.reportConn(druidDataSource);
        testJdbcService.asynclogin(testJdbcService,"jack");
        testJdbcService.sleep(500);
        testJdbcService.reportConn(druidDataSource);
        testJdbcService.sleep(2000);
        testJdbcService.reportConn(druidDataSource);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82

运行结果:
这里写图片描述
由于我使用的是阿里的数据源,没有找到获取空闲连接的方法
分析如下(引自网络)
这里写图片描述

解决办法:
①使用DataSourceUtils
引自网络
将上述获取连接的方式改为DataSourceUtils的形式来获取连接。

public void logon(String name){
        try {
            //Connection connect = jdbcTemplate.getDataSource().getConnection();
            Connection connect  = DataSourceUtils.getConnection(jdbcTemplate.getDataSource());
            String sql = "update zhu_test set last_time = ? where name = ?";
            jdbcTemplate.update(sql, System.currentTimeMillis(),name);  
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

然后查看运行结果:
这里写图片描述
并没有数据连接泄露。
但是这样仅仅是在配置事务的时候不会出现连接泄露,如果将事务注释之后,还是会出现一定的连接泄漏。
将配置文件中的事务注释之后,执行结果
这里写图片描述
分析如下(引自网络)
这里写图片描述
杜绝上述情况连接泄露的方式就是手动关闭连接。
②使用TransactionAwareDataSourceProxy
如果不得已要显式获取数据连接,除了使用 DataSourceUtils 获取事务上下文绑定的连接外,还可以通过 TransactionAwareDataSourceProxy 对数据源进行代理。数据源对象被代理后就具有了事务上下文感知的能力,通过代理数据源的 getConnection() 方法获取的连接和使用 DataSourceUtils.getConnection() 获取连接的效果是一样的。
将配置文件中的配置修改一下

<!-- 数据源默认将autoCommit设置为true -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver"
        p:url="jdbc:mysql://172.16.14.40:3306/zhu" p:username="zhu"
        p:password="zhu" />
<!-- ①对数据源进行代理-->
<bean id="dataSourceProxy" 
    class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy"
    p:targetDataSource-ref="dataSource"/>

<!-- ②直接使用数据源的代理对象-->
<bean id="jdbcTemplate"
    class="org.springframework.jdbc.core.JdbcTemplate"
    p:dataSource-ref="dataSourceProxy"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

打开事务,运行结果
这里写图片描述
没有连接泄露!

在了解数据连接泄露原理之后,触类旁通,我们可以推及其他的框架。
引自网络

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约