Java:与HikariDataSource对象

时间:2016-01-26 00:38:51

标签: java concurrency thread-safety connection-pooling hikaricp

我有一个看起来像这样的课程:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class ConnectionPool {
    private HikariDataSource hds;
    private final String propertyFileName;

    public ConnectionPool(String propertyFileName) {
        if (propertyFileName == null) {
            throw new IllegalArgumentException("propertyFileName can't be null");
        }

        this.propertyFileName = propertyFileName;
        reloadFile();
    }

    public void reloadFile() {
        if (hds != null) {
            hds.close();
        }

        hds = new HikariDataSource(new HikariConfig(propertyFileName));
    }

    public HikariDataSource getHikariDataSource() {
        return hds;
    }

    public String getPropertyFileName() {
        return propertyFileName;
    }

    public void executeQuery(final String sql, final CallBack<ResultSet, SQLException> callBack) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Connection connection = null;
                PreparedStatement preparedStatement = null;
                ResultSet resultSet = null;

                try {
                    connection = hds.getConnection();
                    preparedStatement = connection.prepareStatement(sql);
                    resultSet = preparedStatement.executeQuery();
                    callBack.call(resultSet, null);
                } catch (SQLException e) {
                    callBack.call(null, e);
                } finally {
                    if (resultSet != null) {
                        try {
                            resultSet.close();
                        } catch (SQLException ignored) {}
                    }

                    if (preparedStatement != null) {
                        try {
                            preparedStatement.close();
                        } catch (SQLException ignored) {}
                    }

                    if (connection != null) {
                        try {
                            connection.close();
                        } catch (SQLException ignored) {}
                    }
                }
            }
        }).start();
    }

    public void executeUpdate(final String sql, final CallBack<Integer, SQLException> callBack) {
        //TODO
    }

    public void execute(final String sql, final CallBack<Boolean, SQLException> callBack) {
        //TODO
    }

    public void connection(final String sql, final CallBack<Connection, SQLException> callBack) {
        //TODO
    }
}

问题是reloadFile()方法可以使用hds从不同的线程调用。因此,当我在另一个线程中使用它的连接对象时,hds可能会被关闭。解决这个问题的最佳方法是什么?我应该在创建新的HikariDataSource对象后等待几秒钟,然后关闭旧对象(直到查询完成)?

修改:另一个问题:hds应该volatile,以便所有主题都可以看到hds的更改吗?

3 个答案:

答案 0 :(得分:1)

<form method="post" action="password/email" enctype="multipart/form-code"> {!! csrf_field() !!} <table class="table" style="width:50%; margin:0 auto;"> <tr> <td colspan="2"> <h1 class="well text-center">Reset Password Form</h1> </td> </tr> <tr> <td> Email:<input type="email" name="email" value="{{old('email')}}" /> </td> </tr> <tr> <td> <button type="submit" class="btn btn-default">Send Password</button> </td> </tr> </table> </form> 的源代码中有一个非常快速简短的介绍。在其HikariDataSource中,它正在调用其内部close()的{​​{1}}方法,为此它将尝试正确关闭池化连接。

如果您想避免强制关闭正在进行连接的任何可能性,一种方法是使用HikariPool

shutdown()

这将确保

  1. 多线程可以访问您的数据源(以获取连接等)
  2. 重新加载数据源需要等到使用数据源的线程完成
  3. 没有线程可以在重新加载时使用数据源来获取连接。

答案 1 :(得分:1)

为什么你要让HikariCP重新加载?许多重要的池参数(minimumIdlemaximumPoolSizeconnectionTimeout等)在运行时可通过JMX bean进行控制,而无需重新启动池。

在关闭和重建连接时,重新启动池是将应用程序“挂起”几秒钟的好方法。如果你不能通过JMX界面做你需要的东西,Adrian的建议似乎是一个非常合理的解决方案。

其他解决方案是可行的,但更复杂。

编辑:仅仅是为了我自己的娱乐,这里是更复杂的解决方案......

public class ConnectionPool {
   private AtomicReference<HikariDataSource> hds;

   public ConnectionPool(String propertyFileName) {
      hds = new AtomicReference<>();
      ...
   }

   public void reloadFile() {
      final HikariDataSource ds = hds.getAndSet(new HikariDataSource(new HikariConfig(propertyFileName)));
      if (ds != null) {
         new Thread(new Runnable() {
           public void run() {
              ObjectName poolName = new ObjectName("com.zaxxer.hikari:type=Pool (" + ds.getPoolName() + ")");
              MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
              HikariPoolMXBean poolProxy = JMX.newMXBeanProxy(mBeanServer, poolName, HikariPoolMXBean.class);

              poolProxy.softEvictConnections();
              do {
                 Thread.sleep(500);
              } while (poolProxy.getActiveConnections() > 0);
              ds.close();
           }
         }).start();
       }
   }

   public HikariDataSource getHikariDataSource() {
      return hds.get();
   }

   public void executeQuery(final String sql, final CallBack<ResultSet, SQLException> callBack) {
      new Thread(new Runnable() {
         @Override
         public void run() {
            ...
            try {
               connection = getHikariDataSource().getConnection();
               ...
            }
         }
      }).start();
   }
}

这将换出池(原子)并将启动一个等待所有活动连接返回的线程,然后再关闭孤立池实例。

这假设您让HikariCP生成唯一的池名称,即不在您的属性中设置poolName,并registerMbeans=true

答案 2 :(得分:0)

一些选择:

同步对数据源的所有访问权限,以便只有一个线程可以搞乱它。不可扩展,但可行。

滚动您自己的连接池,例如Apache Commons Pooling,这样无论线程如何,每次访问都会请求数据源,池会根据需要创建一个。可以搞乱数据ACID,只取决于是否需要脏数据,数据刷新,事务性等等。

每个线程也可以使用ThreadLocal拥有自己的数据源,以便每个线程完全相互独立。同样,数据质量可能是一个问题,如果您已经获得了很多资源,那么资源可能会成为一个问题。线程(取决于您的定义)和太多的开放连接导致客户端或服务器上的资源问题。