无法与域的active directory域控制器连接_理论结合实践才是学习的真谛,根据原理实现一个数据库连接池

数据库连接池

1、数据库连接池的作用在那里?为什么要使用它?

​ 纵所周知:我们在进行数据库连接的时候,都是通过jdbc去连接数据库的,

JDBC有SUN公司编写的一套 访问数据库的规范和标准,并提供了连接数据库的协议标准,具体的实现由各个厂商实现。JDBC是一套规范接口,而驱动就是接口的实现,比如Mysql驱动、Oracle驱动,这些就是厂商的驱动,用来连接自己家的数据库。

​ SUN公司的人 想着写出一套可以连接天下所有数据库的api,但他们发现是个不可能完成的任务,因为每个厂商的数据库服务差异太大。所以才以提供了这个 JDBC这一套访问数据库的规范,也因此,几乎各大厂商,对数据库的连接,都基于JDBC去实现。

ab4d22d78157fed52945d37634c0ad89.png

既然JDBC可以实现数据库的连接,为什么还要去整一个连接池来使用呢?

传统的方式使用JDBC 的操作就是:一个请求过来,Class.foname('驱动');去创建一个连接,

使用完毕后关掉连接。不控制被连接的数量。

1.这样的方式会带来什么问题?

​ 1)在高频访问服务器的时候,过多的连接。比如有100个请求进来,就得建立100次连接。连接-断开、连接-断开,这期间都需要建立TCP连接,三次握手过程。这样的重复循环,每次连接都需要很多时间。会占用很多系统内存,这会影响效率、速度。

​ 2)假如程序出异常,导致连接未能正常关闭,这会一直保持着连接,久而久之会导致数据库连接数量达到上限,无法后续请求无法操作数据库,会导致内存泄漏问题。

​ 3)...

数据库连接池,就是解决这些问题的。

​ 数据库连接池,对JDBC进行数据库的连接,进行了包装管控这些连接,使用这个连接池,需要配置数据库的最小最大连接数量。确保不让资源毫无顾忌的被分配出去。而且这些连接可复用。不管有多少个请求来,最大的连接数量也不会大于配置的最大连接数量。

​ 数据库的连接池的作用就体现在了:资源重用,一次连接多次复用、提高效率、统一管理连接、避免资源泄漏等问题。空间换时间。

2、数据库连接池的实现原理是什么?

​ 连接池是用于管控数据库的连接的,所以连接池可根据配置去创建多少个连接。

比如:配置了初始化连接 3个,最大连接数量 10个,每个空闲连接的时间 1分钟。

在初始化连接池的时候:就会预先创建好3个数据库连接。将这些连接放到一个链表中,当进行操作数据的时候(getConnect()),直接 从链表中取出一个连接,取操作数据库,这个时候。就少去了进行数据库连接的时间。初始化的时候已经初始化了3个连接了。(好比是 兵马未动,粮草先行的道理)。

当这个连接使用完毕后,调用close(); 连接池就又将当前的连接放回到链表中,等待下一个请求来拿。(资源重用,一次连接多次使用)

当来的请求大于3个的时候,比如5个请求:此时链表的中的连接不够数:只有3个怎么办?

3个不够,那就继续创建连接,在创建多两个连接就够这5个请求使用了。

假如15个连接怎么办,那就继续创建连接,直到所有的连接数量达到了10个。那剩余的5个请求,就得进入队列等待一段时间,等其他请求的释放连接,才能操作数据库了。如果在一定时间内还获取不到连接,那这个请求就无法操作数据库了。

等创建的连接数量大于 3个的时候。然后很久都没有请求来的时候,这个时候就会根据配置的连接空闲时间去关闭一些连接了(释放资源)。但会保持着最小的连接。

3、根据原理可以自己实现自己的连接池吗?

​ 理论层面的东西只是支撑,看懂了理论或许能糊弄住面试官。我们不仅要能说会道让面试官折服于你的口才,更要能动手去征服面试官。此动手非比动手。我说的是能实践动手操作能力要与你的能说会到相吻合。做程序员的我觉得代码能力比能说会道的这一点要有含金量。

其实我身边有这种人:根据自己的思想能写出某些设计模式思想的代码出来。但是你问他:卧槽你设计模式运用得好溜啊。他一脸懵逼的说:设计模式,什么是设计模式,我没有学过。

这样的人都是实践经验比较多的人,根据自己的经验和聪明的大脑也能写出好程序。然而这些23种设计模式讲得头头是道的人,让他动手去实现一个,估计也是难。他说我没有应用场景去写。 。不是没有而是动手能力弱。

程序员这一行还是最需要的是动手能力的,即使你经验丰富,将理论付诸于行动的时候,你就会发现很多坑等着你。所以经验也是这么来的。

4、代码实现一个高并发的连接池。

​ 造一个轮子,主要是通过实践来学习所掌握的知识点的应用,加固加深印象,提高自己的动手能力。检测自己的理解程度。

1、先看看代码结构

eb88bb208488a2db9984bfa2f36e46b8.png

1、DataBaseConnectPool 连接池的接口

主要有三个简单的方法

  • void init(final PoolConfig config);
  • JdbcConnect getConnection();
  • void recovery(Connection connection);
/**
 * <br>
 * 数据库连接池接口
 *
 * @author 永健
 * @since 2020-08-30 15:50
 */
public interface DataBaseConnectPool
{
    /**
     * 初始化连接池
     */
    void init(final PoolConfig config);

    /**
     * 从连接池中获取一个连接
     *
     * @return 连接对象
     */
    JdbcConnect getConnection();


    /**
     * 连接池回收
     *
     * @param connection 连接对象
     */
    void recovery(Connection connection);
}

2、ConnectPools 连接池的实现

  • 线程的活跃数量
  • 线程池的连接数量
  • 连接对象的监控
  • 单例线程池
/**
 * <br>
 * 连接池的实现
 *
 * @author 永健
 * @since 2020-08-30 15:49
 */
public class ConnectPools implements DataBaseConnectPool
{
    /**
     * 池子
     */
    private ArrayBlockingQueue<Connection> POOLS;

    /**
     * 使用中的连接
     */
    private ArrayBlockingQueue<Connection> ACTIVES;

    /**
     * 连接的空闲时间记录
     */
    private Map<Connection, Long> ACTIVE_MAP;

    /**
     * 配置
     */
    private PoolConfig config;

    private static int init;

    private static ConnectPools connectPools;

    private ConnectPools()
    {
    }

    public static ConnectPools getInstance()
    {
        synchronized (ConnectPools.class)
        {
            if (connectPools == null)
            {
                System.out.println("线程池未初始化");
                connectPools = new ConnectPools();
                return null;
            }
        }
        return connectPools;
    }


    /**
     * 初始化连接池
     */
    @Override
    public void init(final PoolConfig config)
    {
        synchronized (ConnectPools.class)
        {
            if (init == -1)
            {
                return;
            }
            this.config = config;
            int maxActive = config.getMaxActive();
            int initialSize = config.getInitialSize();

            if (maxActive < initialSize)
            {
                throw new RuntimeException("initialSize is gt maxActive");
            }

            POOLS = new ArrayBlockingQueue<>(maxActive);
            ACTIVES = new ArrayBlockingQueue<>(maxActive);
            ACTIVE_MAP = new ConcurrentHashMap<>(maxActive);
            for (int i = 0; i < initialSize; i++)
            {
                POOLS.offer(createConnection());
            }
            System.out.println("初始化连接:" + initialSize + " /个");
            System.out.println("最大连接:" + maxActive + " /个");
            System.out.println("等待超时:" + config.getMaxWait() + " ms");
            init = -1;
            monitorConnectActive();
        }
    }

    /**
     * 从连接池中获取一个连接
     *
     * @return
     */
    @Override
    public JdbcConnect getConnection()
    {
        synchronized (this)
        {
            int activeSize = ACTIVES.size();
            long waiteTime = config.getMaxWait();
            long currentTimeMillis = System.currentTimeMillis();

            // 连接池已满,进行等待
            while (activeSize >= config.getMaxActive())
            {
                if (waiteTime == 0)
                {
                }
                else if ((System.currentTimeMillis() - currentTimeMillis) >= waiteTime)
                {
                    System.out.println(Thread.currentThread().getName() + " - 获取连接超时");
                    return null;
                }
            }

            // 连接数量未达到最大值,创建新的连接
            Connection connection = null;
            if (POOLS.isEmpty())
            {
                connection = createConnection();
            }
            else
            {
                // 从连接池中获取
                connection = POOLS.poll();
            }
            ACTIVE_MAP.put(connection, System.currentTimeMillis());
            ACTIVES.offer(connection);
            return new JdbcConnect(connection);
        }
    }

    /**
     * 连接回收
     *
     * @param connection 连接对象
     */
    @Override
    public void recovery(Connection connection)
    {
        synchronized (this)
        {
            try
            {
                if (connection == null || connection.isClosed() || !connection.isValid(2))
                {
                    connection = createConnection();
                }
                ACTIVES.remove(connection);
                POOLS.offer(connection);
            } catch (SQLException e)
            {
                e.printStackTrace();
            }
            System.out.println();
            System.out.println("close");
            System.out.println("POOLS size():" + POOLS.size());
            System.out.println("ACTIVES size():" + ACTIVES.size());
            System.out.println();
        }
    }

    /**
     * 创建数据库连接
     *
     * @return Connection
     */
    private Connection createConnection()
    {
        if (ACTIVES.size() >= config.getMaxActive())
        {
            throw new RuntimeException("不能创建连接,连接数量已满");
        }
        Connection connection = null;
        try
        {
            Class.forName(config.getDriverClassName());
            connection = DriverManager.getConnection(config.getJdbcUrl(), config.getUsername(), config.getPassword());
            System.out.println("-----------  @_@! 新建数据库连接成功  -----------");
        } catch (Exception e)
        {
            e.printStackTrace();
            System.out.println("@_@!  数据库连接失败");
        }

        return connection;
    }


    /**
     * 监控连接池中的连接
     * 判断是否有连接空闲时间超过了 配置的空闲时间
     * 超过的将其释放断开连接
     */
    private void monitorConnectActive()
    {
        Set<Map.Entry<Connection, Long>> entries = ACTIVE_MAP.entrySet();
        new Thread(() -> {
            while (true)
            {
                try
                {
                    // 多久检测一次
                    Thread.sleep(config.getTimeBetweenEvictionRunsMillis());
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                if (POOLS.size() > config.getInitialSize())
                {
                    entries.forEach(map -> {
                        long value = map.getValue();
                        Connection connection = map.getKey();
                        long minEvictableIdleTimeMillis = config.getMinEvictableIdleTimeMillis();
                        if (System.currentTimeMillis() - value >= minEvictableIdleTimeMillis)
                        {
                            System.out.println("该连接" + connection + "在 " + minEvictableIdleTimeMillis + "ms 内无连接,将该连接关闭,释放资源");
                            ACTIVE_MAP.remove(connection);
                            if (POOLS.size() > config.getInitialSize())
                            {
                                POOLS.remove(connection);
                                ACTIVES.remove(connection);
                            }
                            System.out.println("POOLS size():" + POOLS.size());
                        }
                    });
                }
            }
        }).start();
    }
}

3、JdbcConnect 包装一个Connect对象

/**
 * <br>
 *
 * @author 永健
 * @since 2020-05-20 11:42
 */
public class JdbcConnect
{
    private Connection connection;
    private LinkedList<Statement> STA = new LinkedList<>();
    private LinkedList<ResultSet> RE = new LinkedList<>();
    private boolean isUse;

    public JdbcConnect(Connection connection)
    {
        this.isUse = true;
        this.connection = connection;
    }

    /**
     * 执行回收后 不允许操作
     */
    public void isUse()
    {
        if (!isUse)
        {
            throw new RuntimeException("this connect is close");
        }
    }

    public Connection getConnection()
    {
        return connection;
    }

    /**
     * 改操作
     *
     * @param sql 语句
     * @return 影响行数
     * @throws SQLException 异常
     */
    public int executeUpdate(String sql) throws SQLException
    {
        isUse();
        int resultSet;
        PreparedStatement preparedStatement = null;
        try
        {
            System.out.println();
            System.out.println("#### update sql--> ," + sql);
            connection.setAutoCommit(false);
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeUpdate();
        } catch (SQLException e)
        {
            throw new SQLException();
        } finally
        {
            preparedStatement.close();
        }
        System.out.println();
        return resultSet;
    }

    /**
     * 执行查的sql
     *
     * @param sql sql语句
     * @return ResultSet
     */
    public ResultSet executeQuery(String sql)
    {
        isUse();
        ResultSet resultSet = null;
        Statement statement = null;
        try
        {
            System.out.println();
            connection.setAutoCommit(true);
            //System.out.println("#### select sql--> " + sql);
            statement = connection.createStatement();
            resultSet = statement.executeQuery(sql);
            STA.add(statement);
            RE.addFirst(resultSet);
            //System.out.println();
        } catch (SQLException e)
        {
            e.printStackTrace();
        }
        return resultSet;
    }


    /**
     * 关闭连接
     */
    public void close()
    {
        this.isUse = false;

        Stream.iterate(0, i -> i + 1).limit(RE.size()).forEach(i -> {
            ResultSet resultSet = RE.get(i);
            Statement statement = STA.get(i);
            try
            {
                if (resultSet != null)
                {
                    resultSet.close();
                }
                if (statement != null)
                {
                    statement.close();
                }
            } catch (SQLException e)
            {
                e.printStackTrace();
            }
        });
        RE.clear();
        STA.clear();
        ConnectPools.getInstance().recovery(connection);
    }

    /**
     * 事物提交
     */
    public void commit()
    {
        isUse();
        try
        {
            connection.commit();
        } catch (SQLException throwable)
        {
            throwable.printStackTrace();
        }
    }

    /**
     * 食物回滚
     */
    public void rollback()
    {
        isUse();
        try
        {
            connection.rollback();
        } catch (SQLException throwable)
        {
            throwable.printStackTrace();
        }
    }
}

4、PoolConfig 连接池配置对象

/**
 * <br>
 * 连接池配置文件
 *
 * @author 永健
 * @since 2020-08-30 15:52
 */
public class PoolConfig
{
    /**
     * 默认最小初始化连接
     */
    private final static int INIT_SIZE = 1;

    /**
     * 默认最大连接
     */
    private final static int MAX_ACTIVE = 5;

    /**
     * 最小空闲时间
     */
    private final static int MIN_FREE_TIME = 10000;

    /**
     * 多久检测一次空闲的连接
     */
    private final static int CHECK_TIME = 3000;
    /**
     * 0 的时候无时间限制
     */
    private final static int MAX_WAITE = 10000;


    private final static String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";

    private String jdbcUrl;
    private String username;
    private String password;
    private String driverClassName = DRIVER_CLASS_NAME;
    private long maxWait = MAX_WAITE;
    private int initialSize = INIT_SIZE;
    private int maxActive = MAX_ACTIVE;

    private long minEvictableIdleTimeMillis = MIN_FREE_TIME;

    private long timeBetweenEvictionRunsMillis = CHECK_TIME;

        // ....... 省掉get/set
}

5、PoolTest 测试

/**
 * <br>
 *
 * @author 永健
 * @since 2020-08-30 16:25
 */
public class PoolsTest
{
    private static Set<String> CONNECTS = new HashSet<>();

    private static CountDownLatch latch = new CountDownLatch(50);

    @Test
    public void test() throws IOException
    {
        ConnectPools connectPools = ConnectPools.getInstance();

        PoolConfig poolConfig = new PoolConfig()
                .setJdbcUrl("jdbc:mysql://127.0.0.1:3306/kfc?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true")
                .setUsername("root")
                .setPassword("tiger")
                .setInitialSize(3)
                .setMaxWait(100)
                .setMaxActive(10);

        connectPools.init(poolConfig);

        ExecutorService executorService = Executors.newFixedThreadPool(50);
        for (int i = 0; i < 50; i++)
        {
            executorService.submit(() -> {
                select(connectPools);
                latch.countDown();
            });
        }

        try
        {
            latch.await();
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        System.out.println("CONNECTS:" + CONNECTS.size());
        new BufferedReader(new InputStreamReader(System.in)).readLine();
    }

    private void select(ConnectPools connectPools)
    {
        JdbcConnect connection = connectPools.getConnection();
        if (connection == null)
        {
            return;
        }
        CONNECTS.add(connection.getConnection() + "");
        ResultSet resultSet = null;
        try
        {
            resultSet = connection.executeQuery("select * from tb_user limit 1");
            handler(resultSet);
            while (resultSet.next())
            {
                User user = new User();
                for (String coulmName : FIELD_MAP.keySet())
                {
                    Object val = resultSet.getObject(coulmName);
                    Field field = FIELD_MAP.get(coulmName);
                    if (field != null)
                    {
                        field.setAccessible(true);
                        try
                        {
                            field.set(user, val);
                        } catch (IllegalAccessException e)
                        {
                            e.printStackTrace();
                        }
                    }
                }
                // System.out.println(user);
            }
            System.out.println(Thread.currentThread().getName() + "-" + connection.getConnection());
        } catch (SQLException e)
        {
            e.printStackTrace();
        } finally
        {
            connection.close();
        }
    }

    private static final List<String> COLUMN_NABS = new ArrayList<>();
    private static final HashMap<String, Field> FIELD_MAP = new HashMap<>();

    private void handler(ResultSet resultSet)
    {
        if (!FIELD_MAP.isEmpty())
        {
            return;
        }
        Class<User> personnelClass = User.class;
        Field[] declaredFields = personnelClass.getDeclaredFields();
        try
        {
            int columnCount = resultSet.getMetaData().getColumnCount();
            for (int i = 0; i < columnCount; i++)
            {
                String columnName = resultSet.getMetaData().getColumnName(i + 1);
                COLUMN_NABS.add(columnName);
                for (Field field : declaredFields)
                {
                    TableField annotation = field.getAnnotation(TableField.class);
                    if (annotation != null && !annotation.exist())
                    {
                        continue;
                    }
                    if (columnName.equals(field.getName()))
                    {
                        FIELD_MAP.put(field.getName(), field);
                    }
                }
            }
        } catch (SQLException throwables)
        {
            throwables.printStackTrace();
        } finally
        {
        }

    }
}

class User
{
    private int id;
    private String name;
    private String phone;
    private String sex;

    public int getId()
    {
        return id;
    }

    public User setId(int id)
    {
        this.id = id;
        return this;
    }

    public String getName()
    {
        return name;
    }

    public User setName(String name)
    {
        this.name = name;
        return this;
    }

    public String getPhone()
    {
        return phone;
    }

    public User setPhone(String phone)
    {
        this.phone = phone;
        return this;
    }

    public String getSex()
    {
        return sex;
    }

    public User setSex(String sex)
    {
        this.sex = sex;
        return this;
    }

    @Override
    public String toString()
    {
        return "User{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", phone='" + phone + ''' +
                ", sex='" + sex + ''' +
                '}';
    }
}
  • test()方法

模拟了50个并发请求,最终打印结果,最终只创建了10个连接对象。应为配置的时候只配置了最大连接数为10。所以不回出现,超过10个的连接数。其余线程超时未获取到连接。

连接的时候先拿到的是,初始化的三个连接。然后和不够连接继续创建连接直到10个连接。然后有一部分在等待的时间内没有获取到连接出现获取超时,接着有一部连接释放回收后,又有一部分的线程拿到了连接,仔细看这些连接是重复的。这说明这些连接是复用的。线程 48 那部分和 线程2 那部分比较,是复用的连接,虽然有50个请求但是只有20个拿到连接,其余部分无法获取得到。

其次:在所有线程跑完之后。所有线程都被回收到线程池中。

我们看 ConnectPools 中的连接监控的方法monitorConnectActive()

根据配置多久进行一次连接的空闲监控。当该连接空闲时时间超过 配置的指定时间 minEvictableIdleTimeMillis,将其连接断开释放资源。如打印结果所示,最终释放掉7个连接对象,保持着最小的连接数量。

建议大家,在实际的项目中,尽量不要用自己造的轮子去使用,因为你的轮子没有经过各种的风吹雨打,各种环境的压测等等测试。而且还有好多优秀的框架使用。造轮子,只是来辅助我们学习的一种方式,理论结合实践才能理解的够深刻。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值